//******************************************************************************************************
// Range.cs - Gbtc
//
// Copyright © 2015, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 06/30/2015 - Stephen C. Wills
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
namespace GSF
{
///
/// Represents a range of values defined by start and end value.
///
public class Range
{
#region [ Constructors ]
///
/// Creates a new instance of the class using the default comparer.
///
/// The start value of the range.
/// The end value of the range.
public Range(T start, T end)
{
Start = start;
End = end;
}
#endregion
#region [ Properties ]
///
/// Gets the start value of the range.
///
public T Start { get; }
///
/// Gets the end value of the range.
///
public T End { get; }
#endregion
#region [ Methods ]
///
/// Determines whether the range contains the given value.
///
/// The value to be compared with.
/// True if the value exists within the range; false otherwise.
public bool Contains(T value)
{
return Contains(value, Comparer.Default);
}
///
/// Determines whether the range contains the given value.
///
/// The value to be compared with.
/// The comparer used to compare objects of type .
/// True if the value exists within the range; false otherwise.
public bool Contains(T value, IComparer comparer)
{
return Contains(value, comparer.Compare);
}
///
/// Determines whether the range contains the given value.
///
/// The value to be compared with.
/// The comparison used to compare objects of type .
/// True if the value exists within the range; false otherwise.
public bool Contains(T value, Comparison comparison)
{
return comparison(Start, value) <= 0 && comparison(value, End) <= 0;
}
///
/// Determines whether the range contains the given range.
///
/// The range to be compared with.
/// True if the given range exists within this range; false otherwise.
public bool Contains(Range range)
{
return Contains(range, Comparer.Default);
}
///
/// Determines whether the range contains the given range.
///
/// The range to be compared with.
/// The comparer used to compare objects of type .
/// True if the given range exists within this range; false otherwise.
public bool Contains(Range range, IComparer comparer)
{
return Contains(range, comparer.Compare);
}
///
/// Determines whether the range contains the given range.
///
/// The range to be compared with.
/// The comparison used to compare objects of type .
/// True if the given range exists within this range; false otherwise.
public bool Contains(Range range, Comparison comparison)
{
return comparison(Start, range.Start) <= 0 && comparison(range.End, End) <= 0;
}
///
/// Determines whether the range overlaps with the given range.
///
/// The range to be compared with.
/// True if the ranges overlap; false otherwise.
public bool Overlaps(Range range)
{
return Overlaps(range, Comparer.Default);
}
///
/// Determines whether the range overlaps with the given range.
///
/// The range to be compared with.
/// The comparer used to compare objects of type .
/// True if the ranges overlap; false otherwise.
public bool Overlaps(Range range, IComparer comparer)
{
return Overlaps(range, comparer.Compare);
}
///
/// Determines whether the range overlaps with the given range.
///
/// The range to be compared with.
/// The comparison used to compare objects of type .
/// True if the ranges overlap; false otherwise.
public bool Overlaps(Range range, Comparison comparison)
{
return comparison(Start, range.End) <= 0 && comparison(range.Start, End) <= 0;
}
///
/// Merges two ranges into one range that fully encompasses both ranges.
///
/// The range to be merged with this one.
/// The range that fully encompasses the merged ranges.
public Range Merge(Range range)
{
return Merge(range, Comparer.Default);
}
///
/// Merges two ranges into one range that fully encompasses both ranges.
///
/// The range to be merged with this one.
/// The comparer used to compare objects of type .
/// The range that fully encompasses the merged ranges.
public Range Merge(Range range, IComparer comparer)
{
return Merge(range, comparer.Compare);
}
///
/// Merges two ranges into one range that fully encompasses both ranges.
///
/// The range to be merged with this one.
/// The comparison used to compare objects of type .
/// The range that fully encompasses the merged ranges.
public Range Merge(Range range, Comparison comparison)
{
T start = comparison(Start, range.Start) <= 0 ? Start : range.Start;
T end = comparison(End, range.End) >= 0 ? End : range.End;
return new Range(start, end);
}
#endregion
#region [ Static ]
// Static Methods
///
/// Merges all ranges in a collection of ranges.
///
/// The collection of ranges to be merged.
/// A range that is the result of merging all ranges in the collection.
public static Range Merge(IEnumerable> ranges)
{
return Merge(ranges, Comparer.Default);
}
///
/// Merges all ranges in a collection of ranges.
///
/// The collection of ranges to be merged.
/// The comparer used to compare objects of type .
/// A range that is the result of merging all ranges in the collection.
public static Range Merge(IEnumerable> ranges, Comparer comparer)
{
return Merge(ranges, comparer.Compare);
}
///
/// Merges all ranges in a collection of ranges.
///
/// The collection of ranges to be merged.
/// The comparison used to compare objects of type .
/// A range that is the result of merging all ranges in the collection.
public static Range Merge(IEnumerable> ranges, Comparison comparison)
{
return ranges.Aggregate((range1, range2) => range1.Merge(range2, comparison));
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The collection of merged ranges.
public static IEnumerable> MergeConsecutiveOverlapping(IEnumerable> ranges)
{
return MergeConsecutiveOverlapping(ranges, Comparer.Default);
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparer used to compare objects of type .
/// The collection of merged ranges.
public static IEnumerable> MergeConsecutiveOverlapping(IEnumerable> ranges, IComparer comparer)
{
return MergeConsecutiveOverlapping(ranges, comparer.Compare);
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparison used to compare objects of type .
/// The collection of merged ranges.
public static IEnumerable> MergeConsecutiveOverlapping(IEnumerable> ranges, Comparison comparison)
{
Range currentRange = null;
foreach (Range range in ranges)
{
if ((object)currentRange == null)
{
currentRange = range;
}
else if (currentRange.Overlaps(range, comparison))
{
currentRange = currentRange.Merge(range, comparison);
}
else
{
yield return currentRange;
currentRange = range;
}
}
if ((object)currentRange != null)
yield return currentRange;
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The collection of merged ranges.
/// This method does not preserve the order of the source collection.
public static IEnumerable> MergeAllOverlapping(IEnumerable> ranges)
{
return MergeAllOverlapping(ranges, Comparer.Default);
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparison used to compare objects of type .
/// The collection of merged ranges.
/// This method does not preserve the order of the source collection.
public static IEnumerable> MergeAllOverlapping(IEnumerable> ranges, Comparison comparison)
{
return MergeAllOverlapping(ranges, Comparer.Create(comparison));
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparer used to compare objects of type .
/// The collection of merged ranges.
/// This method does not preserve the order of the source collection.
public static IEnumerable> MergeAllOverlapping(IEnumerable> ranges, IComparer comparer)
{
return MergeConsecutiveOverlapping(ranges.OrderBy(range => range.Start, comparer));
}
#endregion
}
///
/// Represents a range of values defined by a start and end value with encapsulated state.
///
///
/// This class provides flexibility in allowing the user to define their own state and how it is merged.
/// Because of potentially undesirable behavior which could result from merging objects inherited from
/// this class, the class has been sealed to prevent inheritance.
///
public sealed class Range
{
#region [ Constructors ]
///
/// Creates a new instance of the class using the default comparer.
///
/// The start value of the range.
/// The end value of the range.
/// The state object encapsulated by the range.
public Range(TRange start, TRange end, TState state)
{
Start = start;
End = end;
State = state;
}
#endregion
#region [ Properties ]
///
/// Gets the start value of the range.
///
public TRange Start { get; }
///
/// Gets the end value of the range.
///
public TRange End { get; }
///
/// Gets the state encapsulated by the range.
///
public TState State { get; }
#endregion
#region [ Methods ]
///
/// Determines whether the range contains the given value.
///
/// The value to be compared with.
/// True if the value exists within the range; false otherwise.
public bool Contains(TRange value)
{
return Contains(value, Comparer.Default);
}
///
/// Determines whether the range contains the given value.
///
/// The value to be compared with.
/// The comparer used to compare objects of type .
/// True if the value exists within the range; false otherwise.
public bool Contains(TRange value, IComparer comparer)
{
return Contains(value, comparer.Compare);
}
///
/// Determines whether the range contains the given value.
///
/// The value to be compared with.
/// The comparison used to compare objects of type .
/// True if the value exists within the range; false otherwise.
public bool Contains(TRange value, Comparison comparison)
{
return comparison(Start, value) <= 0 && comparison(value, End) <= 0;
}
///
/// Determines whether the range contains the given range.
///
/// The range to be compared with.
/// True if the given range exists within this range; false otherwise.
public bool Contains(Range range)
{
return Contains(range, Comparer.Default);
}
///
/// Determines whether the range contains the given range.
///
/// The range to be compared with.
/// The comparer used to compare objects of type .
/// True if the given range exists within this range; false otherwise.
public bool Contains(Range range, IComparer comparer)
{
return Contains(range, comparer.Compare);
}
///
/// Determines whether the range contains the given range.
///
/// The range to be compared with.
/// The comparison used to compare objects of type .
/// True if the given range exists within this range; false otherwise.
public bool Contains(Range range, Comparison comparison)
{
return comparison(Start, range.Start) <= 0 && comparison(range.End, End) <= 0;
}
///
/// Determines whether the range overlaps with the given range.
///
/// The range to be compared with.
/// True if the ranges overlap; false otherwise.
public bool Overlaps(Range range)
{
return Overlaps(range, Comparer.Default);
}
///
/// Determines whether the range overlaps with the given range.
///
/// The range to be compared with.
/// The comparer used to compare objects of type .
/// True if the ranges overlap; false otherwise.
public bool Overlaps(Range range, IComparer comparer)
{
return Overlaps(range, comparer.Compare);
}
///
/// Determines whether the range overlaps with the given range.
///
/// The range to be compared with.
/// The comparison used to compare objects of type .
/// True if the ranges overlap; false otherwise.
public bool Overlaps(Range range, Comparison comparison)
{
return comparison(Start, range.End) <= 0 && comparison(range.Start, End) <= 0;
}
///
/// Merges two ranges into one range that fully encompasses both ranges.
///
/// The range to be merged with this one.
/// The range that fully encompasses the merged ranges.
/// is not .
public Range Merge(Range range)
{
return Merge(range, Comparer.Default);
}
///
/// Merges two ranges into one range that fully encompasses both ranges.
///
/// The range to be merged with this one.
/// The comparer used to compare objects of type .
/// The range that fully encompasses the merged ranges.
/// is not .
public Range Merge(Range range, IComparer comparer)
{
return Merge(range, comparer.Compare);
}
///
/// Merges two ranges into one range that fully encompasses both ranges.
///
/// The range to be merged with this one.
/// The comparison used to compare objects of type .
/// The range that fully encompasses the merged ranges.
/// is not .
public Range Merge(Range range, Comparison comparison)
{
if (!(State is IMergeable))
throw new InvalidOperationException("Unable to merge ranges because TState is not IMergeable.");
TRange start = comparison(Start, range.Start) <= 0 ? Start : range.Start;
TRange end = comparison(End, range.End) >= 0 ? End : range.End;
IMergeable mergeableState = (IMergeable)State;
TState state = mergeableState.Merge(range.State);
return new Range(start, end, state);
}
#endregion
#region [ Static ]
// Static Methods
///
/// Merges all ranges in a collection of ranges.
///
/// The collection of ranges to be merged.
/// A range that is the result of merging all ranges in the collection.
public static Range Merge(IEnumerable> ranges)
{
return Merge(ranges, Comparer.Default);
}
///
/// Merges all ranges in a collection of ranges.
///
/// The collection of ranges to be merged.
/// The comparer used to compare objects of type .
/// A range that is the result of merging all ranges in the collection.
public static Range Merge(IEnumerable> ranges, Comparer comparer)
{
return Merge(ranges, comparer.Compare);
}
///
/// Merges all ranges in a collection of ranges.
///
/// The collection of ranges to be merged.
/// The comparison used to compare objects of type .
/// A range that is the result of merging all ranges in the collection.
public static Range Merge(IEnumerable> ranges, Comparison comparison)
{
return ranges.Aggregate((range1, range2) => range1.Merge(range2, comparison));
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The collection of merged ranges.
public static IEnumerable> MergeConsecutiveOverlapping(IEnumerable> ranges)
{
return MergeConsecutiveOverlapping(ranges, Comparer.Default);
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparer used to compare objects of type .
/// The collection of merged ranges.
public static IEnumerable> MergeConsecutiveOverlapping(IEnumerable> ranges, IComparer comparer)
{
return MergeConsecutiveOverlapping(ranges, comparer.Compare);
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparison used to compare objects of type .
/// The collection of merged ranges.
public static IEnumerable> MergeConsecutiveOverlapping(IEnumerable> ranges, Comparison comparison)
{
Range currentRange = null;
foreach (Range range in ranges)
{
if ((object)currentRange == null)
{
currentRange = range;
}
else if (currentRange.Overlaps(range, comparison))
{
currentRange = currentRange.Merge(range, comparison);
}
else
{
yield return currentRange;
currentRange = range;
}
}
if ((object)currentRange != null)
yield return currentRange;
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The collection of merged ranges.
/// This method does not preserve the order of the source collection.
public static IEnumerable> MergeAllOverlapping(IEnumerable> ranges)
{
return MergeAllOverlapping(ranges, Comparer.Default);
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparison used to compare objects of type .
/// The collection of merged ranges.
/// This method does not preserve the order of the source collection.
public static IEnumerable> MergeAllOverlapping(IEnumerable> ranges, Comparison comparison)
{
return MergeAllOverlapping(ranges, Comparer.Create(comparison));
}
///
/// Merges all consecutive groups of overlapping ranges in a
/// collection and returns the resulting collection of ranges.
///
/// The collection of ranges.
/// The comparer used to compare objects of type .
/// The collection of merged ranges.
/// This method does not preserve the order of the source collection.
public static IEnumerable> MergeAllOverlapping(IEnumerable> ranges, IComparer comparer)
{
return MergeConsecutiveOverlapping(ranges.OrderBy(range => range.Start, comparer));
}
#endregion
}
///
/// Represents a type of object that is mergeable with another type of object.
///
/// The type of object to be merged with.
///
/// This interface was primarily designed for objects which are mergeable with other objects of the same type.
/// Therefore, should typically be the actual type of the object.
///
public interface IMergeable
{
///
/// Merges this object with another.
///
/// The other mergeable object.
/// The result of merging this object with another.
T Merge(T other);
}
}