diff --git a/BizHawk.Common/Ranges.cs b/BizHawk.Common/Ranges.cs
index d65526d213..68064fdb53 100644
--- a/BizHawk.Common/Ranges.cs
+++ b/BizHawk.Common/Ranges.cs
@@ -6,6 +6,7 @@ using BizHawk.Common.NumberExtensions;
namespace BizHawk.Common
{
+ /// semantically similar to , but obviously does no checks at runtime
public struct RangeStruct where T : unmanaged, IComparable
{
public T Start;
@@ -13,7 +14,7 @@ namespace BizHawk.Common
public T EndInclusive;
}
- /// represents a closed, inclusive range of ( ≤ )
+ /// represents a closed range of (class invariant: ≤ )
public interface Range where T : unmanaged, IComparable
{
T Start { get; }
@@ -21,49 +22,16 @@ namespace BizHawk.Common
T EndInclusive { get; }
}
- public class RangeImpl : Range where T : unmanaged, IComparable
+ /// represents a closed range of which can be grown or shrunk (class invariant: ≤ )
+ public class MutableRange : Range where T : unmanaged, IComparable
{
- protected RangeStruct r;
+ private RangeStruct r;
- /// . < ., or is float/double and either bound is NaN
- internal RangeImpl(RangeStruct range)
- {
- r = ValidatedOrThrow(range);
- }
-
- /// < , or is / and either bound is
- public RangeImpl(T start, T endInclusive) : this(new RangeStruct { Start = start, EndInclusive = endInclusive }) {}
-
- public T Start => r.Start;
-
- public T EndInclusive => r.EndInclusive;
-
- internal RangeStruct CopyStruct() => r;
-
- protected static RangeStruct ValidatedOrThrow(RangeStruct range) where T1 : unmanaged, IComparable
- {
- if (range is RangeStruct fr && (float.IsNaN(fr.Start) || float.IsNaN(fr.EndInclusive))
- || range is RangeStruct dr && (double.IsNaN(dr.Start) || double.IsNaN(dr.EndInclusive)))
- {
- throw new ArgumentException("range bound is NaN", nameof(range));
- }
- if (range.EndInclusive.CompareTo(range.Start) < 0) throw new ArgumentException("range end < start", nameof(range));
- return range;
- }
- }
-
- /// represents a closed, inclusive range of ( ≤ ) which can be grown or shrunk
- /// inheriting reduces code duplication in
- public class MutableRange : RangeImpl where T : unmanaged, IComparable
- {
- ///
- internal MutableRange(RangeStruct range) : base(range) {}
-
- ///
- public MutableRange(T start, T endInclusive) : base(new RangeStruct { Start = start, EndInclusive = endInclusive }) {}
+ ///
+ public MutableRange(T start, T endInclusive) => Overwrite(start, endInclusive);
/// (from setter) >
- public new T Start
+ public T Start
{
get => r.Start;
set => r.Start = r.EndInclusive.CompareTo(value) < 0
@@ -72,7 +40,7 @@ namespace BizHawk.Common
}
/// (from setter) <
- public new T EndInclusive
+ public T EndInclusive
{
get => r.EndInclusive;
set => r.EndInclusive = value.CompareTo(r.Start) < 0
@@ -80,15 +48,31 @@ namespace BizHawk.Common
: value;
}
- public void Overwrite(T start, T endInclusive) => r = ValidatedOrThrow(new RangeStruct { Start = start, EndInclusive = endInclusive });
+ /// < , or is / and either bound is
+ public void Overwrite(T start, T endInclusive)
+ {
+ var range = new RangeStruct { Start = start, EndInclusive = endInclusive };
+ if (range.EndInclusive.CompareTo(range.Start) < 0) throw new ArgumentException("range end < start", nameof(range));
+ if (range is RangeStruct fr && (float.IsNaN(fr.Start) || float.IsNaN(fr.EndInclusive))
+ || range is RangeStruct dr && (double.IsNaN(dr.Start) || double.IsNaN(dr.EndInclusive)))
+ {
+ throw new ArgumentException("range bound is NaN", nameof(range));
+ }
+ r = range;
+ }
}
/// contains most of the logic for ranges
- /// non-generic overloads are used where the method requires an increment or decrement
+ ///
+ /// non-generic overloads are used where the method requires an increment or decrement
+ /// TODO which Enumerate algorithm is faster - yield return in loop or ?
+ ///
public static class RangeExtensions
{
private const string EXCL_RANGE_ARITH_EXC_TEXT = "exclusive range end is min value of integral type";
+ private const ulong MIN_LONG_NEGATION_AS_ULONG = 9223372036854775808UL;
+
/// if it's contained in , or else whichever bound of is closest to
public static T ConstrainWithin(this T value, Range range) where T : unmanaged, IComparable => value.CompareTo(range.Start) < 0
? range.Start
@@ -97,27 +81,33 @@ namespace BizHawk.Common
: value;
/// true iff is contained in ( is considered to be in the range if it's exactly equal to either bound)
- ///
+ ///
public static bool Contains(this Range range, T value) where T : unmanaged, IComparable => !(value.CompareTo(range.Start) < 0 || range.EndInclusive.CompareTo(value) < 0);
- public static byte Count(this Range range) => (byte) (range.EndInclusive - range.Start + (byte) 1U);
+ public static uint Count(this Range range) => (uint) (range.EndInclusive - range.Start + 1);
- public static uint Count(this Range range) => (uint) (1L + range.EndInclusive - range.Start);
+ /// beware integer overflow when contains every value
+ public static uint Count(this Range range) => (uint) ((long) range.EndInclusive - range.Start) + 1U;
- public static ulong Count(this Range range) => throw new NotImplementedException("TODO fancy math");
+ ///
+ public static ulong Count(this Range range) => (range.Contains(0L)
+ ? (range.Start == long.MinValue ? MIN_LONG_NEGATION_AS_ULONG : (ulong) -range.Start) + (ulong) range.EndInclusive
+ : (ulong) (range.EndInclusive - range.Start)
+ ) + 1UL;
- public static byte Count(this Range range) => (byte) (1 + range.EndInclusive - range.Start);
+ public static uint Count(this Range range) => (uint) (range.EndInclusive - range.Start + 1);
- public static ushort Count(this Range range) => (ushort) (1 + range.EndInclusive - range.Start);
+ public static uint Count(this Range range) => (uint) (range.EndInclusive - range.Start + 1);
+ ///
public static uint Count(this Range range) => range.EndInclusive - range.Start + 1U;
- public static ulong Count(this Range range) => range.EndInclusive - range.Start + 1U;
+ ///
+ public static ulong Count(this Range range) => range.EndInclusive - range.Start + 1UL;
- public static ushort Count(this Range range) => (ushort) (range.EndInclusive - range.Start + (ushort) 1U);
+ public static uint Count(this Range range) => (uint) (range.EndInclusive - range.Start + 1);
- /// TODO is this faster or slower than the yield return algorithm used in ?
- public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, range.Count()).Select(i => (byte) i);
+ public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, (int) range.Count()).Select(i => (byte) i);
///
public static IEnumerable Enumerate(this Range range, double step)
@@ -157,9 +147,9 @@ namespace BizHawk.Common
yield return l;
}
- public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, range.Count()).Select(i => (sbyte) i);
+ public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, (int) range.Count()).Select(i => (sbyte) i);
- public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, range.Count()).Select(i => (short) i);
+ public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, (int) range.Count()).Select(i => (short) i);
public static IEnumerable Enumerate(this Range range)
{
@@ -175,87 +165,85 @@ namespace BizHawk.Common
yield return l;
}
- public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, range.Count()).Select(i => (ushort) i);
+ public static IEnumerable Enumerate(this Range range) => Enumerable.Range(range.Start, (int) range.Count()).Select(i => (ushort) i);
- public static Range GetImmutableCopy(this MutableRange range) where T : unmanaged, IComparable => new MutableRange(range.CopyStruct());
+ public static Range GetImmutableCopy(this MutableRange range) where T : unmanaged, IComparable => GetMutableCopy(range);
- public static MutableRange GetMutableCopy(this Range range) where T : unmanaged, IComparable => range is RangeImpl impl
- ? new MutableRange(impl.CopyStruct()) // copied by value when using the implementations in this file
- : new MutableRange(range.Start, range.EndInclusive);
+ public static MutableRange GetMutableCopy(this Range range) where T : unmanaged, IComparable => new MutableRange(range.Start, range.EndInclusive);
///
public static MutableRange MutableRangeTo(this T start, T endInclusive) where T : unmanaged, IComparable => new MutableRange(start, endInclusive);
- ///
- public static MutableRange MutableRangeToExcluding(this byte start, byte endExclusive) => endExclusive == byte.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this byte start, byte endExclusive) => endExclusive == byte.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, (byte) (endExclusive - 1U));
- ///
- public static MutableRange MutableRangeToExcluding(this int start, int endExclusive) => endExclusive == int.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this int start, int endExclusive) => endExclusive == int.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, endExclusive - 1);
- ///
- public static MutableRange MutableRangeToExcluding(this long start, long endExclusive) => endExclusive == long.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this long start, long endExclusive) => endExclusive == long.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, endExclusive - 1L);
- ///
- public static MutableRange MutableRangeToExcluding(this sbyte start, sbyte endExclusive) => endExclusive == sbyte.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this sbyte start, sbyte endExclusive) => endExclusive == sbyte.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, (sbyte) (endExclusive - 1));
- ///
- public static MutableRange MutableRangeToExcluding(this short start, short endExclusive) => endExclusive == short.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this short start, short endExclusive) => endExclusive == short.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, (short) (endExclusive - 1));
- ///
- public static MutableRange MutableRangeToExcluding(this uint start, uint endExclusive) => endExclusive == uint.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this uint start, uint endExclusive) => endExclusive == uint.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, endExclusive - 1U);
- ///
- public static MutableRange MutableRangeToExcluding(this ulong start, ulong endExclusive) => endExclusive == ulong.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this ulong start, ulong endExclusive) => endExclusive == ulong.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, endExclusive - 1UL);
- ///
- public static MutableRange MutableRangeToExcluding(this ushort start, ushort endExclusive) => endExclusive == ushort.MinValue
+ ///
+ public static MutableRange MutableRangeToExclusive(this ushort start, ushort endExclusive) => endExclusive == ushort.MinValue
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
: new MutableRange(start, (ushort) (endExclusive - 1U));
- ///
- public static Range RangeTo(this T start, T endInclusive) where T : unmanaged, IComparable => new RangeImpl(start, endInclusive);
+ ///
+ public static Range RangeTo(this T start, T endInclusive) where T : unmanaged, IComparable => start.MutableRangeTo(endInclusive);
- ///
- public static Range RangeToExcluding(this byte start, byte endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this byte start, byte endExclusive) => MutableRangeToExclusive(start, endExclusive);
/// ≤ (empty ranges where = are not permitted)
/// is min value of integral type (therefore ≤ )
- public static Range RangeToExcluding(this int start, int endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ public static Range RangeToExclusive(this int start, int endExclusive) => MutableRangeToExclusive(start, endExclusive);
- ///
- public static Range RangeToExcluding(this long start, long endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this long start, long endExclusive) => MutableRangeToExclusive(start, endExclusive);
- ///
- public static Range RangeToExcluding(this sbyte start, sbyte endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this sbyte start, sbyte endExclusive) => MutableRangeToExclusive(start, endExclusive);
- ///
- public static Range RangeToExcluding(this short start, short endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this short start, short endExclusive) => MutableRangeToExclusive(start, endExclusive);
- ///
- public static Range RangeToExcluding(this uint start, uint endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this uint start, uint endExclusive) => MutableRangeToExclusive(start, endExclusive);
- ///
- public static Range RangeToExcluding(this ulong start, ulong endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this ulong start, ulong endExclusive) => MutableRangeToExclusive(start, endExclusive);
- ///
- public static Range RangeToExcluding(this ushort start, ushort endExclusive) => MutableRangeToExcluding(start, endExclusive);
+ ///
+ public static Range RangeToExclusive(this ushort start, ushort endExclusive) => MutableRangeToExclusive(start, endExclusive);
/// true iff is strictly contained in ( is considered to be OUTSIDE the range if it's exactly equal to either bound)
///
public static bool StrictlyBoundedBy(this T value, Range range) where T : unmanaged, IComparable => range.Start.CompareTo(value) < 0 && value.CompareTo(range.EndInclusive) < 0;
}
-}
\ No newline at end of file
+}