Jul 19, 2019

Intervals 2.0

Updated intervals combining (splicing/clipping) method with the new features of C# 7.0. Intervals passed can overlap.
Two weird classes added for convenience. Pair<T> is handy when you have to switch often between 2 objects. IntBool is a value type which can act as both int and bool.

    public static class CombineIntervals
    {
        public static IEnumerable<(T, T)> Combine<T>(this IEnumerable<(T, T)> inc, IEnumerable<(T, T)> exc = null) where T : IComparable
        {
            if (exc == null) exc = Enumerable.Empty<(T, T)>();
            IEnumerable<(T val, bool br, bool ex) > GetBorders(IEnumerable<(T left, T right)> ranges, bool ex)
            {
                foreach (var (left, right) in ranges)
                {
                    yield return (left, true, ex);
                    yield return (right, false, ex);
                }
            }
            var borders = GetBorders(inc, false).Union(GetBorders(exc, true)).OrderBy(x => x.val).ToArray();
            T start = borders[0].val;
            var state = new Pair<IntBool>();
            foreach (var (val, br, ex) in borders)
            {
                if (state[ex].Xor(br))
                {
                    if (br == state[!ex])
                        yield return (start, val);
                    else start = val;
                }
                state[ex] += br ? 1 : -1;
            }
        }
    }
    public struct Pair<T>
    {
        T a; T b;
        public T this[IntBool i]
        {
            get => i ? b : a;
            set
            {
                if (i) b = value;
                else a = value;
            }
        }
        public override string ToString()
        {
            return $"{a}, {b}";
        }
        internal void Deconstruct(out T x, out T y)
        {
            x = a;
            y = b;
        }
    }
    public struct IntBool
    {
        private int val;
        public static implicit operator IntBool (int x) => new IntBool() { val = x };
        public static implicit operator int(IntBool x) => x.val;
        public static implicit operator IntBool(bool x) => new IntBool() { val = x ? 1 : 0 };
        public static implicit operator bool(IntBool x) => x.val > 0;
        public override string ToString() => val.ToString();
        public bool And(bool x) => val == 1 && x;
        public bool Xor(bool x) => val == 1 && !x || val == 0 && x;
    }