namespace MeloongCore; /// /// 表示一个由可选下界和可选上界组成的范围。 /// public sealed class ValueRange( T? lower, T? upper, bool isLowerInclusive, bool isUpperInclusive, bool hasLower, bool hasUpper ) where T : notnull, IComparable { public T? Lower { get; private set; } = hasLower && lower is null ? throw new ArgumentNullException(nameof(lower)) : lower; public T? Upper { get; private set; } = hasUpper && upper is null ? throw new ArgumentNullException(nameof(upper)) : upper; public bool HasLower { get; private set; } = hasLower; public bool HasUpper { get; private set; } = hasUpper; public void ClearLower() { Lower = default; HasLower = false; } public void ClearUpper() { Upper = default; HasUpper = false; } public void SetLower(T lower, bool isInclusive) { Lower = lower; HasLower = true; IsLowerInclusive = isInclusive; } public void SetUpper(T upper, bool isInclusive) { Upper = upper; HasUpper = true; IsUpperInclusive = isInclusive; } /// 下界值是否为闭区间。 public bool IsLowerInclusive = isLowerInclusive; /// 上界值是否为闭区间。 public bool IsUpperInclusive = isUpperInclusive; #region 工厂方法 /// 创建等同于 [lower, upper] 的闭区间。 public static ValueRange Closed(T lower, T upper) => new(lower, upper, true, true, true, true); /// 创建形如 [lower, upper) 的左闭右开区间。 public static ValueRange ClosedOpen(T lower, T upper) => new(lower, upper, true, false, true, true); /// 创建形如 (lower, upper] 的左开右闭区间。 public static ValueRange OpenClosed(T lower, T upper) => new(lower, upper, false, true, true, true); /// 创建形如 (lower, upper) 的开区间。 public static ValueRange Open(T lower, T upper) => new(lower, upper, false, false, true, true); /// 创建形如 [value, value] 的单值闭区间。 public static ValueRange Exactly(T value) => new(value, value, true, true, true, true); /// 创建形如 [lower, +∞) 的范围。 public static ValueRange AtLeast(T lower) => new(lower, default, true, true, true, false); /// 创建形如 (lower, +∞) 的范围。 public static ValueRange GreaterThan(T lower) => new(lower, default, false, true, true, false); /// 创建形如 (-∞, upper] 的范围。 public static ValueRange AtMost(T upper) => new(default, upper, true, true, false, true); /// 创建形如 (-∞, upper) 的范围。 public static ValueRange LessThan(T upper) => new(default, upper, true, false, false, true); /// 创建形如 (-∞, +∞) 的无限范围。 public static ValueRange All() => new(default, default, true, true, false, false); #endregion /// /// 判断指定值是否位于当前范围内。 /// public bool Contains(T value) { if (HasLower) { var compareLower = value.CompareTo(Lower!); if (compareLower < 0) return false; if (compareLower == 0 && !IsLowerInclusive) return false; } if (HasUpper) { var compareUpper = value.CompareTo(Upper!); if (compareUpper > 0) return false; if (compareUpper == 0 && !IsUpperInclusive) return false; } return true; } /// /// 取当前范围与另一个范围的交集。 /// /// /// 若两个范围存在交集,则返回交集范围;否则返回 。 /// public ValueRange? Intersect(ValueRange other) { T? lower = default; T? upper = default; var isLowerInclusive = true; var isUpperInclusive = true; if (HasLower && other.HasLower) { switch (Lower!.CompareTo(other.Lower!)) { case > 0: lower = Lower; isLowerInclusive = IsLowerInclusive; break; case < 0: lower = other.Lower; isLowerInclusive = other.IsLowerInclusive; break; default: lower = Lower; isLowerInclusive = IsLowerInclusive && other.IsLowerInclusive; break; } } else if (HasLower) { lower = Lower; isLowerInclusive = IsLowerInclusive; } else if (other.HasLower) { lower = other.Lower; isLowerInclusive = other.IsLowerInclusive; } if (HasUpper && other.HasUpper) { switch (Upper!.CompareTo(other.Upper!)) { case < 0: upper = Upper; isUpperInclusive = IsUpperInclusive; break; case > 0: upper = other.Upper; isUpperInclusive = other.IsUpperInclusive; break; default: upper = Upper; isUpperInclusive = IsUpperInclusive && other.IsUpperInclusive; break; } } else if (HasUpper) { upper = Upper; isUpperInclusive = IsUpperInclusive; } else if (other.HasUpper) { upper = other.Upper; isUpperInclusive = other.IsUpperInclusive; } var anyHasLower = HasLower || other.HasLower; var anyHasUpper = HasUpper || other.HasUpper; if (anyHasLower && anyHasUpper) { var compare = lower!.CompareTo(upper!); if (compare > 0) return null; if (compare == 0 && (!isLowerInclusive || !isUpperInclusive)) return null; } return new(lower, upper, isLowerInclusive, isUpperInclusive, anyHasLower, anyHasUpper); } public override string ToString() => $"{(HasLower && IsLowerInclusive ? "[" : "(")}{(HasLower ? Lower!.ToString() : "-∞")}, {(HasUpper ? Upper!.ToString() : "+∞")}{(HasUpper && IsUpperInclusive ? "]" : ")")}"; }