| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="ComparableExtensions.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using MyNet.Primitives.Comparers; |
| | | 9 | | using MyNet.Primitives.Intervals; |
| | | 10 | | |
| | | 11 | | #pragma warning disable IDE0130 // Namespace does not match folder structure |
| | | 12 | | namespace MyNet.Primitives; |
| | | 13 | | #pragma warning restore IDE0130 // Namespace does not match folder structure |
| | | 14 | | |
| | | 15 | | /// <summary> |
| | | 16 | | /// Extension methods to compare values using custom comparison operators. |
| | | 17 | | /// </summary> |
| | | 18 | | public static class ComparableExtensions |
| | | 19 | | { |
| | | 20 | | extension(IComparable? x) |
| | | 21 | | { |
| | | 22 | | /// <summary> |
| | | 23 | | /// Compares two IComparable values using the supplied operator. |
| | | 24 | | /// </summary> |
| | | 25 | | public bool Compare(IComparable? y, ComparableOperator sign) |
| | | 26 | | { |
| | 39 | 27 | | if (x == null || y == null) |
| | 6 | 28 | | return false; |
| | | 29 | | |
| | 33 | 30 | | var compare = x.CompareTo(y); |
| | | 31 | | |
| | 33 | 32 | | return sign switch |
| | 33 | 33 | | { |
| | 6 | 34 | | ComparableOperator.EqualsTo => compare == 0, |
| | 6 | 35 | | ComparableOperator.NotEqualsTo => compare != 0, |
| | 6 | 36 | | ComparableOperator.LessThan => compare < 0, |
| | 6 | 37 | | ComparableOperator.GreaterThan => compare > 0, |
| | 3 | 38 | | ComparableOperator.LessEqualThan => compare <= 0, |
| | 3 | 39 | | ComparableOperator.GreaterEqualThan => compare >= 0, |
| | 3 | 40 | | _ => throw new ArgumentException(null, nameof(sign)) |
| | 33 | 41 | | }; |
| | | 42 | | } |
| | | 43 | | |
| | | 44 | | /// <summary> |
| | | 45 | | /// Compares a value against a range using a complex operator. |
| | | 46 | | /// </summary> |
| | | 47 | | public bool Compare(IComparable? from, IComparable? to, ComplexComparableOperator sign) |
| | | 48 | | { |
| | 36 | 49 | | if (x == null || from == null || to == null) |
| | 9 | 50 | | return false; |
| | | 51 | | |
| | 27 | 52 | | var compareFrom = x.CompareTo(from); |
| | 27 | 53 | | var compareTo = x.CompareTo(to); |
| | | 54 | | |
| | 27 | 55 | | var result = compareFrom >= 0 && compareTo <= 0; |
| | | 56 | | |
| | 27 | 57 | | return sign switch |
| | 27 | 58 | | { |
| | 3 | 59 | | ComplexComparableOperator.IsBetween => result, |
| | 3 | 60 | | ComplexComparableOperator.IsNotBetween => !result, |
| | 3 | 61 | | ComplexComparableOperator.EqualsTo => compareFrom == 0, |
| | 3 | 62 | | ComplexComparableOperator.NotEqualsTo => compareFrom != 0, |
| | 3 | 63 | | ComplexComparableOperator.LessThan => compareTo < 0, |
| | 3 | 64 | | ComplexComparableOperator.GreaterThan => compareFrom > 0, |
| | 3 | 65 | | ComplexComparableOperator.LessEqualThan => compareTo <= 0, |
| | 3 | 66 | | ComplexComparableOperator.GreaterEqualThan => compareFrom >= 0, |
| | 3 | 67 | | _ => throw new ArgumentException(null, nameof(sign)) |
| | 27 | 68 | | }; |
| | | 69 | | } |
| | | 70 | | } |
| | | 71 | | |
| | | 72 | | /// <summary> |
| | | 73 | | /// Compares a generic comparable against a range using a complex operator. |
| | | 74 | | /// </summary> |
| | | 75 | | public static bool Compare<T>(this IComparable<T> x, T? from, T? to, ComplexComparableOperator sign) |
| | | 76 | | where T : struct, IComparable<T> |
| | | 77 | | { |
| | 27 | 78 | | var compareFrom = x.CompareNullableTo(from); |
| | 27 | 79 | | var compareTo = x.CompareNullableTo(to); |
| | | 80 | | |
| | 27 | 81 | | var result = compareFrom >= 0 && compareTo <= 0; |
| | | 82 | | |
| | 27 | 83 | | return sign switch |
| | 27 | 84 | | { |
| | 3 | 85 | | ComplexComparableOperator.IsBetween => result, |
| | 3 | 86 | | ComplexComparableOperator.IsNotBetween => !result, |
| | 3 | 87 | | ComplexComparableOperator.EqualsTo => compareFrom == 0, |
| | 3 | 88 | | ComplexComparableOperator.NotEqualsTo => compareFrom != 0, |
| | 3 | 89 | | ComplexComparableOperator.LessThan => compareTo < 0, |
| | 3 | 90 | | ComplexComparableOperator.GreaterThan => compareFrom > 0, |
| | 3 | 91 | | ComplexComparableOperator.LessEqualThan => compareTo <= 0, |
| | 3 | 92 | | ComplexComparableOperator.GreaterEqualThan => compareFrom >= 0, |
| | 3 | 93 | | _ => throw new ArgumentException(null, nameof(sign)) |
| | 27 | 94 | | }; |
| | | 95 | | } |
| | | 96 | | |
| | | 97 | | extension<T>(IComparable<T>? value) |
| | | 98 | | where T : struct, IComparable<T> |
| | | 99 | | { |
| | | 100 | | /// <summary> |
| | | 101 | | /// Determines if the current value is between two other values, handling nulls safely. The range is exclusive, |
| | | 102 | | /// </summary> |
| | | 103 | | /// <param name="from">The lower bound value.</param> |
| | | 104 | | /// <param name="to">The upper bound value.</param> |
| | | 105 | | /// <returns><c>true</c> if the current value is between the specified values; otherwise, <c>false</c>.</returns |
| | 12 | 106 | | public bool IsBetween(T? from, T? to) => value is T t && new Interval<T>(from, to, false).Contains(t); |
| | | 107 | | |
| | | 108 | | /// <summary> |
| | | 109 | | /// Returns the maximum of two IComparable values. |
| | | 110 | | /// </summary> |
| | | 111 | | /// <param name="other">The value to compare with.</param> |
| | | 112 | | /// <returns>The maximum of the two values.</returns> |
| | 15 | 113 | | public T Max(T other) => value is T t && t.CompareTo(other) > 0 ? t : other; |
| | | 114 | | |
| | | 115 | | /// <summary> |
| | | 116 | | /// Returns the minimum of two IComparable values. |
| | | 117 | | /// </summary> |
| | | 118 | | /// <param name="other">The value to compare with.</param> |
| | | 119 | | /// <returns>The minimum of the two values.</returns> |
| | 15 | 120 | | public T Min(T other) => value is T t && t.CompareTo(other) < 0 ? t : other; |
| | | 121 | | |
| | | 122 | | /// <summary> |
| | | 123 | | /// Returns a MinMax struct containing the minimum and maximum of the current value and another value. The metho |
| | | 124 | | /// </summary> |
| | | 125 | | /// <param name="other">The value to compare with.</param> |
| | | 126 | | /// <returns>A MinMax struct containing the minimum and maximum values.</returns> |
| | | 127 | | /// <exception cref="InvalidCastException">Thrown if the current value is null or not of type T.</exception> |
| | 60 | 128 | | public MinMax<T> MinMax(T other) => value is T t ? (t.CompareTo(other) >= 0 ? new(other, t) : new(t, other)) : t |
| | | 129 | | |
| | | 130 | | /// <summary> |
| | | 131 | | /// Returns a MinMax struct containing the minimum and maximum of the current value and another value. The metho |
| | | 132 | | /// </summary> |
| | | 133 | | /// <param name="other">The value to compare with.</param> |
| | | 134 | | /// <returns>A MinMax struct containing the minimum and maximum values.</returns> |
| | | 135 | | /// <exception cref="InvalidCastException">Thrown if the current value is null or not of type T.</exception> |
| | 75 | 136 | | public OptionalMinMax<T> MinMax(T? other) => new NullableComparer<T>().Compare(value, other) >= 0 ? new(other, v |
| | | 137 | | |
| | | 138 | | /// <summary> |
| | | 139 | | /// Clamps a value between a minimum and maximum value. |
| | | 140 | | /// </summary> |
| | | 141 | | /// <param name="min"> The minimum value. </param> |
| | | 142 | | /// <param name="max"> The maximum value. </param> |
| | | 143 | | public T SafeClamp(T min, T max) |
| | | 144 | | { |
| | 54 | 145 | | var minMax = min.MinMax(max); |
| | | 146 | | |
| | 54 | 147 | | return value is not T t |
| | 54 | 148 | | ? throw new InvalidCastException() |
| | 54 | 149 | | : t.CompareTo(minMax.Min) < 0 ? minMax.Min : t.CompareTo(minMax.Max) > 0 ? minMax.Max : t; |
| | | 150 | | } |
| | | 151 | | |
| | | 152 | | /// <summary> |
| | | 153 | | /// Safely compares a nullable comparable to a value. |
| | | 154 | | /// </summary> |
| | 12 | 155 | | public int CompareNullableTo(T other) => new NullableComparer<T>().Compare(value, other); |
| | | 156 | | |
| | | 157 | | /// <summary> |
| | | 158 | | /// Safely compares a nullable comparable to a nullable value. |
| | | 159 | | /// </summary> |
| | 69 | 160 | | public int CompareNullableTo(T? other) => new NullableComparer<T>().Compare(value, other); |
| | | 161 | | |
| | | 162 | | /// <summary> |
| | | 163 | | /// Determines if the current value is in a range defined by two other values, handling nulls safely. The range |
| | | 164 | | /// </summary> |
| | | 165 | | /// <param name="from">The lower bound value.</param> |
| | | 166 | | /// <param name="to">The upper bound value.</param> |
| | | 167 | | /// <returns><c>true</c> if the current value is in the specified range; otherwise, <c>false</c>.</returns> |
| | 24 | 168 | | public bool InRange(T? from, T? to) => value is T t && new Interval<T>(from, to).Contains(t); |
| | | 169 | | } |
| | | 170 | | } |
| | | 171 | | |
| | | 172 | | /// <summary> |
| | | 173 | | /// Represents a minimum and maximum value of a comparable type. This struct is immutable and provides a convenient way |
| | | 174 | | /// </summary> |
| | | 175 | | /// <param name="Min">The minimum value.</param> |
| | | 176 | | /// <param name="Max">The maximum value.</param> |
| | | 177 | | /// <typeparam name="T">The type of the comparable values.</typeparam> |
| | | 178 | | public readonly record struct MinMax<T>(T Min, T Max) |
| | | 179 | | where T : struct, IComparable<T>; |
| | | 180 | | |
| | | 181 | | /// <summary> |
| | | 182 | | /// Represents an optional minimum and maximum value of a comparable type. This struct is immutable and provides a conve |
| | | 183 | | /// </summary> |
| | | 184 | | /// <param name="Min">The minimum value, or null if unbounded.</param> |
| | | 185 | | /// <param name="Max">The maximum value, or null if unbounded.</param> |
| | | 186 | | /// <typeparam name="T">The type of the comparable values.</typeparam> |
| | | 187 | | public readonly record struct OptionalMinMax<T>(T? Min, T? Max) |
| | | 188 | | where T : struct, IComparable<T>; |
| | | 189 | | |