mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 17:18:48 +00:00
Compare commits
22 Commits
c334ceb7fe
...
de86d3a79c
Author | SHA1 | Date | |
---|---|---|---|
|
de86d3a79c | ||
d2924d7ac0 | |||
77dd144321 | |||
19c467d88b | |||
3e0853d102 | |||
30f158e861 | |||
eb46257b75 | |||
69f0fa7b80 | |||
8cdbfcfe0d | |||
99e0bef64d | |||
cc94905930 | |||
458ab03be0 | |||
be44b9d549 | |||
bb2f67e9b6 | |||
2da8c7db7a | |||
b8f85e4270 | |||
39dfea7622 | |||
423fb90cc4 | |||
8bc7372ba4 | |||
21271314af | |||
b91aad6305 | |||
87a85b82d9 |
@ -40,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
BigEndian/LittleEndian methods.
|
BigEndian/LittleEndian methods.
|
||||||
- X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` now throw ArgumentException in lieu of
|
- X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` now throw ArgumentException in lieu of
|
||||||
TypeInitializationException.
|
TypeInitializationException.
|
||||||
- X10D: `char.IsEmoji` no longer allocates for .NET 7.
|
- X10D: `char.IsEmoji` no longer allocates for .NET 7+.
|
||||||
- X10D: `string.Repeat` is now more efficient.
|
- X10D: `string.Repeat` is now more efficient.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
@ -49,6 +49,7 @@ TypeInitializationException.
|
|||||||
- X10D: Removed `Endianness` enum.
|
- X10D: Removed `Endianness` enum.
|
||||||
- X10D: Removed `Span<T>.Replace(T, T)` for .NET 8 target.
|
- X10D: Removed `Span<T>.Replace(T, T)` for .NET 8 target.
|
||||||
- X10D: Removed .NET Standard 2.1 target.
|
- X10D: Removed .NET Standard 2.1 target.
|
||||||
|
- X10D: Removed extensions for `Progress<T>`. These are already provided by [`Observable.FromEventPattern`](https://learn.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh229424(v=vs.103)).
|
||||||
- X10D.Hosting: Removed .NET Standard 2.1 target.
|
- X10D.Hosting: Removed .NET Standard 2.1 target.
|
||||||
- X10D.DSharpPlus: Complete sunset of library. This library will not be updated to support DSharpPlus v5.0.0 (#83).
|
- X10D.DSharpPlus: Complete sunset of library. This library will not be updated to support DSharpPlus v5.0.0 (#83).
|
||||||
- X10D.Unity: Complete sunset of library. This library will not be updated effective immediately (#86).
|
- X10D.Unity: Complete sunset of library. This library will not be updated effective immediately (#86).
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using X10D.IO;
|
using X10D.IO;
|
||||||
|
|
||||||
namespace X10D.Tests.IO;
|
namespace X10D.Tests.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using X10D.IO;
|
using X10D.IO;
|
||||||
|
|
||||||
namespace X10D.Tests.IO;
|
namespace X10D.Tests.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using X10D.IO;
|
using X10D.IO;
|
||||||
|
|
||||||
namespace X10D.Tests.IO;
|
namespace X10D.Tests.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using X10D.Math;
|
using X10D.Math;
|
||||||
|
|
||||||
namespace X10D.Tests.Math;
|
namespace X10D.Tests.Math;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using X10D.Math;
|
using X10D.Math;
|
||||||
|
|
||||||
namespace X10D.Tests.Math;
|
namespace X10D.Tests.Math;
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
using NUnit.Framework;
|
|
||||||
using X10D.Reactive;
|
|
||||||
|
|
||||||
namespace X10D.Tests.Reactive;
|
|
||||||
|
|
||||||
[TestFixture]
|
|
||||||
internal class ProgressTests
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void OnProgressChanged_ShouldCallCompletionDelegate_GivenCompletionValue()
|
|
||||||
{
|
|
||||||
var subscriberWasCalled = false;
|
|
||||||
var completionWasCalled = false;
|
|
||||||
|
|
||||||
var progress = new Progress<float>();
|
|
||||||
progress.OnProgressChanged(1.0f).Subscribe(_ => subscriberWasCalled = true, () => completionWasCalled = true);
|
|
||||||
|
|
||||||
((IProgress<float>)progress).Report(0.5f);
|
|
||||||
((IProgress<float>)progress).Report(1.0f);
|
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
Assert.That(subscriberWasCalled);
|
|
||||||
Assert.That(completionWasCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void OnProgressChanged_ShouldCallSubscribers_OnProgressChanged()
|
|
||||||
{
|
|
||||||
var subscriberWasCalled = false;
|
|
||||||
|
|
||||||
var progress = new Progress<float>();
|
|
||||||
progress.OnProgressChanged().Subscribe(_ => subscriberWasCalled = true);
|
|
||||||
|
|
||||||
((IProgress<float>)progress).Report(0.5f);
|
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
Assert.That(subscriberWasCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void OnProgressChanged_ShouldCallSubscribers_OnProgressChanged_GivenCompletionValue()
|
|
||||||
{
|
|
||||||
var subscriberWasCalled = false;
|
|
||||||
|
|
||||||
var progress = new Progress<float>();
|
|
||||||
progress.OnProgressChanged(1.0f).Subscribe(_ => subscriberWasCalled = true);
|
|
||||||
|
|
||||||
((IProgress<float>)progress).Report(0.5f);
|
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
Assert.That(subscriberWasCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void OnProgressChanged_ShouldThrowArgumentNullException_GivenNullProgress()
|
|
||||||
{
|
|
||||||
Progress<float> progress = null!;
|
|
||||||
Assert.Throws<ArgumentNullException>(() => progress.OnProgressChanged());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void OnProgressChanged_ShouldThrowArgumentNullException_GivenNullProgressAndCompletionValue()
|
|
||||||
{
|
|
||||||
Progress<float> progress = null!;
|
|
||||||
Assert.Throws<ArgumentNullException>(() => progress.OnProgressChanged(1.0f));
|
|
||||||
}
|
|
||||||
}
|
|
89
X10D/src/Collections/BinaryIntegerExtensions.cs
Normal file
89
X10D/src/Collections/BinaryIntegerExtensions.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
|
namespace X10D.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection-related extension methods for <see cref="IBinaryInteger{T}" />.
|
||||||
|
/// </summary>
|
||||||
|
public static class BinaryIntegerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks this integer into a boolean list, treating it as a bit field.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to unpack.</param>
|
||||||
|
/// <returns>An array of <see cref="bool" /> with a length equal to the size of <typeparamref name="TInteger" />.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static bool[] Unpack<TInteger>(this TInteger value)
|
||||||
|
where TInteger : unmanaged, IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var buffer = new bool[sizeof(TInteger) * 8];
|
||||||
|
value.Unpack(buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks this integer into a boolean list, treating it as a bit field.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to unpack.</param>
|
||||||
|
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</param>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static void Unpack<TInteger>(this TInteger value, Span<bool> destination)
|
||||||
|
where TInteger : unmanaged, IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
if (destination.Length < sizeof(TInteger) * 8)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case byte valueByte when Sse3.IsSupported:
|
||||||
|
valueByte.UnpackInternal_Ssse3(destination);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case int valueInt32 when Avx2.IsSupported:
|
||||||
|
valueInt32.UnpackInternal_Ssse3(destination);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case int valueInt32 when Sse3.IsSupported:
|
||||||
|
valueInt32.UnpackInternal_Ssse3(destination);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case short valueInt16 when Sse3.IsSupported:
|
||||||
|
valueInt16.UnpackInternal_Ssse3(destination);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnpackInternal_Fallback(value, destination);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
internal static void UnpackInternal_Fallback<TInteger>(this TInteger value, Span<bool> destination)
|
||||||
|
where TInteger : unmanaged, IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
int bitCount = sizeof(TInteger) * 8;
|
||||||
|
for (var index = 0; index < bitCount; index++)
|
||||||
|
{
|
||||||
|
destination[index] = (value & (TInteger.One << index)) != TInteger.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,5 +1,7 @@
|
|||||||
|
#if !NET7_0_OR_GREATER
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
#endif
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
@ -14,6 +16,7 @@ public static class ByteExtensions
|
|||||||
{
|
{
|
||||||
private const int Size = sizeof(byte) * 8;
|
private const int Size = sizeof(byte) * 8;
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks this 8-bit unsigned integer into a boolean list, treating it as a bit field.
|
/// Unpacks this 8-bit unsigned integer into a boolean list, treating it as a bit field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -51,6 +54,7 @@ public static class ByteExtensions
|
|||||||
|
|
||||||
UnpackInternal_Fallback(value, destination);
|
UnpackInternal_Fallback(value, destination);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
internal static void UnpackInternal_Fallback(this byte value, Span<bool> destination)
|
internal static void UnpackInternal_Fallback(this byte value, Span<bool> destination)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
#if !NET7_0_OR_GREATER
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
#endif
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
@ -14,6 +16,7 @@ public static class Int16Extensions
|
|||||||
{
|
{
|
||||||
private const int Size = sizeof(short) * 8;
|
private const int Size = sizeof(short) * 8;
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks this 16-bit signed integer into a boolean list, treating it as a bit field.
|
/// Unpacks this 16-bit signed integer into a boolean list, treating it as a bit field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -51,6 +54,7 @@ public static class Int16Extensions
|
|||||||
|
|
||||||
UnpackInternal_Fallback(value, destination);
|
UnpackInternal_Fallback(value, destination);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
internal static void UnpackInternal_Fallback(this short value, Span<bool> destination)
|
internal static void UnpackInternal_Fallback(this short value, Span<bool> destination)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
#if !NET7_0_OR_GREATER
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
#endif
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
@ -14,6 +16,7 @@ public static class Int32Extensions
|
|||||||
{
|
{
|
||||||
private const int Size = sizeof(int) * 8;
|
private const int Size = sizeof(int) * 8;
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks this 32-bit signed integer into a boolean list, treating it as a bit field.
|
/// Unpacks this 32-bit signed integer into a boolean list, treating it as a bit field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -57,6 +60,7 @@ public static class Int32Extensions
|
|||||||
|
|
||||||
UnpackInternal_Fallback(value, destination);
|
UnpackInternal_Fallback(value, destination);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
internal static void UnpackInternal_Fallback(this int value, Span<bool> destination)
|
internal static void UnpackInternal_Fallback(this int value, Span<bool> destination)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#if !NET7_0_OR_GREATER
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.Collections;
|
namespace X10D.Collections;
|
||||||
@ -41,3 +42,4 @@ public static class Int64Extensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.IO;
|
namespace X10D.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.IO;
|
namespace X10D.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.IO;
|
namespace X10D.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
#if !NET5_0_OR_GREATER
|
#if !NET5_0_OR_GREATER
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.IO;
|
namespace X10D.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.IO;
|
namespace X10D.IO;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
namespace X10D.IO;
|
namespace X10D.IO;
|
||||||
|
@ -10,6 +10,7 @@ namespace X10D.Math;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BigIntegerExtensions
|
public static class BigIntegerExtensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current integer.
|
/// Returns the number of digits in the current integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -42,6 +43,7 @@ public static class BigIntegerExtensions
|
|||||||
BigInteger root = BigInteger.Abs(value).Mod(9);
|
BigInteger root = BigInteger.Abs(value).Mod(9);
|
||||||
return (int)(root == 0 ? 9 : root);
|
return (int)(root == 0 ? 9 : root);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the factorial of the current 64-bit signed integer.
|
/// Returns the factorial of the current 64-bit signed integer.
|
||||||
@ -170,6 +172,7 @@ public static class BigIntegerExtensions
|
|||||||
return value * other / value.GreatestCommonFactor(other);
|
return value * other / value.GreatestCommonFactor(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a modulo operation which supports a negative dividend.
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -191,6 +194,7 @@ public static class BigIntegerExtensions
|
|||||||
BigInteger r = dividend % divisor;
|
BigInteger r = dividend % divisor;
|
||||||
return r < 0 ? r + divisor : r;
|
return r < 0 ? r + divisor : r;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the multiplicative persistence of a specified value.
|
/// Returns the multiplicative persistence of a specified value.
|
||||||
|
101
X10D/src/Math/BinaryIntegerExtensions.cs
Normal file
101
X10D/src/Math/BinaryIntegerExtensions.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
|
namespace X10D.Math;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Math-related extension methods for <see cref="IBinaryInteger{TSelf}" />.
|
||||||
|
/// </summary>
|
||||||
|
public static class BinaryIntegerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the number of digits in the current binary integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose digit count to compute.</param>
|
||||||
|
/// <returns>The number of digits in <paramref name="value" />.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static int CountDigits<TInteger>(this TInteger value)
|
||||||
|
where TInteger : IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
if (TInteger.IsZero(value))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 + (int)System.Math.Floor(System.Math.Log10(System.Math.Abs(double.CreateChecked(value))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the digital root of this integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose digital root to compute.</param>
|
||||||
|
/// <returns>The digital root of <paramref name="value" />.</returns>
|
||||||
|
/// <remarks>The digital root is defined as the recursive sum of digits until that result is a single digit.</remarks>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>The digital root is defined as the recursive sum of digits until that result is a single digit.</para>
|
||||||
|
/// <para>For example, the digital root of 239 is 5: <c>2 + 3 + 9 = 14</c>, then <c>1 + 4 = 5</c>.</para>
|
||||||
|
/// </remarks>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static int DigitalRoot<TInteger>(this TInteger value)
|
||||||
|
where TInteger : IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
var nine = TInteger.CreateChecked(9);
|
||||||
|
TInteger root = TInteger.Abs(value).Mod(nine);
|
||||||
|
return int.CreateChecked(root == TInteger.Zero ? nine : root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the factorial of the current binary integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose factorial to compute.</param>
|
||||||
|
/// <returns>The factorial of <paramref name="value" />.</returns>
|
||||||
|
/// <exception cref="ArithmeticException"><paramref name="value" /> is less than 0.</exception>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static long Factorial<TInteger>(this TInteger value)
|
||||||
|
where TInteger : IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
if (value < TInteger.Zero)
|
||||||
|
{
|
||||||
|
throw new ArithmeticException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == TInteger.Zero)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = 1L;
|
||||||
|
for (TInteger i = TInteger.One; i <= value; i++)
|
||||||
|
{
|
||||||
|
result *= long.CreateChecked(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the greatest common factor between the current binary integer, and another binary integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The first value.</param>
|
||||||
|
/// <param name="other">The second value.</param>
|
||||||
|
/// <returns>The greatest common factor between <paramref name="value" /> and <paramref name="other" />.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static TInteger GreatestCommonFactor<TInteger>(this TInteger value, TInteger other)
|
||||||
|
where TInteger : IBinaryInteger<TInteger>
|
||||||
|
{
|
||||||
|
while (other != TInteger.Zero)
|
||||||
|
{
|
||||||
|
(value, other) = (other, value % other);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -9,6 +9,7 @@ namespace X10D.Math;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ByteExtensions
|
public static class ByteExtensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 8-bit unsigned integer.
|
/// Returns the number of digits in the current 8-bit unsigned integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -107,6 +108,7 @@ public static class ByteExtensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
|
@ -9,6 +9,7 @@ namespace X10D.Math;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Int16Extensions
|
public static class Int16Extensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 16-bit signed integer.
|
/// Returns the number of digits in the current 16-bit signed integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -112,6 +113,7 @@ public static class Int16Extensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
@ -140,6 +142,7 @@ public static class Int16Extensions
|
|||||||
return (short)((long)value).LowestCommonMultiple(other);
|
return (short)((long)value).LowestCommonMultiple(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a modulo operation which supports a negative dividend.
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -161,6 +164,7 @@ public static class Int16Extensions
|
|||||||
int r = dividend % divisor;
|
int r = dividend % divisor;
|
||||||
return (short)(r < 0 ? r + divisor : r);
|
return (short)(r < 0 ? r + divisor : r);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the multiplicative persistence of a specified value.
|
/// Returns the multiplicative persistence of a specified value.
|
||||||
|
@ -9,6 +9,7 @@ namespace X10D.Math;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Int32Extensions
|
public static class Int32Extensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 32-bit signed integer.
|
/// Returns the number of digits in the current 32-bit signed integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -112,6 +113,7 @@ public static class Int32Extensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
@ -140,6 +142,7 @@ public static class Int32Extensions
|
|||||||
return (int)((long)value).LowestCommonMultiple(other);
|
return (int)((long)value).LowestCommonMultiple(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a modulo operation which supports a negative dividend.
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -161,6 +164,7 @@ public static class Int32Extensions
|
|||||||
int r = dividend % divisor;
|
int r = dividend % divisor;
|
||||||
return r < 0 ? r + divisor : r;
|
return r < 0 ? r + divisor : r;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the multiplicative persistence of a specified value.
|
/// Returns the multiplicative persistence of a specified value.
|
||||||
|
@ -9,6 +9,7 @@ namespace X10D.Math;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Int64Extensions
|
public static class Int64Extensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 64-bit signed integer.
|
/// Returns the number of digits in the current 64-bit signed integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -117,6 +118,7 @@ public static class Int64Extensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
@ -179,6 +181,7 @@ public static class Int64Extensions
|
|||||||
return value * other / value.GreatestCommonFactor(other);
|
return value * other / value.GreatestCommonFactor(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a modulo operation which supports a negative dividend.
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -200,6 +203,7 @@ public static class Int64Extensions
|
|||||||
long r = dividend % divisor;
|
long r = dividend % divisor;
|
||||||
return r < 0 ? r + divisor : r;
|
return r < 0 ? r + divisor : r;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the multiplicative persistence of a specified value.
|
/// Returns the multiplicative persistence of a specified value.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using X10D.CompilerServices;
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
@ -12,34 +13,6 @@ public static class MathUtility
|
|||||||
private const double DefaultGamma = 2.2;
|
private const double DefaultGamma = 2.2;
|
||||||
private const float DefaultGammaF = 2.2f;
|
private const float DefaultGammaF = 2.2f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a simple bias function to value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to which the bias function will be applied.</param>
|
|
||||||
/// <param name="bias">The bias value. Valid values range from 0-1.</param>
|
|
||||||
/// <returns>The biased result.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// If <paramref name="bias" /> is less than 0.5, <paramref name="value" /> will be shifted downward; otherwise, upward.
|
|
||||||
/// </remarks>
|
|
||||||
public static float Bias(float value, float bias)
|
|
||||||
{
|
|
||||||
return value / ((1.0f / bias - 2.0f) * (1.0f - value) + 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a simple bias function to value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to which the bias function will be applied.</param>
|
|
||||||
/// <param name="bias">The bias value. Valid values range from 0-1.</param>
|
|
||||||
/// <returns>The biased result.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// If <paramref name="bias" /> is less than 0.5, <paramref name="value" /> will be shifted downward; otherwise, upward.
|
|
||||||
/// </remarks>
|
|
||||||
public static double Bias(double value, double bias)
|
|
||||||
{
|
|
||||||
return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates exponential decay for a value.
|
/// Calculates exponential decay for a value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -154,6 +127,35 @@ public static class MathUtility
|
|||||||
return (alpha - start) / (end - start);
|
return (alpha - start) / (end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a simple bias function to value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to which the bias function will be applied.</param>
|
||||||
|
/// <param name="bias">The bias value. Valid values range from 0-1.</param>
|
||||||
|
/// <returns>The biased result.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// If <paramref name="bias" /> is less than 0.5, <paramref name="value" /> will be shifted downward; otherwise, upward.
|
||||||
|
/// </remarks>
|
||||||
|
public static float Bias(float value, float bias)
|
||||||
|
{
|
||||||
|
return value / ((1.0f / bias - 2.0f) * (1.0f - value) + 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a simple bias function to value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to which the bias function will be applied.</param>
|
||||||
|
/// <param name="bias">The bias value. Valid values range from 0-1.</param>
|
||||||
|
/// <returns>The biased result.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// If <paramref name="bias" /> is less than 0.5, <paramref name="value" /> will be shifted downward; otherwise, upward.
|
||||||
|
/// </remarks>
|
||||||
|
public static double Bias(double value, double bias)
|
||||||
|
{
|
||||||
|
return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Linearly interpolates from one value to a target using a specified alpha.
|
/// Linearly interpolates from one value to a target using a specified alpha.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -190,6 +192,93 @@ public static class MathUtility
|
|||||||
return ((1.0 - alpha) * value) + (alpha * target);
|
return ((1.0 - alpha) * value) + (alpha * target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The interpolation source.</param>
|
||||||
|
/// <param name="target">The interpolation target.</param>
|
||||||
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
|
/// <returns>The interpolation result.</returns>
|
||||||
|
public static float SmoothStep(float value, float target, float alpha)
|
||||||
|
{
|
||||||
|
alpha = System.Math.Clamp(alpha, 0.0f, 1.0f);
|
||||||
|
alpha = -2.0f * alpha * alpha * alpha + 3.0f * alpha * alpha;
|
||||||
|
return target * alpha + value * (1.0f - alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The interpolation source.</param>
|
||||||
|
/// <param name="target">The interpolation target.</param>
|
||||||
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
|
/// <returns>The interpolation result.</returns>
|
||||||
|
public static double SmoothStep(double value, double target, double alpha)
|
||||||
|
{
|
||||||
|
alpha = System.Math.Clamp(alpha, 0.0, 1.0);
|
||||||
|
alpha = -2.0 * alpha * alpha * alpha + 3.0 * alpha * alpha;
|
||||||
|
return target * alpha + value * (1.0 - alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="oldMin">The old minimum value.</param>
|
||||||
|
/// <param name="oldMax">The old maximum value.</param>
|
||||||
|
/// <param name="newMin">The new minimum value.</param>
|
||||||
|
/// <param name="newMax">The new maximum value.</param>
|
||||||
|
/// <returns>The scaled value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static float ScaleRange(float value, float oldMin, float oldMax, float newMin, float newMax)
|
||||||
|
{
|
||||||
|
float oldRange = oldMax - oldMin;
|
||||||
|
float newRange = newMax - newMin;
|
||||||
|
float alpha = (value - oldMin) / oldRange;
|
||||||
|
return (alpha * newRange) + newMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="oldMin">The old minimum value.</param>
|
||||||
|
/// <param name="oldMax">The old maximum value.</param>
|
||||||
|
/// <param name="newMin">The new minimum value.</param>
|
||||||
|
/// <param name="newMax">The new maximum value.</param>
|
||||||
|
/// <returns>The scaled value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static double ScaleRange(double value, double oldMin, double oldMax, double newMin, double newMax)
|
||||||
|
{
|
||||||
|
double oldRange = oldMax - oldMin;
|
||||||
|
double newRange = newMax - newMin;
|
||||||
|
double alpha = (value - oldMin) / oldRange;
|
||||||
|
return (alpha * newRange) + newMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the incremental sawtooth wave of a given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to calculate.</param>
|
||||||
|
/// <returns>The sawtooth wave of the given value.</returns>
|
||||||
|
public static float Sawtooth(float value)
|
||||||
|
{
|
||||||
|
return (value - MathF.Floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the incremental sawtooth wave of a given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to calculate.</param>
|
||||||
|
/// <returns>The sawtooth wave of the given value.</returns>
|
||||||
|
public static double Sawtooth(double value)
|
||||||
|
{
|
||||||
|
return (value - System.Math.Floor(value));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a linear value to a gamma-encoded value using a gamma value of <c>2.2</c>.
|
/// Converts a linear value to a gamma-encoded value using a gamma value of <c>2.2</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -272,64 +361,6 @@ public static class MathUtility
|
|||||||
return Unsafe.As<bool, int>(ref result);
|
return Unsafe.As<bool, int>(ref result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the incremental sawtooth wave of a given value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to calculate.</param>
|
|
||||||
/// <returns>The sawtooth wave of the given value.</returns>
|
|
||||||
public static float Sawtooth(float value)
|
|
||||||
{
|
|
||||||
return (value - MathF.Floor(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the incremental sawtooth wave of a given value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to calculate.</param>
|
|
||||||
/// <returns>The sawtooth wave of the given value.</returns>
|
|
||||||
public static double Sawtooth(double value)
|
|
||||||
{
|
|
||||||
return (value - System.Math.Floor(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to convert.</param>
|
|
||||||
/// <param name="oldMin">The old minimum value.</param>
|
|
||||||
/// <param name="oldMax">The old maximum value.</param>
|
|
||||||
/// <param name="newMin">The new minimum value.</param>
|
|
||||||
/// <param name="newMax">The new maximum value.</param>
|
|
||||||
/// <returns>The scaled value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
|
||||||
public static float ScaleRange(float value, float oldMin, float oldMax, float newMin, float newMax)
|
|
||||||
{
|
|
||||||
float oldRange = oldMax - oldMin;
|
|
||||||
float newRange = newMax - newMin;
|
|
||||||
float alpha = (value - oldMin) / oldRange;
|
|
||||||
return (alpha * newRange) + newMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to convert.</param>
|
|
||||||
/// <param name="oldMin">The old minimum value.</param>
|
|
||||||
/// <param name="oldMax">The old maximum value.</param>
|
|
||||||
/// <param name="newMin">The new minimum value.</param>
|
|
||||||
/// <param name="newMax">The new maximum value.</param>
|
|
||||||
/// <returns>The scaled value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
|
||||||
public static double ScaleRange(double value, double oldMin, double oldMax, double newMin, double newMax)
|
|
||||||
{
|
|
||||||
double oldRange = oldMax - oldMin;
|
|
||||||
double newRange = newMax - newMin;
|
|
||||||
double alpha = (value - oldMin) / oldRange;
|
|
||||||
return (alpha * newRange) + newMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the sigmoid function for the given input value.
|
/// Calculates the sigmoid function for the given input value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -358,18 +389,71 @@ public static class MathUtility
|
|||||||
return 1.0f / (1.0f + System.Math.Exp(-value));
|
return 1.0f / (1.0f + System.Math.Exp(-value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
|
/// Applies a simple bias function to value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to which the bias function will be applied.</param>
|
||||||
|
/// <param name="bias">The bias value. Valid values range from 0-1.</param>
|
||||||
|
/// <returns>The biased result.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// If <paramref name="bias" /> is less than 0.5, <paramref name="value" /> will be shifted downward; otherwise, upward.
|
||||||
|
/// </remarks>
|
||||||
|
public static TNumber Bias<TNumber>(TNumber value, TNumber bias)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
TNumber identity = TNumber.MultiplicativeIdentity;
|
||||||
|
return value / ((identity / bias - TNumber.CreateChecked(2)) * (identity - value) + identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Linearly interpolates from one value to a target using a specified alpha.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The interpolation source.</param>
|
/// <param name="value">The interpolation source.</param>
|
||||||
/// <param name="target">The interpolation target.</param>
|
/// <param name="target">The interpolation target.</param>
|
||||||
/// <param name="alpha">The interpolation alpha.</param>
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
/// <returns>The interpolation result.</returns>
|
/// <returns>
|
||||||
public static float SmoothStep(float value, float target, float alpha)
|
/// The interpolation result as determined by <c>(1 - alpha) * value + alpha * target</c>.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static TNumber Lerp<TNumber>(TNumber value, TNumber target, TNumber alpha)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
{
|
{
|
||||||
alpha = System.Math.Clamp(alpha, 0.0f, 1.0f);
|
// rookie mistake: a + t * (b - a)
|
||||||
alpha = -2.0f * alpha * alpha * alpha + 3.0f * alpha * alpha;
|
// "precise" method: (1 - t) * a + t * b
|
||||||
return target * alpha + value * (1.0f - alpha);
|
return ((TNumber.MultiplicativeIdentity - alpha) * value) + (alpha * target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the incremental sawtooth wave of a given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to calculate.</param>
|
||||||
|
/// <returns>The sawtooth wave of the given value.</returns>
|
||||||
|
public static TNumber Sawtooth<TNumber>(TNumber value)
|
||||||
|
where TNumber : IFloatingPoint<TNumber>
|
||||||
|
{
|
||||||
|
return (value - TNumber.Floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="oldMin">The old minimum value.</param>
|
||||||
|
/// <param name="oldMax">The old maximum value.</param>
|
||||||
|
/// <param name="newMin">The new minimum value.</param>
|
||||||
|
/// <param name="newMax">The new maximum value.</param>
|
||||||
|
/// <returns>The scaled value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static TNumber ScaleRange<TNumber>(TNumber value, TNumber oldMin, TNumber oldMax, TNumber newMin, TNumber newMax)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
TNumber oldRange = oldMax - oldMin;
|
||||||
|
TNumber newRange = newMax - newMin;
|
||||||
|
TNumber alpha = (value - oldMin) / oldRange;
|
||||||
|
return (alpha * newRange) + newMin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -379,10 +463,16 @@ public static class MathUtility
|
|||||||
/// <param name="target">The interpolation target.</param>
|
/// <param name="target">The interpolation target.</param>
|
||||||
/// <param name="alpha">The interpolation alpha.</param>
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
/// <returns>The interpolation result.</returns>
|
/// <returns>The interpolation result.</returns>
|
||||||
public static double SmoothStep(double value, double target, double alpha)
|
public static TNumber SmoothStep<TNumber>(TNumber value, TNumber target, TNumber alpha)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
{
|
{
|
||||||
alpha = System.Math.Clamp(alpha, 0.0, 1.0);
|
TNumber one = TNumber.One;
|
||||||
alpha = -2.0 * alpha * alpha * alpha + 3.0 * alpha * alpha;
|
TNumber two = one + one;
|
||||||
return target * alpha + value * (1.0 - alpha);
|
TNumber three = two + one;
|
||||||
|
|
||||||
|
alpha = TNumber.Clamp(alpha, TNumber.Zero, TNumber.One);
|
||||||
|
alpha = -two * alpha * alpha * alpha + three * alpha * alpha;
|
||||||
|
return target * alpha + value * (one - alpha);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
104
X10D/src/Math/NumberExtensions.cs
Normal file
104
X10D/src/Math/NumberExtensions.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
|
namespace X10D.Math;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Math-related extension methods for <see cref="INumber{TSelf}" />.
|
||||||
|
/// </summary>
|
||||||
|
public static class NumberExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is evenly divisible by 2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose parity to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is evenly divisible by 2, or <see langword="false" />
|
||||||
|
/// otherwise.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static bool IsEven<TNumber>(this TNumber value)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
return value % TNumber.CreateChecked(2) == TNumber.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose parity to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is not evenly divisible by 2, or <see langword="false" />
|
||||||
|
/// otherwise.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static bool IsOdd<TNumber>(this TNumber value)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
return !value.IsEven();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dividend">The dividend.</param>
|
||||||
|
/// <param name="divisor">The divisor.</param>
|
||||||
|
/// <returns>The result of <c>dividend mod divisor</c>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The <c>%</c> operator (commonly called the modulo operator) in C# is not defined to be modulo, but is instead
|
||||||
|
/// remainder. This quirk inherently makes it difficult to use modulo in a negative context, as <c>x % y</c> where x is
|
||||||
|
/// negative will return a negative value, akin to <c>-(x % y)</c>, even if precedence is forced. This method provides a
|
||||||
|
/// modulo operation which supports negative dividends.
|
||||||
|
/// </remarks>
|
||||||
|
/// <author>ShreevatsaR, https://stackoverflow.com/a/1082938/1467293</author>
|
||||||
|
/// <license>CC-BY-SA 2.5</license>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static TNumber Mod<TNumber>(this TNumber dividend, TNumber divisor)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
TNumber r = dividend % divisor;
|
||||||
|
return r < TNumber.Zero ? r + divisor : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an integer that indicates the sign of this number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A signed number.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A number that indicates the sign of <paramref name="value" />, as shown in the following table.
|
||||||
|
///
|
||||||
|
/// <list type="table">
|
||||||
|
/// <listheader>
|
||||||
|
/// <term>Return value</term>
|
||||||
|
/// <description>Meaning</description>
|
||||||
|
/// </listheader>
|
||||||
|
///
|
||||||
|
/// <item>
|
||||||
|
/// <term>-1</term>
|
||||||
|
/// <description><paramref name="value" /> is less than zero.</description>
|
||||||
|
/// </item>
|
||||||
|
/// <item>
|
||||||
|
/// <term>0</term>
|
||||||
|
/// <description><paramref name="value" /> is equal to zero.</description>
|
||||||
|
/// </item>
|
||||||
|
/// <item>
|
||||||
|
/// <term>1</term>
|
||||||
|
/// <description><paramref name="value" /> is greater than zero.</description>
|
||||||
|
/// </item>
|
||||||
|
/// </list>
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static int Sign<TNumber>(this TNumber value)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
return TNumber.Sign(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -10,6 +10,7 @@ namespace X10D.Math;
|
|||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public static class SByteExtensions
|
public static class SByteExtensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 8-bit signed integer.
|
/// Returns the number of digits in the current 8-bit signed integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -113,6 +114,7 @@ public static class SByteExtensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
@ -141,6 +143,7 @@ public static class SByteExtensions
|
|||||||
return (sbyte)((long)value).LowestCommonMultiple(other);
|
return (sbyte)((long)value).LowestCommonMultiple(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a modulo operation which supports a negative dividend.
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -162,6 +165,7 @@ public static class SByteExtensions
|
|||||||
int r = dividend % divisor;
|
int r = dividend % divisor;
|
||||||
return (sbyte)(r < 0 ? r + divisor : r);
|
return (sbyte)(r < 0 ? r + divisor : r);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the multiplicative persistence of a specified value.
|
/// Returns the multiplicative persistence of a specified value.
|
||||||
|
@ -10,6 +10,7 @@ namespace X10D.Math;
|
|||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public static class UInt16Extensions
|
public static class UInt16Extensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 16-bit signed integer.
|
/// Returns the number of digits in the current 16-bit signed integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -94,20 +95,6 @@ public static class UInt16Extensions
|
|||||||
return (value & 1) == 0;
|
return (value & 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value whose primality to check.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see langword="true" /> if <paramref name="value" /> is prime; otherwise, <see langword="false" />.
|
|
||||||
/// </returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
|
||||||
public static bool IsPrime(this ushort value)
|
|
||||||
{
|
|
||||||
return ((ulong)value).IsPrime();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -122,6 +109,21 @@ public static class UInt16Extensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose primality to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is prime; otherwise, <see langword="false" />.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static bool IsPrime(this ushort value)
|
||||||
|
{
|
||||||
|
return ((ulong)value).IsPrime();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the lowest common multiple between the current 16-bit unsigned integer, and another 16-bit unsigned
|
/// Calculates the lowest common multiple between the current 16-bit unsigned integer, and another 16-bit unsigned
|
||||||
|
@ -10,6 +10,7 @@ namespace X10D.Math;
|
|||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public static class UInt32Extensions
|
public static class UInt32Extensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 32-bit unsigned integer.
|
/// Returns the number of digits in the current 32-bit unsigned integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -94,20 +95,6 @@ public static class UInt32Extensions
|
|||||||
return (value & 1) == 0;
|
return (value & 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value whose primality to check.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see langword="true" /> if <paramref name="value" /> is prime; otherwise, <see langword="false" />.
|
|
||||||
/// </returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
|
||||||
public static bool IsPrime(this uint value)
|
|
||||||
{
|
|
||||||
return ((ulong)value).IsPrime();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -122,6 +109,21 @@ public static class UInt32Extensions
|
|||||||
{
|
{
|
||||||
return !value.IsEven();
|
return !value.IsEven();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose primality to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is prime; otherwise, <see langword="false" />.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static bool IsPrime(this uint value)
|
||||||
|
{
|
||||||
|
return ((ulong)value).IsPrime();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the lowest common multiple between the current 32-bit unsigned integer, and another 32-bit unsigned
|
/// Calculates the lowest common multiple between the current 32-bit unsigned integer, and another 32-bit unsigned
|
||||||
|
@ -10,6 +10,7 @@ namespace X10D.Math;
|
|||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public static class UInt64Extensions
|
public static class UInt64Extensions
|
||||||
{
|
{
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of digits in the current 64-bit unsigned integer.
|
/// Returns the number of digits in the current 64-bit unsigned integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -99,6 +100,22 @@ public static class UInt64Extensions
|
|||||||
return (value & 1) == 0;
|
return (value & 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose parity to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is not evenly divisible by 2, or <see langword="false" />
|
||||||
|
/// otherwise.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MaxOptimization)]
|
||||||
|
public static bool IsOdd(this ulong value)
|
||||||
|
{
|
||||||
|
return !value.IsEven();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether the current value is a prime number.
|
/// Returns a value indicating whether the current value is a prime number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -132,21 +149,6 @@ public static class UInt64Extensions
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value whose parity to check.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see langword="true" /> if <paramref name="value" /> is not evenly divisible by 2, or <see langword="false" />
|
|
||||||
/// otherwise.
|
|
||||||
/// </returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MaxOptimization)]
|
|
||||||
public static bool IsOdd(this ulong value)
|
|
||||||
{
|
|
||||||
return !value.IsEven();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the lowest common multiple between the current 64-bit unsigned integer, and another 64-bit unsigned
|
/// Calculates the lowest common multiple between the current 64-bit unsigned integer, and another 64-bit unsigned
|
||||||
/// integer.
|
/// integer.
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
namespace X10D.Reactive;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a disposable that removes an observer from a collection of observers.
|
|
||||||
/// </summary>
|
|
||||||
internal readonly struct ObservableDisposer<T> : IDisposable
|
|
||||||
{
|
|
||||||
private readonly HashSet<IObserver<T>> _observers;
|
|
||||||
private readonly IObserver<T> _observer;
|
|
||||||
private readonly Action? _additionalAction;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ObservableDisposer{T}" /> struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="observers">A collection of observers from which to remove the specified observer.</param>
|
|
||||||
/// <param name="observer">The observer to remove from the collection.</param>
|
|
||||||
/// <param name="additionalAction">The additional action to run on dispose.</param>
|
|
||||||
public ObservableDisposer(HashSet<IObserver<T>> observers, IObserver<T> observer, Action? additionalAction)
|
|
||||||
{
|
|
||||||
_observers = observers ?? throw new ArgumentNullException(nameof(observers));
|
|
||||||
_observer = observer ?? throw new ArgumentNullException(nameof(observer));
|
|
||||||
_additionalAction = additionalAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the observer from the collection of observers.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_observers.Remove(_observer);
|
|
||||||
_additionalAction?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
namespace X10D.Reactive;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for <see cref="Progress{T}" />.
|
|
||||||
/// </summary>
|
|
||||||
public static class ProgressExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Wraps the <see cref="Progress{T}.ProgressChanged" /> event of the current <see cref="Progress{T}" /> in an
|
|
||||||
/// <see cref="IObservable{T}" /> object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="progress">The progress whose <see cref="Progress{T}.ProgressChanged" /> event to wrap.</param>
|
|
||||||
/// <typeparam name="T">The type of progress update value.</typeparam>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="IObservable{T}" /> object that wraps the <see cref="Progress{T}.ProgressChanged" /> event of the current
|
|
||||||
/// <see cref="Progress{T}" />.
|
|
||||||
/// </returns>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="progress" /> is <see langword="null" />.</exception>
|
|
||||||
public static IObservable<T> OnProgressChanged<T>(this Progress<T> progress)
|
|
||||||
{
|
|
||||||
if (progress is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
var progressObservable = new ProgressObservable<T>();
|
|
||||||
|
|
||||||
void ProgressChangedHandler(object? sender, T args)
|
|
||||||
{
|
|
||||||
IObserver<T>[] observers = progressObservable.Observers;
|
|
||||||
|
|
||||||
for (var index = 0; index < observers.Length; index++)
|
|
||||||
{
|
|
||||||
observers[index].OnNext(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.ProgressChanged += ProgressChangedHandler;
|
|
||||||
progressObservable.OnDispose = () => progress.ProgressChanged -= ProgressChangedHandler;
|
|
||||||
|
|
||||||
return progressObservable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Wraps the <see cref="Progress{T}.ProgressChanged" /> event of the current <see cref="Progress{T}" /> in an
|
|
||||||
/// <see cref="IObservable{T}" /> object, and completes the observable when the progress reaches the specified value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="progress">The progress whose <see cref="Progress{T}.ProgressChanged" /> event to wrap.</param>
|
|
||||||
/// <param name="completeValue">The value that indicates completion.</param>
|
|
||||||
/// <typeparam name="T">The type of progress update value.</typeparam>
|
|
||||||
/// <returns>
|
|
||||||
/// An <see cref="IObservable{T}" /> object that wraps the <see cref="Progress{T}.ProgressChanged" /> event of the current
|
|
||||||
/// <see cref="Progress{T}" />.
|
|
||||||
/// </returns>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="progress" /> is <see langword="null" />.</exception>
|
|
||||||
public static IObservable<T> OnProgressChanged<T>(this Progress<T> progress, T completeValue)
|
|
||||||
{
|
|
||||||
if (progress is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
var progressObservable = new ProgressObservable<T>();
|
|
||||||
var comparer = EqualityComparer<T>.Default;
|
|
||||||
|
|
||||||
void ProgressChangedHandler(object? sender, T args)
|
|
||||||
{
|
|
||||||
IObserver<T>[] observers = progressObservable.Observers;
|
|
||||||
|
|
||||||
for (var index = 0; index < observers.Length; index++)
|
|
||||||
{
|
|
||||||
observers[index].OnNext(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comparer.Equals(args, completeValue))
|
|
||||||
{
|
|
||||||
for (var index = 0; index < observers.Length; index++)
|
|
||||||
{
|
|
||||||
observers[index].OnCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.ProgressChanged += ProgressChangedHandler;
|
|
||||||
progressObservable.OnDispose = () => progress.ProgressChanged -= ProgressChangedHandler;
|
|
||||||
|
|
||||||
return progressObservable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
namespace X10D.Reactive;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a concrete implementation of <see cref="IObservable{T}" /> that tracks progress of a <see cref="Progress{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class ProgressObservable<T> : IObservable<T>
|
|
||||||
{
|
|
||||||
private readonly HashSet<IObserver<T>> _observers = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the observers.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The observers.</value>
|
|
||||||
public IObserver<T>[] Observers
|
|
||||||
{
|
|
||||||
get => _observers.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Action? OnDispose { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Subscribes the specified observer to the progress tracker.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="observer">The observer.</param>
|
|
||||||
/// <returns>An object which can be disposed to unsubscribe from progress tracking.</returns>
|
|
||||||
public IDisposable Subscribe(IObserver<T> observer)
|
|
||||||
{
|
|
||||||
if (observer is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(observer));
|
|
||||||
}
|
|
||||||
|
|
||||||
_observers.Add(observer);
|
|
||||||
return new ObservableDisposer<T>(_observers, observer, OnDispose);
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,7 +32,7 @@ public static class Int16Extensions
|
|||||||
value++;
|
value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Mod(4) == 0 && (value.Mod(100) != 0 || value.Mod(400) == 0);
|
return value.Mod((short)4) == 0 && (value.Mod((short)100) != 0 || value.Mod((short)400) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -33,7 +33,7 @@ public static class SByteExtensions
|
|||||||
value++;
|
value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Mod(4) == 0 && value.Mod(100) != 0; // mod 400 not required, sbyte.MaxValue is 127 anyway
|
return value.Mod((sbyte)4) == 0 && value.Mod((sbyte)100) != 0; // mod 400 not required, sbyte.MaxValue is 127 anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
2
codecov.yml
Normal file
2
codecov.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ignore:
|
||||||
|
- "X10D/src/ExceptionMessages.Designer.cs"
|
Loading…
Reference in New Issue
Block a user