using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Advanced.Algorithms.DataStructures
{
///
/// A multi-dimensional interval tree implementation.
///
public class IntervalTree : IEnumerable> where T : IComparable
{
private readonly int dimensions;
private readonly OneDimentionalIntervalTree tree;
private HashSet> items = new HashSet>(new IntervalComparer());
public int Count { get; private set; }
public IntervalTree(int dimension)
{
if (dimension <= 0)
{
throw new Exception("Dimension should be greater than 0.");
}
this.dimensions = dimension;
tree = new OneDimentionalIntervalTree(defaultValue);
}
///
/// Add a new interval to this interval tree.
/// Time complexity : O(d(log(n) + m)) where d is dimensions and
/// m is the number of intervals that overlaps with this inserted interval.
///
public void Insert(T[] start, T[] end)
{
validateDimensions(start, end);
if(items.Contains(new Tuple(start, end)))
{
throw new Exception("Inteval exists.");
}
var currentTrees = new List> { tree };
for (var i = 0; i < dimensions; i++)
{
var allOverlaps = new List>();
foreach (var tree in currentTrees)
{
//insert in current dimension
tree.Insert(new OneDimentionalInterval(start[i], end[i], defaultValue));
//get all overlaps
//and insert next dimension value to each overlapping node
var overlaps = tree.GetOverlaps(new OneDimentionalInterval(start[i], end[i], defaultValue));
foreach (var overlap in overlaps)
{
allOverlaps.Add(overlap.NextDimensionIntervals);
}
}
currentTrees = allOverlaps;
}
items.Add(new Tuple (start, end));
Count++;
}
///
/// Delete this interval from this interval tree.
/// Time complexity : O(d(log(n) + m)) where d is dimensions and
/// m is the number of intervals that overlap with this deleted interval.
///
public void Delete(T[] start, T[] end)
{
if (!items.Contains(new Tuple(start, end)))
{
throw new Exception("Inteval does'nt exist.");
}
validateDimensions(start, end);
var allOverlaps = new List>();
var overlaps = tree.GetOverlaps(new OneDimentionalInterval(start[0], end[0], defaultValue));
foreach (var overlap in overlaps)
{
allOverlaps.Add(overlap.NextDimensionIntervals);
}
DeleteOverlaps(allOverlaps, start, end, 1);
tree.Delete(new OneDimentionalInterval(start[0], end[0], defaultValue));
items.Remove(new Tuple(start, end));
Count--;
}
///
/// Recursively delete values from overlaps in next dimension.
///
private void DeleteOverlaps(List> currentTrees, T[] start, T[] end, int index)
{
//base case
if (index == start.Length)
return;
var allOverlaps = new List>();
foreach (var tree in currentTrees)
{
var overlaps = tree.GetOverlaps(new OneDimentionalInterval(start[index], end[index], defaultValue));
foreach (var overlap in overlaps)
{
allOverlaps.Add(overlap.NextDimensionIntervals);
}
}
//dig in to next dimension to
DeleteOverlaps(allOverlaps, start, end, ++index);
index--;
//now delete
foreach (var tree in allOverlaps)
{
if (tree.Count > 0)
{
tree.Delete(new OneDimentionalInterval(start[index], end[index], defaultValue));
}
}
}
///
/// Does this interval overlap with any interval in this interval tree?
///
public bool DoOverlap(T[] start, T[] end)
{
validateDimensions(start, end);
var allOverlaps = getOverlaps(tree, start, end, 0);
return allOverlaps.Count > 0;
}
///
/// returns a list of matching intervals.
/// Time complexity : O(d(log(n) + m)) where d is dimensions and
/// m is the number of overlaps.
///
public List> GetOverlaps(T[] start, T[] end)
{
return getOverlaps(tree, start, end, 0);
}
///
/// Does this interval overlap with any interval in this interval tree?
///
private List> getOverlaps(OneDimentionalIntervalTree currentTree,
T[] start, T[] end, int dimension)
{
var nodes = currentTree.GetOverlaps(new OneDimentionalInterval(start[dimension], end[dimension], defaultValue));
if (dimension + 1 == dimensions)
{
var result = new List>();
foreach (var node in nodes)
{
var fStart = new T[dimensions];
var fEnd = new T[dimensions];
fStart[dimension] = node.Start;
fEnd[dimension] = node.End[node.MatchingEndIndex];
var thisDimResult = new Tuple(fStart, fEnd);
result.Add(thisDimResult);
}
return result;
}
else
{
var result = new List>();
foreach (var node in nodes)
{
var nextDimResult = getOverlaps(node.NextDimensionIntervals, start, end, dimension + 1);
foreach (var nextResult in nextDimResult)
{
nextResult.Item1[dimension] = node.Start;
nextResult.Item2[dimension] = node.End[node.MatchingEndIndex];
result.Add(nextResult);
}
}
return result;
}
}
///
/// A cached function to override default(T)
/// so that for value types we can return min value as default.
///
private readonly Lazy defaultValue = new Lazy(() =>
{
var s = typeof(T);
bool isValueType;
#if NET40
isValueType = s.IsValueType;
#else
isValueType = s.GetTypeInfo().IsValueType;
#endif
if (isValueType)
{
return (T)Convert.ChangeType(int.MinValue, s);
}
return default(T);
});
///
/// validate dimensions for point length.
///
private void validateDimensions(T[] start, T[] end)
{
if (start == null)
{
throw new ArgumentNullException(nameof(start));
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
if (start.Length != dimensions || start.Length != end.Length)
{
throw new Exception($"Expecting {dimensions} points in start and end values for this interval.");
}
if (start.Where((t, i) => t.Equals(defaultValue.Value)
|| end[i].Equals(defaultValue.Value)).Any())
{
throw new Exception("Points cannot contain Minimum Value or Null values");
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator> GetEnumerator()
{
return items.GetEnumerator();
}
}
///
/// An interval tree implementation in one dimension using augmentation tree method.
///
internal class OneDimentionalIntervalTree where T : IComparable
{
//use a height balanced binary search tree
private readonly RedBlackTree> redBlackTree
= new RedBlackTree>();
internal int Count { get; private set; }
///
/// A cached function to override default(T)
/// so that for value types we can return min value as default.
///
private readonly Lazy defaultValue;
internal OneDimentionalIntervalTree(Lazy defaultValue)
{
this.defaultValue = defaultValue;
}
///
/// Insert a new Interval.
///
internal void Insert(OneDimentionalInterval newInterval)
{
sortInterval(newInterval);
var existing = redBlackTree.FindNode(newInterval);
if (existing != null)
{
existing.Value.End.Add(newInterval.End[0]);
}
else
{
existing = redBlackTree.InsertAndReturnNode(newInterval).Item1;
}
updateMax(existing);
Count++;
}
///
/// Delete this interval
///
internal void Delete(OneDimentionalInterval interval)
{
sortInterval(interval);
var existing = redBlackTree.FindNode(interval);
if (existing != null && existing.Value.End.Count > 1)
{
existing.Value.End.RemoveAt(existing.Value.End.Count - 1);
updateMax(existing);
}
else if (existing != null)
{
redBlackTree.Delete(interval);
updateMax(existing.Parent);
}
else
{
throw new Exception("Interval not found in this interval tree.");
}
Count--;
}
///
/// Returns an interval in this tree that overlaps with this search interval
///
internal OneDimentionalInterval GetOverlap(OneDimentionalInterval searchInterval)
{
sortInterval(searchInterval);
return getOverlap(redBlackTree.Root, searchInterval);
}
///
/// Returns an interval in this tree that overlaps with this search interval.
///
internal List> GetOverlaps(OneDimentionalInterval searchInterval)
{
sortInterval(searchInterval);
return getOverlaps(redBlackTree.Root, searchInterval);
}
///
/// Does any interval overlaps with this search interval.
///
internal bool DoOverlap(OneDimentionalInterval searchInterval)
{
sortInterval(searchInterval);
return getOverlap(redBlackTree.Root, searchInterval) != null;
}
///
/// Swap intervals so that start always appear before end.
///
private void sortInterval(OneDimentionalInterval value)
{
if (value.Start.CompareTo(value.End[0]) <= 0)
{
return;
}
var tmp = value.End[0];
value.End[0] = value.Start;
value.Start = tmp;
}
///
/// Returns an interval that overlaps with this interval
///
private OneDimentionalInterval getOverlap(RedBlackTreeNode> current, OneDimentionalInterval searchInterval)
{
while (true)
{
if (current == null)
{
return null;
}
if (doOverlap(current.Value, searchInterval))
{
return current.Value;
}
//if left max is greater than search start
//then the search interval can occur in left sub tree
if (current.Left != null && current.Left.Value.MaxEnd.CompareTo(searchInterval.Start) >= 0)
{
current = current.Left;
continue;
}
//otherwise look in right subtree
current = current.Right;
}
}
///
/// Returns all intervals that overlaps with this interval.
///
private List> getOverlaps(RedBlackTreeNode> current,
OneDimentionalInterval searchInterval, List> result = null)
{
if (result == null)
{
result = new List>();
}
if (current == null)
{
return result;
}
if (doOverlap(current.Value, searchInterval))
{
result.Add(current.Value);
}
//if left max is greater than search start
//then the search interval can occur in left sub tree
if (current.Left != null
&& current.Left.Value.MaxEnd.CompareTo(searchInterval.Start) >= 0)
{
getOverlaps(current.Left, searchInterval, result);
}
//otherwise look in right subtree
getOverlaps(current.Right, searchInterval, result);
return result;
}
///
/// Does this interval a overlap with b.
///
private bool doOverlap(OneDimentionalInterval a, OneDimentionalInterval b)
{
//lazy reset
a.MatchingEndIndex = -1;
b.MatchingEndIndex = -1;
for (var i = 0; i < a.End.Count; i++)
{
for (var j = 0; j < b.End.Count; j++)
{
//a.Start less than b.End and a.End greater than b.Start
if (a.Start.CompareTo(b.End[j]) > 0 || a.End[i].CompareTo(b.Start) < 0)
{
continue;
}
a.MatchingEndIndex = i;
b.MatchingEndIndex = j;
return true;
}
}
return false;
}
///
/// update max end value under each node in red-black tree recursively.
///
private void updateMax(RedBlackTreeNode> node, T currentMax, bool recurseUp = true)
{
while (true)
{
if (node == null)
{
return;
}
if (node.Left != null && node.Right != null)
{
//if current Max is less than current End
//then update current Max
if (currentMax.CompareTo(node.Left.Value.MaxEnd) < 0)
{
currentMax = node.Left.Value.MaxEnd;
}
if (currentMax.CompareTo(node.Right.Value.MaxEnd) < 0)
{
currentMax = node.Right.Value.MaxEnd;
}
}
else if (node.Left != null)
{
//if current Max is less than current End
//then update current Max
if (currentMax.CompareTo(node.Left.Value.MaxEnd) < 0)
{
currentMax = node.Left.Value.MaxEnd;
}
}
else if (node.Right != null)
{
if (currentMax.CompareTo(node.Right.Value.MaxEnd) < 0)
{
currentMax = node.Right.Value.MaxEnd;
}
}
foreach (var v in node.Value.End)
{
if (currentMax.CompareTo(v) < 0)
{
currentMax = v;
}
}
node.Value.MaxEnd = currentMax;
if (recurseUp)
{
node = node.Parent;
continue;
}
break;
}
}
///
/// Update Max recursively up each node in red-black tree.
///
private void updateMax(RedBlackTreeNode> newRoot, bool recurseUp = true)
{
if (newRoot == null)
return;
newRoot.Value.MaxEnd = defaultValue.Value;
if (newRoot.Left != null)
{
newRoot.Left.Value.MaxEnd = defaultValue.Value;
updateMax(newRoot.Left, newRoot.Left.Value.MaxEnd, recurseUp);
}
if (newRoot.Right != null)
{
newRoot.Right.Value.MaxEnd = defaultValue.Value;
updateMax(newRoot.Right, newRoot.Right.Value.MaxEnd, recurseUp);
}
updateMax(newRoot, newRoot.Value.MaxEnd, recurseUp);
}
}
///
/// One dimensional interval.
///
internal class OneDimentionalInterval : IComparable where T : IComparable
{
///
/// Start of this interval range.
///
public T Start { get; set; }
///
/// End of this interval range.
///
public List End { get; set; }
///
/// Max End interval under this interval.
///
internal T MaxEnd { get; set; }
///
/// Holds intervals for the next dimension.
///
internal OneDimentionalIntervalTree NextDimensionIntervals { get; set; }
///
/// Mark the matching end index during overlap search
/// so that we can return the overlapping interval.
///
internal int MatchingEndIndex { get; set; }
public int CompareTo(object obj)
{
return Start.CompareTo(((OneDimentionalInterval)obj).Start);
}
public OneDimentionalInterval(T start, T end, Lazy defaultValue)
{
Start = start;
End = new List { end };
NextDimensionIntervals = new OneDimentionalIntervalTree(defaultValue);
}
}
///
/// Compares two intervals.
///
internal class IntervalComparer : IEqualityComparer> where T : IComparable
{
public bool Equals(Tuple x, Tuple y)
{
if (x == y)
{
return true;
}
for (int i = 0; i < x.Item1.Length; i++)
{
if (!x.Item1[i].Equals(y.Item1[i]))
{
return false;
}
if (!x.Item2[i].Equals(y.Item2[i]))
{
return false;
}
}
return true;
}
public int GetHashCode(Tuple x)
{
unchecked
{
if (x == null)
{
return 0;
}
int hash = 17;
foreach (var element in x.Item1)
{
hash = hash * 31 + element.GetHashCode();
}
foreach (var element in x.Item2)
{
hash = hash * 31 + element.GetHashCode();
}
return hash;
}
}
}
}