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;
}