151 lines
3.0 KiB
C#

namespace AssetRipper.Numerics;
public readonly struct Range<T> : IEquatable<Range<T>> where T : notnull, IComparable<T>, IEquatable<T>
{
/// <summary>
/// Represents the inclusive start of the Range.
/// </summary>
public T Start { get; }
/// <summary>
/// Represents the exclusive end of the Range.
/// </summary>
/// <remarks>
/// This must be greater than <see cref="Start"/>.
/// </remarks>
public T End { get; }
public Range(T start, T end)
{
if (start.IsGreaterEqual(end))
{
throw new ArgumentException($"{nameof(start)} {start} must be less than {nameof(end)} {end}");
}
Start = start;
End = end;
}
public bool Contains(T value)
{
return Start.IsLessEqual(value) && End.IsGreater(value);
}
public bool Contains(Range<T> range)
{
return Start.IsLessEqual(range.Start) && End.IsGreaterEqual(range.End);
}
public bool IsStrictlyLess(Range<T> other)
{
return End.IsLessEqual(other.Start);
}
public bool IsStrictlyGreater(Range<T> other)
{
return Start.IsGreaterEqual(other.End);
}
public bool Intersects(Range<T> other)
{
return Contains(other.Start) || other.Contains(Start);
}
public bool Intersects(Range<T> other, out Range<T> intersection)
{
if (Intersects(other))
{
intersection = MakeIntersectionInternal(other);
return true;
}
else
{
intersection = default;
return false;
}
}
public bool CanUnion(Range<T> other)
{
return Intersects(other) || Start.Equals(other.End) || End.Equals(other.Start);
}
public bool CanUnion(Range<T> other, out Range<T> union)
{
if (CanUnion(other))
{
union = MakeUnionInternal(other);
return true;
}
else
{
union = default;
return false;
}
}
public Range<T> MakeUnion(Range<T> other)
{
return CanUnion(other)
? MakeUnionInternal(other)
: throw new ArgumentException("These ranges cannot be unioned", nameof(other));
}
private Range<T> MakeUnionInternal(Range<T> other)
{
return new Range<T>(Minimum(Start, other.Start), Maximum(End, other.End));
}
public Range<T> MakeIntersection(Range<T> other)
{
return Intersects(other)
? MakeIntersectionInternal(other)
: throw new ArgumentException("These ranges do not intersect", nameof(other));
}
private Range<T> MakeIntersectionInternal(Range<T> other)
{
return new Range<T>(Maximum(Start, other.Start), Minimum(End, other.End));
}
private static T Minimum(T left, T right)
{
return left.IsLess(right) ? left : right;
}
private static T Maximum(T left, T right)
{
return left.IsGreater(right) ? left : right;
}
public override bool Equals(object? obj)
{
return obj is Range<T> range && Equals(range);
}
public bool Equals(Range<T> other)
{
return Start.Equals(other.Start) && End.Equals(other.End);
}
public override int GetHashCode()
{
return HashCode.Combine(Start, End);
}
public static bool operator ==(Range<T> left, Range<T> right)
{
return left.Equals(right);
}
public static bool operator !=(Range<T> left, Range<T> right)
{
return !(left == right);
}
public override string ToString()
{
return $"{Start} : {End}";
}
}