namespace MeloongCore;
///
/// 表示一个由可选下界和可选上界组成的范围。
///
public sealed class ValueRange(
T? lower, T? upper,
bool isLowerInclusive, bool isUpperInclusive,
bool hasLower, bool hasUpper
) : IEquatable?> where T : IComparable {
#region 基础字段
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;
#endregion
#region ClosedOpen、AtLeast 等工厂方法
/// 创建等同于 [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
#region 字符串双向转换
///
/// 从字符串解析 实例。
///
public static ValueRange FromString(string str, Func? parseFunc = null) {
if (string.IsNullOrEmpty(str)) throw new ArgumentException("不能为空");
var isLowerInclusive = str[0] == '[';
var isUpperInclusive = str[^1] == ']';
if (!isLowerInclusive && str[0] != '(') throw new ArgumentException("开头必须是 [ 或 (");
if (!isUpperInclusive && str[^1] != ')') throw new ArgumentException("结尾必须是 ] 或 )");
var commaIndex = str.IndexOfAny([',', ',']);
if (commaIndex == -1) throw new ArgumentException("需要用逗号分隔两个值");
var lowerStr = str[1..commaIndex].Trim();
var upperStr = str[(commaIndex + 1)..^1].Trim();
T? lower = default;
T? upper = default;
bool hasLower = false;
bool hasUpper = false;
if (lowerStr is not ("" or "-∞")) {
lower = parseFunc != null ? parseFunc(lowerStr) : (T) Convert.ChangeType(lowerStr, typeof(T));
hasLower = true;
}
if (upperStr is not ("" or "+∞" or "∞")) {
upper = parseFunc != null ? parseFunc(upperStr) : (T) Convert.ChangeType(upperStr, typeof(T));
hasUpper = true;
}
return new(lower, upper, isLowerInclusive, isUpperInclusive, hasLower, hasUpper);
}
public override string ToString()
=> $"{(HasLower && IsLowerInclusive ? "[" : "(")}{(HasLower ? Lower!.ToString() : "-∞")}, {(HasUpper ? Upper!.ToString() : "+∞")}{(HasUpper && IsUpperInclusive ? "]" : ")")}";
#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 bool IsEmpty() {
if (!HasLower || !HasUpper) return false;
int compared = Lower!.CompareTo(Upper!);
return compared > 0 || (compared == 0 && (!IsLowerInclusive || !IsUpperInclusive));
}
///
/// 取当前范围与另一个范围的交集。
///
/// 若两个范围存在交集,则返回交集范围;否则返回 。
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;
return new(lower, upper, isLowerInclusive, isUpperInclusive, anyHasLower, anyHasUpper);
}
#region 相等判断(自动生成)
public override bool Equals(object? obj) => Equals(obj as ValueRange);
public bool Equals(ValueRange? other) {
return other is not null&&
EqualityComparer.Default.Equals(Lower, other.Lower)&&
EqualityComparer.Default.Equals(Upper, other.Upper)&&
HasLower==other.HasLower&&
HasUpper==other.HasUpper&&
IsLowerInclusive==other.IsLowerInclusive&&
IsUpperInclusive==other.IsUpperInclusive;
}
public override int GetHashCode() {
int hashCode = 169504101;
hashCode=hashCode*-1521134295+EqualityComparer.Default.GetHashCode(Lower);
hashCode=hashCode*-1521134295+EqualityComparer.Default.GetHashCode(Upper);
hashCode=hashCode*-1521134295+HasLower.GetHashCode();
hashCode=hashCode*-1521134295+HasUpper.GetHashCode();
hashCode=hashCode*-1521134295+IsLowerInclusive.GetHashCode();
hashCode=hashCode*-1521134295+IsUpperInclusive.GetHashCode();
return hashCode;
}
#pragma warning disable CS8604 // 引用类型参数可能为 null。
public static bool operator ==(ValueRange? left, ValueRange? right) => EqualityComparer>.Default.Equals(left, right);
#pragma warning restore CS8604 // 引用类型参数可能为 null。
public static bool operator !=(ValueRange? left, ValueRange? right) => !(left==right);
#endregion
}