/// <summary> /// Tests an interval to see if it wholly contains another interval. /// </summary> /// <typeparam name="T">The type of values included in the intervals.</typeparam> /// <param name="interval">The interval to test.</param> /// <param name="other">The other interval.</param> /// <returns>true, if <paramref name="interval"/> contains <paramref name="other"/>; false, otherwise.</returns> /// <remarks> /// All intervals (including empty ones) contain every other empty interval. So, if <paramref name="other"/> is empty, this method always returns true. /// Otherwise, this method only returns true if the start and end values of <paramref name="interval"/> surround the values of <paramref name="other"/>. /// </remarks> public static bool Contains <T>(this IInterval <T> interval, IInterval <T> other) where T : IComparable <T> { if (other.IsEmpty()) { return(true); } var intersection = other.IntersectWith(interval); return(!intersection.IsEmpty() && intersection == other); }
/// <summary> /// Returns the difference of one interval to another. /// </summary> /// <typeparam name="T">The type of values included in the intervals.</typeparam> /// <param name="interval">The source interval.</param> /// <param name="other">The interval to exclude.</param> /// <returns>A set that contains every part of <paramref name="interval"/> that is not also contained by <paramref name="other"/>.</returns> public static IList <IInterval <T> > DifferenceWith <T>(this IInterval <T> interval, IInterval <T> other) where T : IComparable <T> { if (interval.IsEmpty()) { return(null); } var intersection = interval.IntersectWith(other); if (intersection.IsEmpty()) { return(new[] { interval }); } else if (intersection == interval) { return(null); } var intervals = new List <IInterval <T> >(); var startToStart = interval.Start.CompareTo(intersection.Start); if (startToStart != 0 || (interval.StartInclusive && !intersection.StartInclusive)) { intervals.Add(interval.Clone( interval.Start, interval.StartInclusive, intersection.Start, !intersection.StartInclusive)); } var endToEnd = interval.End.CompareTo(intersection.End); if (endToEnd != 0 || (interval.EndInclusive && !intersection.EndInclusive)) { intervals.Add(interval.Clone( intersection.End, !intersection.EndInclusive, interval.End, interval.EndInclusive)); } return(intervals); }
/// <summary> /// Tests an interval to see if it contains a given value. /// </summary> /// <typeparam name="T">The type of values included in the interval.</typeparam> /// <param name="interval">The interval to test.</param> /// <param name="value">The value to test.</param> /// <returns>true, if the interval is non-empty and contains the value; false, otherwise.</returns> public static bool Contains <T>(this IInterval <T> interval, T value) where T : IComparable <T> { if (interval.IsEmpty()) { return(false); } var start = interval.Start.CompareTo(value); var end = interval.End.CompareTo(value); if ((interval.StartInclusive && start == 0) || (interval.EndInclusive && end == 0)) { return(true); } if (start >= 0 || end <= 0) { return(false); } return(true); }
/// <summary> /// Returns the union of two intervals. /// </summary> /// <typeparam name="T">The type of values included in the intervals.</typeparam> /// <param name="interval">The first interval.</param> /// <param name="other">The second interval.</param> /// <returns>A set that contains both intervals.</returns> /// <remarks> /// If the intervals intersect, the result will be a set with a single interval. /// </remarks> public static IList <IInterval <T> > UnionWith <T>(this IInterval <T> interval, IInterval <T> other) where T : IComparable <T> { var intervalEmpty = interval.IsEmpty(); var otherEmpty = other.IsEmpty(); if (intervalEmpty && otherEmpty) { return(null); } else if (intervalEmpty) { return(new[] { other }); } else if (otherEmpty) { return(new[] { interval }); } int startToStart, endToEnd, startToEnd; T start, end; bool startInclusive, endInclusive; startToStart = interval.Start.CompareTo(other.Start); if (startToStart > 0) { start = interval.Start; startInclusive = interval.StartInclusive; } else if (startToStart < 0) { start = other.Start; startInclusive = other.StartInclusive; } else { start = interval.Start; startInclusive = interval.StartInclusive && other.StartInclusive; } endToEnd = interval.End.CompareTo(other.End); if (endToEnd < 0) { end = interval.End; endInclusive = interval.EndInclusive; } else if (endToEnd > 0) { end = other.End; endInclusive = other.EndInclusive; } else { end = interval.End; endInclusive = interval.EndInclusive && other.EndInclusive; } startToEnd = start.CompareTo(end); if (startToEnd > 0) { return(new[] { interval, other }); } else if (startToEnd == 0 && !(startInclusive || endInclusive)) { return(new[] { interval, other }); } bool startMatchesInterval = false, startMatchesOther = false, endMatchesInterval = false, endMatchesOther = false; if (startToStart < 0) { start = interval.Start; startInclusive = interval.StartInclusive; startMatchesInterval = true; } else if (startToStart > 0) { start = other.Start; startInclusive = other.StartInclusive; startMatchesOther = true; } else { start = interval.Start; startInclusive = interval.StartInclusive || other.StartInclusive; startMatchesInterval = startInclusive == interval.StartInclusive; startMatchesOther = startInclusive == other.StartInclusive; } if (endToEnd > 0) { end = interval.End; endInclusive = interval.EndInclusive; endMatchesInterval = true; } else if (endToEnd < 0) { end = other.End; endInclusive = other.EndInclusive; endMatchesOther = true; } else { end = interval.End; endInclusive = interval.EndInclusive || other.EndInclusive; endMatchesInterval = endInclusive == interval.EndInclusive; endMatchesOther = endInclusive == other.EndInclusive; } if (startMatchesInterval && endMatchesInterval) { return(new[] { interval }); } else if (startMatchesOther && endMatchesOther) { return(new[] { other }); } return(new[] { interval.Clone( start, startInclusive, end, endInclusive) }); }
/// <summary> /// Returns the intersection of two intervals. /// </summary> /// <typeparam name="T">The type of values included in the intervals.</typeparam> /// <param name="interval">The first interval.</param> /// <param name="other">The second interval.</param> /// <returns>An interval that represents the intersection of the intervals.</returns> public static IInterval <T> IntersectWith <T>(this IInterval <T> interval, IInterval <T> other) where T : IComparable <T> { if (interval.IsEmpty() || other.IsEmpty()) { return(null); } T start, end; bool startInclusive, endInclusive; bool startMatchesInterval = false, startMatchesOther = false, endMatchesInterval = false, endMatchesOther = false; var startToStart = interval.Start.CompareTo(other.Start); if (startToStart > 0) { start = interval.Start; startInclusive = interval.StartInclusive; startMatchesInterval = true; } else if (startToStart < 0) { start = other.Start; startInclusive = other.StartInclusive; startMatchesOther = true; } else { start = interval.Start; startInclusive = interval.StartInclusive && other.StartInclusive; startMatchesInterval = startInclusive == interval.StartInclusive; startMatchesOther = startInclusive == other.StartInclusive; } var endToEnd = interval.End.CompareTo(other.End); if (endToEnd < 0) { end = interval.End; endInclusive = interval.EndInclusive; endMatchesInterval = true; } else if (endToEnd > 0) { end = other.End; endInclusive = other.EndInclusive; endMatchesOther = true; } else { end = interval.End; endInclusive = interval.EndInclusive && other.EndInclusive; endMatchesInterval = endInclusive == interval.EndInclusive; endMatchesOther = endInclusive == other.EndInclusive; } var startToEnd = start.CompareTo(end); if (startToEnd > 0) { return(null); } else if (startToEnd == 0 && (!startInclusive || !endInclusive)) { return(null); } if (startMatchesInterval && endMatchesInterval) { return(interval); } else if (startMatchesOther && endMatchesOther) { return(other); } return(interval.Clone( start, startInclusive, end, endInclusive)); }