mirror of https://github.com/SixLabors/ImageSharp
28 changed files with 781 additions and 317 deletions
@ -0,0 +1,417 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides optimized static methods for trigonometric, logarithmic,
|
||||
|
/// and other common mathematical functions.
|
||||
|
/// </summary>
|
||||
|
internal static class Numerics |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
|
||||
|
/// </summary>
|
||||
|
public static int GreatestCommonDivisor(int a, int b) |
||||
|
{ |
||||
|
while (b != 0) |
||||
|
{ |
||||
|
int temp = b; |
||||
|
b = a % b; |
||||
|
a = temp; |
||||
|
} |
||||
|
|
||||
|
return a; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determine the Least Common Multiple (LCM) of two numbers.
|
||||
|
/// </summary>
|
||||
|
public static int LeastCommonMultiple(int a, int b) |
||||
|
{ |
||||
|
// https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor
|
||||
|
return (a / GreatestCommonDivisor(a, b)) * b; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates <paramref name="x"/> % 2
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static int Modulo2(int x) => x & 1; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates <paramref name="x"/> % 4
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static int Modulo4(int x) => x & 3; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates <paramref name="x"/> % 8
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static int Modulo8(int x) => x & 7; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fast (x mod m) calculator, with the restriction that
|
||||
|
/// <paramref name="m"/> should be power of 2.
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static int ModuloP2(int x, int m) => x & (m - 1); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the absolute value of a 32-bit signed integer.
|
||||
|
/// Uses bit shifting to speed up the operation compared to <see cref="Math"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">
|
||||
|
/// A number that is greater than <see cref="int.MinValue"/>, but less than
|
||||
|
/// or equal to <see cref="int.MaxValue"/>
|
||||
|
/// </param>
|
||||
|
/// <returns>The <see cref="int"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static int Abs(int x) |
||||
|
{ |
||||
|
int y = x >> 31; |
||||
|
return (x ^ y) - y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns a specified number raised to the power of 2
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">A single-precision floating-point number</param>
|
||||
|
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float Pow2(float x) => x * x; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns a specified number raised to the power of 3
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">A single-precision floating-point number</param>
|
||||
|
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float Pow3(float x) => x * x * x; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implementation of 1D Gaussian G(x) function
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The x provided to G(x).</param>
|
||||
|
/// <param name="sigma">The spread of the blur.</param>
|
||||
|
/// <returns>The Gaussian G(x)</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float Gaussian(float x, float sigma) |
||||
|
{ |
||||
|
const float Numerator = 1.0f; |
||||
|
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma; |
||||
|
|
||||
|
float exponentNumerator = -x * x; |
||||
|
float exponentDenominator = 2 * Pow2(sigma); |
||||
|
|
||||
|
float left = Numerator / denominator; |
||||
|
float right = MathF.Exp(exponentNumerator / exponentDenominator); |
||||
|
|
||||
|
return left * right; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the result of a normalized sine cardinal function for the given value.
|
||||
|
/// SinC(x) = sin(pi*x)/(pi*x).
|
||||
|
/// </summary>
|
||||
|
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
|
||||
|
/// <returns>
|
||||
|
/// The sine cardinal of <paramref name="f" />.
|
||||
|
/// </returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float SinC(float f) |
||||
|
{ |
||||
|
if (MathF.Abs(f) > Constants.Epsilon) |
||||
|
{ |
||||
|
f *= MathF.PI; |
||||
|
float result = MathF.Sin(f) / f; |
||||
|
return MathF.Abs(result) < Constants.Epsilon ? 0F : result; |
||||
|
} |
||||
|
|
||||
|
return 1F; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the value clamped to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
/// <returns>The clamped <see cref="byte"/>.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static byte Clamp(byte value, byte min, byte max) |
||||
|
{ |
||||
|
// Order is important here as someone might set min to higher than max.
|
||||
|
if (value > max) |
||||
|
{ |
||||
|
return max; |
||||
|
} |
||||
|
|
||||
|
if (value < min) |
||||
|
{ |
||||
|
return min; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the value clamped to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
/// <returns>The clamped <see cref="uint"/>.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static uint Clamp(uint value, uint min, uint max) |
||||
|
{ |
||||
|
if (value > max) |
||||
|
{ |
||||
|
return max; |
||||
|
} |
||||
|
|
||||
|
if (value < min) |
||||
|
{ |
||||
|
return min; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the value clamped to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
/// <returns>The clamped <see cref="uint"/>.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static int Clamp(int value, int min, int max) |
||||
|
{ |
||||
|
if (value > max) |
||||
|
{ |
||||
|
return max; |
||||
|
} |
||||
|
|
||||
|
if (value < min) |
||||
|
{ |
||||
|
return min; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the value clamped to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
/// <returns>The clamped <see cref="float"/>.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float Clamp(float value, float min, float max) |
||||
|
{ |
||||
|
if (value > max) |
||||
|
{ |
||||
|
return max; |
||||
|
} |
||||
|
|
||||
|
if (value < min) |
||||
|
{ |
||||
|
return min; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the value clamped to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
/// <returns>The clamped <see cref="double"/>.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static double Clamp(double value, double min, double max) |
||||
|
{ |
||||
|
if (value > max) |
||||
|
{ |
||||
|
return max; |
||||
|
} |
||||
|
|
||||
|
if (value < min) |
||||
|
{ |
||||
|
return min; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps the span values to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="span">The span containing the values to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Clamp(Span<byte> span, byte min, byte max) |
||||
|
{ |
||||
|
int reduced = ClampReduce(span, min, max); |
||||
|
|
||||
|
if (reduced > 0) |
||||
|
{ |
||||
|
for (int i = reduced; i < span.Length; i++) |
||||
|
{ |
||||
|
ref byte v = ref span[i]; |
||||
|
v = Clamp(v, min, max); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps the span values to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="span">The span containing the values to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Clamp(Span<uint> span, uint min, uint max) |
||||
|
{ |
||||
|
int reduced = ClampReduce(span, min, max); |
||||
|
|
||||
|
if (reduced > 0) |
||||
|
{ |
||||
|
for (int i = reduced; i < span.Length; i++) |
||||
|
{ |
||||
|
ref uint v = ref span[i]; |
||||
|
v = Clamp(v, min, max); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps the span values to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="span">The span containing the values to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Clamp(Span<int> span, int min, int max) |
||||
|
{ |
||||
|
int reduced = ClampReduce(span, min, max); |
||||
|
|
||||
|
if (reduced > 0) |
||||
|
{ |
||||
|
for (int i = reduced; i < span.Length; i++) |
||||
|
{ |
||||
|
ref int v = ref span[i]; |
||||
|
v = Clamp(v, min, max); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps the span values to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="span">The span containing the values to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Clamp(Span<float> span, float min, float max) |
||||
|
{ |
||||
|
int reduced = ClampReduce(span, min, max); |
||||
|
|
||||
|
if (reduced > 0) |
||||
|
{ |
||||
|
for (int i = reduced; i < span.Length; i++) |
||||
|
{ |
||||
|
ref float v = ref span[i]; |
||||
|
v = Clamp(v, min, max); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps the span values to the inclusive range of min and max.
|
||||
|
/// </summary>
|
||||
|
/// <param name="span">The span containing the values to clamp.</param>
|
||||
|
/// <param name="min">The minimum inclusive value.</param>
|
||||
|
/// <param name="max">The maximum inclusive value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Clamp(Span<double> span, double min, double max) |
||||
|
{ |
||||
|
int reduced = ClampReduce(span, min, max); |
||||
|
|
||||
|
if (reduced > 0) |
||||
|
{ |
||||
|
for (int i = reduced; i < span.Length; i++) |
||||
|
{ |
||||
|
ref double v = ref span[i]; |
||||
|
v = Clamp(v, min, max); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static int ClampReduce<T>(Span<T> span, T min, T max) |
||||
|
where T : unmanaged |
||||
|
{ |
||||
|
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count) |
||||
|
{ |
||||
|
int remainder = ModuloP2(span.Length, Vector<T>.Count); |
||||
|
int adjustedCount = span.Length - remainder; |
||||
|
|
||||
|
if (adjustedCount > 0) |
||||
|
{ |
||||
|
ClampImpl(span.Slice(0, adjustedCount), min, max); |
||||
|
} |
||||
|
|
||||
|
return adjustedCount; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void ClampImpl<T>(Span<T> span, T min, T max) |
||||
|
where T : unmanaged |
||||
|
{ |
||||
|
ref T sRef = ref MemoryMarshal.GetReference(span); |
||||
|
ref Vector<T> vsBase = ref Unsafe.As<T, Vector<T>>(ref MemoryMarshal.GetReference(span)); |
||||
|
var vmin = new Vector<T>(min); |
||||
|
var vmax = new Vector<T>(max); |
||||
|
|
||||
|
int n = span.Length / Vector<T>.Count; |
||||
|
int m = Modulo4(n); |
||||
|
int u = n - m; |
||||
|
|
||||
|
for (int i = 0; i < u; i += 4) |
||||
|
{ |
||||
|
ref Vector<T> vs0 = ref Unsafe.Add(ref vsBase, i); |
||||
|
ref Vector<T> vs1 = ref Unsafe.Add(ref vs0, 1); |
||||
|
ref Vector<T> vs2 = ref Unsafe.Add(ref vs0, 2); |
||||
|
ref Vector<T> vs3 = ref Unsafe.Add(ref vs0, 3); |
||||
|
|
||||
|
vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax); |
||||
|
vs1 = Vector.Min(Vector.Max(vmin, vs1), vmax); |
||||
|
vs2 = Vector.Min(Vector.Max(vmin, vs2), vmax); |
||||
|
vs3 = Vector.Min(Vector.Max(vmin, vs3), vmax); |
||||
|
} |
||||
|
|
||||
|
if (m > 0) |
||||
|
{ |
||||
|
for (int i = u; i < n; i++) |
||||
|
{ |
||||
|
ref Vector<T> vs0 = ref Unsafe.Add(ref vsBase, i); |
||||
|
vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath |
||||
|
{ |
||||
|
public class ClampSpan |
||||
|
{ |
||||
|
private static readonly int[] A = new int[2048]; |
||||
|
private static readonly int[] B = new int[2048]; |
||||
|
|
||||
|
public void Setup() |
||||
|
{ |
||||
|
var r = new Random(); |
||||
|
|
||||
|
for (int i = 0; i < A.Length; i++) |
||||
|
{ |
||||
|
int x = r.Next(); |
||||
|
A[i] = x; |
||||
|
B[i] = x; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void ClampNoIntrinsics() |
||||
|
{ |
||||
|
for (int i = 0; i < A.Length; i++) |
||||
|
{ |
||||
|
ref int x = ref A[i]; |
||||
|
x = x.Clamp(64, 128); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void ClampVectorIntrinsics() |
||||
|
{ |
||||
|
Numerics.Clamp(B, 64, 128); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,265 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Helpers |
||||
|
{ |
||||
|
public class NumericsTests |
||||
|
{ |
||||
|
public delegate void SpanAction<T, in TArg, in TArg1>(Span<T> span, TArg arg, TArg1 arg1); |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
[InlineData(3)] |
||||
|
[InlineData(4)] |
||||
|
[InlineData(100)] |
||||
|
[InlineData(123)] |
||||
|
[InlineData(53436353)] |
||||
|
public void Modulo2(int x) |
||||
|
{ |
||||
|
int actual = Numerics.Modulo2(x); |
||||
|
Assert.Equal(x % 2, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
[InlineData(3)] |
||||
|
[InlineData(4)] |
||||
|
[InlineData(100)] |
||||
|
[InlineData(123)] |
||||
|
[InlineData(53436353)] |
||||
|
public void Modulo4(int x) |
||||
|
{ |
||||
|
int actual = Numerics.Modulo4(x); |
||||
|
Assert.Equal(x % 4, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
[InlineData(6)] |
||||
|
[InlineData(7)] |
||||
|
[InlineData(8)] |
||||
|
[InlineData(100)] |
||||
|
[InlineData(123)] |
||||
|
[InlineData(53436353)] |
||||
|
[InlineData(975)] |
||||
|
public void Modulo8(int x) |
||||
|
{ |
||||
|
int actual = Numerics.Modulo8(x); |
||||
|
Assert.Equal(x % 8, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0, 2)] |
||||
|
[InlineData(1, 2)] |
||||
|
[InlineData(2, 2)] |
||||
|
[InlineData(0, 4)] |
||||
|
[InlineData(3, 4)] |
||||
|
[InlineData(5, 4)] |
||||
|
[InlineData(5, 8)] |
||||
|
[InlineData(8, 8)] |
||||
|
[InlineData(8, 16)] |
||||
|
[InlineData(15, 16)] |
||||
|
[InlineData(17, 16)] |
||||
|
[InlineData(17, 32)] |
||||
|
[InlineData(31, 32)] |
||||
|
[InlineData(32, 32)] |
||||
|
[InlineData(33, 32)] |
||||
|
public void Modulo2P(int x, int m) |
||||
|
{ |
||||
|
int actual = Numerics.ModuloP2(x, m); |
||||
|
Assert.Equal(x % m, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(-5)] |
||||
|
[InlineData(-17)] |
||||
|
[InlineData(-12856)] |
||||
|
[InlineData(-32)] |
||||
|
[InlineData(-7425)] |
||||
|
[InlineData(5)] |
||||
|
[InlineData(17)] |
||||
|
[InlineData(12856)] |
||||
|
[InlineData(32)] |
||||
|
[InlineData(7425)] |
||||
|
public void Abs(int x) |
||||
|
{ |
||||
|
int expected = Math.Abs(x); |
||||
|
Assert.Equal(expected, Numerics.Abs(x)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(-5)] |
||||
|
[InlineData(-17)] |
||||
|
[InlineData(-12856)] |
||||
|
[InlineData(-32)] |
||||
|
[InlineData(-7425)] |
||||
|
[InlineData(5)] |
||||
|
[InlineData(17)] |
||||
|
[InlineData(12856)] |
||||
|
[InlineData(32)] |
||||
|
[InlineData(7425)] |
||||
|
public void Pow2(float x) |
||||
|
{ |
||||
|
float expected = (float)Math.Pow(x, 2); |
||||
|
Assert.Equal(expected, Numerics.Pow2(x)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(-5)] |
||||
|
[InlineData(-17)] |
||||
|
[InlineData(-12856)] |
||||
|
[InlineData(-32)] |
||||
|
[InlineData(5)] |
||||
|
[InlineData(17)] |
||||
|
[InlineData(12856)] |
||||
|
[InlineData(32)] |
||||
|
public void Pow3(float x) |
||||
|
{ |
||||
|
float expected = (float)Math.Pow(x, 3); |
||||
|
Assert.Equal(expected, Numerics.Pow3(x)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(1, 1, 1)] |
||||
|
[InlineData(1, 42, 1)] |
||||
|
[InlineData(10, 8, 2)] |
||||
|
[InlineData(12, 18, 6)] |
||||
|
[InlineData(4536, 1000, 8)] |
||||
|
[InlineData(1600, 1024, 64)] |
||||
|
public void GreatestCommonDivisor(int a, int b, int expected) |
||||
|
{ |
||||
|
int actual = Numerics.GreatestCommonDivisor(a, b); |
||||
|
Assert.Equal(expected, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(1, 1, 1)] |
||||
|
[InlineData(1, 42, 42)] |
||||
|
[InlineData(3, 4, 12)] |
||||
|
[InlineData(6, 4, 12)] |
||||
|
[InlineData(1600, 1024, 25600)] |
||||
|
[InlineData(3264, 100, 81600)] |
||||
|
public void LeastCommonMultiple(int a, int b, int expected) |
||||
|
{ |
||||
|
int actual = Numerics.LeastCommonMultiple(a, b); |
||||
|
Assert.Equal(expected, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(64, 36, 96)] |
||||
|
[InlineData(128, 16, 196)] |
||||
|
[InlineData(567, 18, 142)] |
||||
|
[InlineData(1024, 0, 255)] |
||||
|
public void ClampByte(int length, byte min, byte max) |
||||
|
{ |
||||
|
TestClampSpan( |
||||
|
length, |
||||
|
min, |
||||
|
max, |
||||
|
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
||||
|
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(64, 36, 96)] |
||||
|
[InlineData(128, 16, 196)] |
||||
|
[InlineData(567, 18, 142)] |
||||
|
[InlineData(1024, 0, 255)] |
||||
|
public void ClampInt(int length, int min, int max) |
||||
|
{ |
||||
|
TestClampSpan( |
||||
|
length, |
||||
|
min, |
||||
|
max, |
||||
|
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
||||
|
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(64, 36, 96)] |
||||
|
[InlineData(128, 16, 196)] |
||||
|
[InlineData(567, 18, 142)] |
||||
|
[InlineData(1024, 0, 255)] |
||||
|
public void ClampUInt(int length, uint min, uint max) |
||||
|
{ |
||||
|
TestClampSpan( |
||||
|
length, |
||||
|
min, |
||||
|
max, |
||||
|
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
||||
|
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(64, 36, 96)] |
||||
|
[InlineData(128, 16, 196)] |
||||
|
[InlineData(567, 18, 142)] |
||||
|
[InlineData(1024, 0, 255)] |
||||
|
public void ClampFloat(int length, float min, float max) |
||||
|
{ |
||||
|
TestClampSpan( |
||||
|
length, |
||||
|
min, |
||||
|
max, |
||||
|
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
||||
|
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(64, 36, 96)] |
||||
|
[InlineData(128, 16, 196)] |
||||
|
[InlineData(567, 18, 142)] |
||||
|
[InlineData(1024, 0, 255)] |
||||
|
public void ClampDouble(int length, double min, double max) |
||||
|
{ |
||||
|
TestClampSpan( |
||||
|
length, |
||||
|
min, |
||||
|
max, |
||||
|
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
||||
|
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
||||
|
} |
||||
|
|
||||
|
private static void TestClampSpan<T>( |
||||
|
int length, |
||||
|
T min, |
||||
|
T max, |
||||
|
SpanAction<T, T, T> clampAction, |
||||
|
Func<T, T, T, T> refClampFunc) |
||||
|
where T : unmanaged, IComparable<T> |
||||
|
{ |
||||
|
Span<T> actual = new T[length]; |
||||
|
|
||||
|
var r = new Random(); |
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
actual[i] = (T)Convert.ChangeType(r.Next(byte.MinValue, byte.MaxValue), typeof(T)); |
||||
|
} |
||||
|
|
||||
|
Span<T> expected = new T[length]; |
||||
|
actual.CopyTo(expected); |
||||
|
|
||||
|
for (int i = 0; i < expected.Length; i++) |
||||
|
{ |
||||
|
ref T v = ref expected[i]; |
||||
|
v = refClampFunc(v, min, max); |
||||
|
} |
||||
|
|
||||
|
clampAction(actual, min, max); |
||||
|
|
||||
|
for (int i = 0; i < expected.Length; i++) |
||||
|
{ |
||||
|
Assert.Equal(expected[i], actual[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue