ValueRange.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. namespace MeloongCore;
  2. /// <summary>
  3. /// 表示一个由可选下界和可选上界组成的范围。
  4. /// </summary>
  5. public sealed class ValueRange<T>(
  6. T? lower, T? upper,
  7. bool isLowerInclusive, bool isUpperInclusive,
  8. bool hasLower, bool hasUpper
  9. ) where T : notnull, IComparable<T> {
  10. public T? Lower { get; private set; } = hasLower && lower is null ? throw new ArgumentNullException(nameof(lower)) : lower;
  11. public T? Upper { get; private set; } = hasUpper && upper is null ? throw new ArgumentNullException(nameof(upper)) : upper;
  12. public bool HasLower { get; private set; } = hasLower;
  13. public bool HasUpper { get; private set; } = hasUpper;
  14. public void ClearLower() { Lower = default; HasLower = false; }
  15. public void ClearUpper() { Upper = default; HasUpper = false; }
  16. public void SetLower(T lower, bool isInclusive) {
  17. Lower = lower;
  18. HasLower = true;
  19. IsLowerInclusive = isInclusive;
  20. }
  21. public void SetUpper(T upper, bool isInclusive) {
  22. Upper = upper;
  23. HasUpper = true;
  24. IsUpperInclusive = isInclusive;
  25. }
  26. /// <summary> 下界值是否为闭区间。 </summary>
  27. public bool IsLowerInclusive = isLowerInclusive;
  28. /// <summary> 上界值是否为闭区间。 </summary>
  29. public bool IsUpperInclusive = isUpperInclusive;
  30. #region 工厂方法
  31. /// <summary> 创建等同于 <c>[lower, upper]</c> 的闭区间。 </summary>
  32. public static ValueRange<T> Closed(T lower, T upper) => new(lower, upper, true, true, true, true);
  33. /// <summary> 创建形如 <c>[lower, upper)</c> 的左闭右开区间。 </summary>
  34. public static ValueRange<T> ClosedOpen(T lower, T upper) => new(lower, upper, true, false, true, true);
  35. /// <summary> 创建形如 <c>(lower, upper]</c> 的左开右闭区间。 </summary>
  36. public static ValueRange<T> OpenClosed(T lower, T upper) => new(lower, upper, false, true, true, true);
  37. /// <summary> 创建形如 <c>(lower, upper)</c> 的开区间。 </summary>
  38. public static ValueRange<T> Open(T lower, T upper) => new(lower, upper, false, false, true, true);
  39. /// <summary> 创建形如 <c>[value, value]</c> 的单值闭区间。 </summary>
  40. public static ValueRange<T> Exactly(T value) => new(value, value, true, true, true, true);
  41. /// <summary> 创建形如 <c>[lower, +∞)</c> 的范围。 </summary>
  42. public static ValueRange<T> AtLeast(T lower) => new(lower, default, true, true, true, false);
  43. /// <summary> 创建形如 <c>(lower, +∞)</c> 的范围。 </summary>
  44. public static ValueRange<T> GreaterThan(T lower) => new(lower, default, false, true, true, false);
  45. /// <summary> 创建形如 <c>(-∞, upper]</c> 的范围。 </summary>
  46. public static ValueRange<T> AtMost(T upper) => new(default, upper, true, true, false, true);
  47. /// <summary> 创建形如 <c>(-∞, upper)</c> 的范围。 </summary>
  48. public static ValueRange<T> LessThan(T upper) => new(default, upper, true, false, false, true);
  49. /// <summary> 创建形如 <c>(-∞, +∞)</c> 的无限范围。 </summary>
  50. public static ValueRange<T> All() => new(default, default, true, true, false, false);
  51. #endregion
  52. /// <summary>
  53. /// 判断指定值是否位于当前范围内。
  54. /// </summary>
  55. public bool Contains(T value) {
  56. if (HasLower) {
  57. var compareLower = value.CompareTo(Lower!);
  58. if (compareLower < 0) return false;
  59. if (compareLower == 0 && !IsLowerInclusive) return false;
  60. }
  61. if (HasUpper) {
  62. var compareUpper = value.CompareTo(Upper!);
  63. if (compareUpper > 0) return false;
  64. if (compareUpper == 0 && !IsUpperInclusive) return false;
  65. }
  66. return true;
  67. }
  68. /// <summary>
  69. /// 取当前范围与另一个范围的交集。
  70. /// </summary>
  71. /// <returns>
  72. /// 若两个范围存在交集,则返回交集范围;否则返回 <see langword="null" />。
  73. /// </returns>
  74. public ValueRange<T>? Intersect(ValueRange<T> other) {
  75. T? lower = default;
  76. T? upper = default;
  77. var isLowerInclusive = true;
  78. var isUpperInclusive = true;
  79. if (HasLower && other.HasLower) {
  80. switch (Lower!.CompareTo(other.Lower!)) {
  81. case > 0:
  82. lower = Lower;
  83. isLowerInclusive = IsLowerInclusive;
  84. break;
  85. case < 0:
  86. lower = other.Lower;
  87. isLowerInclusive = other.IsLowerInclusive;
  88. break;
  89. default:
  90. lower = Lower;
  91. isLowerInclusive = IsLowerInclusive && other.IsLowerInclusive;
  92. break;
  93. }
  94. } else if (HasLower) {
  95. lower = Lower;
  96. isLowerInclusive = IsLowerInclusive;
  97. } else if (other.HasLower) {
  98. lower = other.Lower;
  99. isLowerInclusive = other.IsLowerInclusive;
  100. }
  101. if (HasUpper && other.HasUpper) {
  102. switch (Upper!.CompareTo(other.Upper!)) {
  103. case < 0:
  104. upper = Upper;
  105. isUpperInclusive = IsUpperInclusive;
  106. break;
  107. case > 0:
  108. upper = other.Upper;
  109. isUpperInclusive = other.IsUpperInclusive;
  110. break;
  111. default:
  112. upper = Upper;
  113. isUpperInclusive = IsUpperInclusive && other.IsUpperInclusive;
  114. break;
  115. }
  116. } else if (HasUpper) {
  117. upper = Upper;
  118. isUpperInclusive = IsUpperInclusive;
  119. } else if (other.HasUpper) {
  120. upper = other.Upper;
  121. isUpperInclusive = other.IsUpperInclusive;
  122. }
  123. var anyHasLower = HasLower || other.HasLower;
  124. var anyHasUpper = HasUpper || other.HasUpper;
  125. if (anyHasLower && anyHasUpper) {
  126. var compare = lower!.CompareTo(upper!);
  127. if (compare > 0) return null;
  128. if (compare == 0 && (!isLowerInclusive || !isUpperInclusive)) return null;
  129. }
  130. return new(lower, upper, isLowerInclusive, isUpperInclusive, anyHasLower, anyHasUpper);
  131. }
  132. public override string ToString()
  133. => $"{(HasLower && IsLowerInclusive ? "[" : "(")}{(HasLower ? Lower!.ToString() : "-∞")}, {(HasUpper ? Upper!.ToString() : "+∞")}{(HasUpper && IsUpperInclusive ? "]" : ")")}";
  134. }