Data Types C#

//http://isotopescreencapture.codeplex.com/
//The MIT License (MIT)
using System.Collections.Generic;
using System.Linq;
namespace Isotope.Collections.Ranges
{
    public static partial class EnumerableUtil
    {
        public static IEnumerable Single(T item)
        {
            yield return item;
        }
        /// 
        /// Given a range (start,end) and a number of steps, will yield that a number for each step
        /// 

        /// 
        /// 
        /// 
        /// 
        public static IEnumerable RangeSteps(double start, double end, int steps)
        {
            // for non-positive number of steps, yield no points
            if (steps < 1)
            {
                yield break;
            }
            // for exactly 1 step, yield the start value
            if (steps == 1)
            {
                yield return start;
                yield break;
            }
            // for exactly 2 stesp, yield the start value, and then the end value
            if (steps == 2)
            {
                yield return start;
                yield return end;
                yield break;
            }
            // for 3 steps or above, start yielding the segments
            // notice that the start and end values are explicitly returned so that there
            // is no possibility of rounding error affecting their values
            int segments = steps - 1;
            double total_length = end - start;
            double stepsize = total_length / segments;
            yield return start;
            for (int i = 1; i < (steps - 1); i++)
            {
                double p = start + (stepsize * i);
                yield return p;
            }
            yield return end;
        }
        public static IEnumerable GroupByCount(
            IEnumerable items,
            int group_size,
            System.Func func_new_col,
            System.Action func_add) where TGROUP : class
        {
            if (items == null)
            {
                throw new System.ArgumentNullException("items");
            }
            if (group_size < 1)
            {
                throw new System.ArgumentOutOfRangeException("group_size");
            }
            if (func_new_col == null)
            {
                throw new System.ArgumentNullException("func_new_col");
            }
            if (func_add == null)
            {
                throw new System.ArgumentNullException("func_add");
            }
            int cur_group_size = 0;
            TGROUP cur_group = null;
            foreach (var item in items)
            {
                if (cur_group_size == 0)
                {
                    if (cur_group == null)
                    {
                        cur_group = func_new_col(group_size);
                    }
                    else
                    {
                        throw new System.InvalidOperationException();
                    }
                }
                func_add(cur_group, cur_group_size, item);
                cur_group_size++;
                if (cur_group_size == group_size)
                {
                    yield return cur_group;
                    cur_group = null;
                    cur_group_size = 0;
                }
            }
            if (cur_group_size > 0)
            {
                if (cur_group == null)
                {
                    throw new System.InvalidOperationException();
                }
                yield return cur_group;
            }
        }
        public static IEnumerable GroupByCount(ICollection items, ICollection group_sizes, System.Func func_new_col, System.Action func_add)
        {
            if (items == null)
            {
                throw new System.ArgumentNullException("items");
            }
            if (group_sizes == null)
            {
                throw new System.ArgumentNullException("group_sizes");
            }
            if (func_new_col == null)
            {
                throw new System.ArgumentNullException("func_new_col");
            }
            if (func_add == null)
            {
                throw new System.ArgumentNullException("func_add");
            }
            int total_group_sizes = group_sizes.Sum();
            if (total_group_sizes != items.Count)
            {
                throw new System.ArgumentException("group_sizes must account for all items");
            }
            int items_added = 0;
            for (int group_index = 0; group_index < group_sizes.Count; group_index++)
            {
                int cur_group_size = group_sizes.ElementAt(group_index);
                if (cur_group_size < 0)
                {
                    throw new System.ArgumentException("group_sizes contains a negative numver");
                }
                var cur_group = func_new_col(cur_group_size);
                for (int row_index = 0; row_index < cur_group_size; row_index++)
                {
                    var cur_item = items.ElementAt(items_added);
                    func_add(cur_group, row_index, cur_item);
                    items_added++;
                }
                yield return cur_group;
            }
        }
        public static IDictionary> Bucketize(IEnumerable items, System.Func func_get_key, IEqualityComparer ieq)
        {
            if (items == null)
            {
                throw new System.ArgumentNullException("items");
            }
            if (func_get_key == null)
            {
                throw new System.ArgumentNullException("func_get_key");
            }
            var dic = new Dictionary>(ieq);
            foreach (var item in items)
            {
                var key = func_get_key(item);
                List list = null;
                bool found = dic.TryGetValue(key, out list);
                if (!found)
                {
                    list = new List();
                    dic[key] = list;
                }
                list.Add(item);
            }
            return dic;
        }
        public static IDictionary> Bucketize(IEnumerable items, System.Func func_get_key)
        {
            IEqualityComparer ieq = null;
            return Bucketize(items, func_get_key, ieq);
        }
        public static IDictionary Histogram(IEnumerable items, System.Func func_get_key, IEqualityComparer ieq)
        {
            if (items == null)
            {
                throw new System.ArgumentNullException("items");
            }
            if (func_get_key == null)
            {
                throw new System.ArgumentNullException("func_get_key");
            }
            var dic = new Dictionary(ieq);
            foreach (var item in items)
            {
                var key = func_get_key(item);
                int old_value = 0;
                bool found = dic.TryGetValue(key, out old_value);
                if (!found)
                {
                    dic[key] = 1;
                }
                else
                {
                    dic[key] = old_value + 1;
                }
            }
            return dic;
        }
        public static IDictionary Histogram(IEnumerable items)
        {
            var dic = Histogram(items, i => i, null);
            return dic;
        }
        public static List> Chunkify(IEnumerable items, int chunksize)
        {
            var chunks = new List>();
            List cur_chunk = null;
            Chunkify(items, chunksize,
                () =>
                {
                    cur_chunk = new List(chunksize); chunks.Add(cur_chunk);
                },
                item =>
                {
                    cur_chunk.Add(item);
                });
            return chunks;
        }
        public static void Chunkify(IEnumerable items, int chunksize, System.Action create_chunk, System.Action add_item)
        {
            if (items == null)
            {
                throw new System.ArgumentNullException("items");
            }
            if (chunksize < 1)
            {
                throw new System.ArgumentOutOfRangeException("chunksize");
            }
            int item_count = 0;
            int curchunk_size = 0;
            foreach (T item in items)
            {
                if ((item_count % chunksize) == 0)
                {
                    create_chunk();
                    curchunk_size = 0;
                }
                add_item(item);
                item_count++;
                curchunk_size++;
            }
        }
    }
    public struct IntRange
    {
        public readonly int _Lower;
        public readonly int _Length;
        public int Lower
        {
            get { return this._Lower; }
        }
        public int Upper
        {
            get { return this.Lower + this.Length - 1; }
        }
        public int Length
        {
            get { return this._Length;  }
        }
            
        private IntRange(int lower, int length)
        {
            this._Lower = lower;
            this._Length = length;
            if (this.Lower > this.Upper)
            {
                throw new System.OverflowException();
            }
        }
        /// 
        /// Creates a range from the lower and upper values
        /// 

        /// 
        /// (0,0) -> [0]
        /// (0,1) -> [0,1]
        /// (0,5) -> [0,1,2,3,4,5]
        /// (2,5) -> [2,3,4,5]
        /// 

        /// 
        /// 
        /// 
        public static IntRange FromEndpoints(int lower, int upper)
        {
            return new IntRange(lower, upper - lower + 1);
        }
        /// 
        /// Creates a range of a single number
        ///  

        /// 
        /// 

        /// 
        public static IntRange FromInteger(int value)
        {
            return new IntRange(value, 1);
        }
            
        /// 
        /// Creates a new range from a starting value and including all the numbers following that
        ///  

        /// 
        /// (2,0) -> []
        /// (2,1) -> [2]
        /// (2,2) -> [2,3]
        /// (2,3) -> [2,3,4]
        /// (2,4) -> [2,3,4,5]
        /// 

        /// 
        /// 
        /// 
        public static IntRange FromLower(int lower, int length)
        {
            if (length < 0)
            {
                throw new System.ArgumentOutOfRangeException("length", "must be >= 0");
            }
            return new IntRange(lower, length);
        }
        public override string ToString()
        {
            var invariant_culture = System.Globalization.CultureInfo.InvariantCulture;
            return string.Format(invariant_culture, "Range({0},{1})", this.Lower, this.Upper);
        }
        private static bool _intersects(IntRange range1, IntRange range2)
        {
            return ((range1.Lower <= range2.Lower) && (range1.Upper >= range2.Lower));
        }
            
        /// 
        /// Tests if this range interests with another
        /// 

        /// the other range
        /// true if they intersect
        public bool Intersects(IntRange range)
        {
            return (_intersects(this, range) || _intersects(range, this));
        }
            
        private static int Delta = 1;
        private static bool _touches(IntRange range1, IntRange range2)
        {
            var val = (range1.Upper + Delta) == range2.Lower;
            return val;
        }
            
        /// 
        /// Tests if this range touches another. For example (1-2) touches (3,5) but (1,2) does not touch (4,5)
        /// 

        /// the other range
        /// true if they touch
        public bool Touches(IntRange range)
        {
            var val = _touches(this, range) || _touches(range, this);
            return val;
        }
            
        /// 
        /// Tests if this range contains a specific integer
        /// 

        /// the integet
        /// true if the number is contained
        public bool Contains(int n)
        {
            return ((this.Lower <= n) && (n <= this.Upper));
        }
            
        /// 
        /// Join this range with another and return a single range that contains them both. The ranges must either touch or interest.
        /// for example (0,2) amd (3,7) will yield (0,7)
        /// 

        /// the other range
        /// the merged range
        public IntRange JoinWith(IntRange range)
        {
            if (this.Intersects(range) || this.Touches(range))
            {
                int new_Upper = System.Math.Max(this.Upper, range.Upper);
                int new_Lower = System.Math.Min(this.Lower, range.Lower);
                return IntRange.FromEndpoints(new_Lower, new_Upper);
            }
            else
            {
                throw new System.ArgumentException();
            }
        }
        /// 
        /// Get each int int he range from the lower value to the upper value
        /// 

        /// each int in the range
        public IEnumerable Values
        {
            get
            {
                for (int i = this.Lower; i <= this.Upper; i++)
                {
                    yield return i;
                }
            }
        }
    }
    public class SparseIntRange
    {
        private List ranges;
        public SparseIntRange()
        {
            this.clear();
        }
        public IEnumerable Ranges
        {
            get
            {
                foreach (var range in this.ranges)
                {
                    yield return range;
                }
            }
        }
        public IEnumerable Values
        {
            get
            {
                foreach (var rng in this.Ranges)
                {
                    foreach (int i in rng.Values)
                    {
                        yield return i;
                    }
                }
            }
        }
        private void clear()
        {
            this.ranges = new List();
        }
        public int RangeCount
        {
            get { return this.ranges.Count; }
        }
        public void Add(int n)
        {
            var rng = IntRange.FromInteger(n);
            this.Add(rng);
        }
        public void AddInclusive(int lower, int upper)
        {
            var rng = IntRange.FromEndpoints(lower, upper);
            this.Add(rng);
        }
        public void Add(IntRange range)
        {
            var left = new List();
            var right = new List();
            foreach (var rng in this.ranges)
            {
                if (range.Intersects(rng) || range.Touches(rng))
                {
                    range = range.JoinWith(rng);
                }
                else if (rng.Upper < range.Lower)
                {
                    left.Add(rng);
                }
                else if (range.Upper < rng.Lower)
                {
                    right.Add(rng);
                }
                else
                {
                    throw new System.InvalidOperationException("Internal Error");
                }
            }
            this.ranges = left.Concat(EnumerableUtil.Single(range)).Concat(right).ToList();
        }
        public int Lower
        {
            get
            {
                if (this.ranges.Count < 1)
                {
                    throw new System.InvalidOperationException("There are no ranges");
                }
                return this.ranges[0].Lower;
            }
        }
        public int Upper
        {
            get
            {
                if (this.ranges.Count < 1)
                {
                    throw new System.InvalidOperationException("empty");
                }
                return this.ranges[this.ranges.Count - 1].Upper;
            }
        }
        public int Count
        {
            get
            {
                int length = this.ranges.Aggregate(0, (old, rng) => old + rng.Length);
                return length;
            }
        }
        public void Remove(int value)
        {
            var rng = IntRange.FromInteger(value);
            this.Remove(rng);
        }
        public void RemoveInclusive(int lower, int upper)
        {
            var rng = IntRange.FromEndpoints(lower, upper);
            this.Remove(rng);
        }
        public void Remove(IntRange range)
        {
            // if the range doesn't intersect this collection do nothing
            if (!range.Intersects(IntRange.FromEndpoints(this.Lower, this.Upper)))
            {
                return;
            }
            var middle = new List();
            foreach (var S in this.ranges)
            {
                if (!range.Intersects(S))
                {
                    middle.Add(S);
                    continue;
                }
                if ((range.Lower <= S.Lower) && (range.Upper >= S.Upper))
                {
                    // disregard S completely
                    continue;
                }
                if (range.Lower > S.Lower)
                {
                    if (S.Lower <= (range.Lower - 1))
                    {
                        var X = IntRange.FromEndpoints(S.Lower, range.Lower - 1);
                        middle.Add(X);
                    }
                }
                if (range.Upper <= S.Upper)
                {
                    if ((range.Upper + 1) <= S.Upper)
                    {
                        var X = IntRange.FromEndpoints(range.Upper + 1, S.Upper);
                        middle.Add(X);
                    }
                }
                else
                {
                    throw new System.InvalidOperationException("internal error");
                }
            }
            this.ranges = middle;
        }
        public IntRange? FindRangeContainingNumber(int n)
        {
            foreach (var rng in this.ranges)
            {
                if (rng.Contains(n))
                {
                    return rng;
                }
            }
            return null;
        }
        public bool Contains(int n)
        {
            var rng = this.FindRangeContainingNumber(n);
            return rng != null ? true : false;
        }
        public override string ToString()
        {
            var sb = new System.Text.StringBuilder();
            sb.AppendFormat("{0}(", this.GetType().Name);
            foreach (var rng in this.ranges)
            {
                sb.Append(rng.ToString());
            }
            sb.Append(")");
            return sb.ToString();
        }
    }
}