Compare commits
	
		
			No commits in common. "main" and "v1.0.0" have entirely different histories.
		
	
	
		
	
		
| @ -1,6 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="UserContentModel"> | ||||
|   <component name="ContentModelUserStore"> | ||||
|     <attachedFolders /> | ||||
|     <explicitIncludes /> | ||||
|     <explicitExcludes /> | ||||
|  | ||||
							
								
								
									
										8
									
								
								.idea/.idea.FastRng/.idea/modules.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.idea.FastRng/.idea/modules.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/.idea.FastRng/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.FastRng/riderModule.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										12
									
								
								.idea/.idea.FastRng/riderModule.iml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.idea/.idea.FastRng/riderModule.iml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="RIDER_MODULE" version="4"> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$USER_HOME$/.nuget/packages/microsoft.net.test.sdk/16.5.0/build/netcoreapp2.1" /> | ||||
|     <content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.16.1/build/netcoreapp2.1/NUnit3.TestAdapter.dll" /> | ||||
|     <content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.16.1/build/netcoreapp2.1/NUnit3.TestAdapter.pdb" /> | ||||
|     <content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.16.1/build/netcoreapp2.1/nunit.engine.api.dll" /> | ||||
|     <content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.16.1/build/netcoreapp2.1/nunit.engine.dll" /> | ||||
|     <content url="file://$MODULE_DIR$/../.." /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
| @ -1,17 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class BetaA2B2<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum ALPHA = TNum.One + TNum.One; | ||||
|     private static readonly TNum BETA = TNum.One + TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(4); | ||||
| 
 | ||||
|     public BetaA2B2(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * TNum.Pow(x, ALPHA - TNum.One) * TNum.Pow(TNum.One - x, BETA - TNum.One); | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class BetaA2B5<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum ALPHA = TNum.One + TNum.One; | ||||
|     private static readonly TNum BETA = TNum.CreateChecked(5); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(12.2f); | ||||
| 
 | ||||
|     public BetaA2B5(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * TNum.Pow(x, ALPHA - TNum.One) * TNum.Pow(TNum.One - x, BETA - TNum.One); | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class BetaA5B2<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum ALPHA = TNum.CreateChecked(5f); | ||||
|     private static readonly TNum BETA = TNum.One + TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(12.2f); | ||||
| 
 | ||||
|     public BetaA5B2(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * TNum.Pow(x, ALPHA - TNum.One) * TNum.Pow(TNum.One - x, BETA - TNum.One); | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class CauchyLorentzX0<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.31f); | ||||
|     private static readonly TNum SCALE = TNum.CreateChecked(0.1f); | ||||
|     private static readonly TNum MEDIAN = TNum.Zero; | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
| 
 | ||||
|     public CauchyLorentzX0(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * ( TNum.One / (TNum.Pi * SCALE)) * ((SCALE * SCALE) / (TNum.Pow(x - MEDIAN, TWO) + (SCALE * SCALE))); | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class CauchyLorentzX1<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.31f); | ||||
|     private static readonly TNum SCALE = TNum.CreateChecked(0.1f); | ||||
|     private static readonly TNum MEDIAN =  TNum.One; | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
| 
 | ||||
|     public CauchyLorentzX1(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * (TNum.One / (TNum.Pi * SCALE)) * ((SCALE * SCALE) / (TNum.Pow(x - MEDIAN, TWO) + (SCALE * SCALE))); | ||||
| } | ||||
| @ -1,29 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class ChiSquareK1<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum HALF = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
|     private static readonly TNum K =  TNum.One; | ||||
|     private static readonly TNum K_HALF = K * HALF; | ||||
|     private static readonly TNum K_HALF_MINUS_ONE = K_HALF -  TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.252f); | ||||
| 
 | ||||
|     private static readonly TNum DIVISOR; | ||||
|          | ||||
|     static ChiSquareK1() | ||||
|     { | ||||
|         var twoToTheKHalf = TNum.Pow(TWO, K_HALF); | ||||
|         var gammaKHalf = MathToolsFloatingPoint<TNum>.Gamma(K_HALF); | ||||
|         DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|     } | ||||
|          | ||||
|     public ChiSquareK1(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * ((TNum.Pow(x, K_HALF_MINUS_ONE) * TNum.Exp(-x * HALF)) / DIVISOR); | ||||
| } | ||||
| @ -1,29 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class ChiSquareK10<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum HALF = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
|     private static readonly TNum K = TNum.CreateChecked(10.0f); | ||||
|     private static readonly TNum K_HALF = K * HALF; | ||||
|     private static readonly TNum K_HALF_MINUS_ONE = K_HALF - TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.252f); | ||||
| 
 | ||||
|     private static readonly TNum DIVISOR; | ||||
|          | ||||
|     static ChiSquareK10() | ||||
|     { | ||||
|         var twoToTheKHalf = TNum.Pow(TWO, K_HALF); | ||||
|         var gammaKHalf = MathToolsFloatingPoint<TNum>.Gamma(K_HALF); | ||||
|         DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|     } | ||||
|          | ||||
|     public ChiSquareK10(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * ((TNum.Pow(x, K_HALF_MINUS_ONE) * TNum.Exp(-x * HALF)) / DIVISOR); | ||||
| } | ||||
| @ -1,29 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class ChiSquareK4<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum HALF = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
|     private static readonly TNum K = TNum.CreateChecked(4f); | ||||
|     private static readonly TNum K_HALF = K * HALF; | ||||
|     private static readonly TNum K_HALF_MINUS_ONE = K_HALF -  TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.252f); | ||||
| 
 | ||||
|     private static readonly TNum DIVISOR; | ||||
|          | ||||
|     static ChiSquareK4() | ||||
|     { | ||||
|         var twoToTheKHalf = TNum.Pow(TWO, K_HALF); | ||||
|         var gammaKHalf = MathToolsFloatingPoint<TNum>.Gamma(K_HALF); | ||||
|         DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|     } | ||||
| 
 | ||||
|     public ChiSquareK4(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * ((TNum.Pow(x, K_HALF_MINUS_ONE) * TNum.Exp(-x * HALF)) / DIVISOR); | ||||
| } | ||||
| @ -1,63 +0,0 @@ | ||||
| using System; | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public abstract class Distribution<TNum> : IDistribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private readonly ShapeFitter<TNum> fitter; | ||||
| 
 | ||||
|     protected Distribution(IRandom<TNum> rng) | ||||
|     { | ||||
|         if (rng == null) | ||||
|             throw new ArgumentNullException(nameof(rng), "An IRandom<TNum> implementation is needed."); | ||||
|          | ||||
|         this.fitter = new ShapeFitter<TNum>(this.ShapeFunction, rng, 100); | ||||
|     } | ||||
| 
 | ||||
|     public abstract TNum ShapeFunction(TNum x); | ||||
|          | ||||
|     public TNum GetDistributedValue(CancellationToken token = default) => this.fitter.NextNumber(token); | ||||
|          | ||||
|     public uint NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default) | ||||
|     { | ||||
|         // Swap the values if the range start is greater than the range end: | ||||
|         if (rangeStart > rangeEnd) | ||||
|             (rangeStart, rangeEnd) = (rangeEnd, rangeStart); | ||||
| 
 | ||||
|         var range = rangeEnd - rangeStart; | ||||
|         var distributedValue = this.GetDistributedValue(cancel); | ||||
|         return (uint) ((float.CreateChecked(distributedValue) * range) + rangeStart); | ||||
|     } | ||||
| 
 | ||||
|     public ulong NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default) | ||||
|     { | ||||
|         // Swap the values if the range start is greater than the range end: | ||||
|         if (rangeStart > rangeEnd) | ||||
|             (rangeStart, rangeEnd) = (rangeEnd, rangeStart); | ||||
| 
 | ||||
|         var range = rangeEnd - rangeStart; | ||||
|         var distributedValue = this.GetDistributedValue(cancel); | ||||
|         return (ulong) ((double.CreateChecked(distributedValue) * range) + rangeStart); | ||||
|     } | ||||
| 
 | ||||
|     public TNum NextNumber(TNum rangeStart, TNum rangeEnd, CancellationToken cancel = default) | ||||
|     { | ||||
|         // Swap the values if the range start is greater than the range end: | ||||
|         if (rangeStart > rangeEnd) | ||||
|             (rangeStart, rangeEnd) = (rangeEnd, rangeStart); | ||||
| 
 | ||||
|         var range = rangeEnd - rangeStart; | ||||
|         var distributedValue = this.GetDistributedValue(cancel); | ||||
|         return (distributedValue * range) + rangeStart; | ||||
|     } | ||||
| 
 | ||||
|     public TNum NextNumber(CancellationToken cancel = default) => this.NextNumber(TNum.Zero, TNum.One, cancel); | ||||
|          | ||||
|     public bool HasDecisionBeenMade(TNum above, TNum below, CancellationToken cancel = default) | ||||
|     { | ||||
|         var number = this.NextNumber(cancel); | ||||
|         return number > above && number < below; | ||||
|     } | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class ExponentialLa10<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum LAMBDA = TNum.CreateChecked(10.0f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.1106f); | ||||
| 
 | ||||
|     public ExponentialLa10(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * LAMBDA * TNum.Exp(-LAMBDA * x); | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class ExponentialLa5<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum LAMBDA = TNum.CreateChecked(5.0f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.2103f); | ||||
| 
 | ||||
|     public ExponentialLa5(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * LAMBDA * TNum.Exp(-LAMBDA * x); | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class GammaA5B15<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum ALPHA = TNum.CreateChecked(5.0f); | ||||
|     private static readonly TNum BETA = TNum.CreateChecked(15.0f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.341344210715475f); | ||||
| 
 | ||||
|     private static readonly TNum GAMMA_ALPHA; | ||||
|     private static readonly TNum BETA_TO_THE_ALPHA; | ||||
|          | ||||
|     static GammaA5B15() | ||||
|     { | ||||
|         GAMMA_ALPHA = MathToolsFloatingPoint<TNum>.Gamma(ALPHA); | ||||
|         BETA_TO_THE_ALPHA = TNum.Pow(BETA, ALPHA); | ||||
|     } | ||||
| 
 | ||||
|     public GammaA5B15(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * ((BETA_TO_THE_ALPHA * TNum.Pow(x, ALPHA - TNum.One) * TNum.Exp(-BETA * x)) / GAMMA_ALPHA); | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public interface IDistribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     public TNum GetDistributedValue(CancellationToken token = default); | ||||
|          | ||||
|     public uint NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|     public ulong NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|     public TNum NextNumber(TNum rangeStart, TNum rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|     public TNum NextNumber(CancellationToken cancel = default); | ||||
|          | ||||
|     public bool HasDecisionBeenMade(TNum above, TNum below, CancellationToken cancel = default); | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class InverseExponentialLa10<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum LAMBDA = TNum.CreateChecked(10.0f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(4.539992976248453e-06f); | ||||
| 
 | ||||
|     public InverseExponentialLa10(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * LAMBDA * TNum.Exp(LAMBDA * x); | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class InverseExponentialLa5<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum LAMBDA = TNum.CreateChecked(5.0f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.001347589399817f); | ||||
| 
 | ||||
|     public InverseExponentialLa5(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * LAMBDA * TNum.Exp(LAMBDA * x); | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class InverseGammaA3B05<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum ALPHA = TNum.CreateChecked(3.0f); | ||||
|     private static readonly TNum BETA = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.213922656884911f); | ||||
| 
 | ||||
|     private static readonly TNum FACTOR_LEFT; | ||||
|          | ||||
|     static InverseGammaA3B05() | ||||
|     { | ||||
|         var gammaAlpha = MathToolsFloatingPoint<TNum>.Gamma(ALPHA); | ||||
|         var betaToTheAlpha = TNum.Pow(BETA, ALPHA); | ||||
|              | ||||
|         FACTOR_LEFT = CONSTANT * (betaToTheAlpha / gammaAlpha); | ||||
|     } | ||||
|          | ||||
|     public InverseGammaA3B05(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => FACTOR_LEFT * TNum.Pow(x, -ALPHA - TNum.One) * TNum.Exp(-BETA / x); | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class LaplaceB01M0<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2.0f); | ||||
|     private static readonly TNum B = TNum.CreateChecked(0.1f); | ||||
|     private static readonly TNum MU = TNum.Zero; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.221034183615129f); | ||||
|          | ||||
|     private static readonly TNum FACTOR_LEFT; | ||||
|          | ||||
|     static LaplaceB01M0() | ||||
|     { | ||||
|         FACTOR_LEFT = CONSTANT / (TWO * B); | ||||
|     } | ||||
|          | ||||
|     public LaplaceB01M0(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => FACTOR_LEFT * TNum.Exp(-TNum.Abs(x - MU) / B); | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class LaplaceB01M05<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2.0f); | ||||
|     private static readonly TNum B = TNum.CreateChecked(0.1f); | ||||
|     private static readonly TNum MU = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.2f); | ||||
|          | ||||
|     private static readonly TNum FACTOR_LEFT; | ||||
|          | ||||
|     static LaplaceB01M05() | ||||
|     { | ||||
|         FACTOR_LEFT = CONSTANT / (TWO * B); | ||||
|     } | ||||
|          | ||||
|     public LaplaceB01M05(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => FACTOR_LEFT * TNum.Exp(-TNum.Abs(x - MU) / B); | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class LogNormalS1M0<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
|     private static readonly TNum SIGMA = TNum.One; | ||||
|     private static readonly TNum MU = TNum.Zero; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(1.51998658387455f); | ||||
|          | ||||
|     private static readonly TNum FACTOR; | ||||
|          | ||||
|     static LogNormalS1M0() | ||||
|     { | ||||
|         FACTOR = SIGMA * TNum.Sqrt(TWO * TNum.Pi); | ||||
|     } | ||||
|          | ||||
|     public LogNormalS1M0(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => (CONSTANT / (x * FACTOR)) * TNum.Exp( -(TNum.Pow(TNum.Log(x) - MU, TWO) / (TWO * TNum.Pow(SIGMA, TWO)))); | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class NormalS02M05<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
|     private static readonly TNum NEGATIVE_HALF = TNum.CreateChecked(-0.5f); | ||||
|     private static readonly TNum SQRT_2_PI = TNum.CreateChecked(2.506628275f); | ||||
|     private static readonly TNum STD_DEV = TNum.CreateChecked(0.2f); | ||||
|     private static readonly TNum MEAN = TNum.CreateChecked(0.5f); | ||||
| 
 | ||||
|     public NormalS02M05(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) =>  TNum.One / (STD_DEV * SQRT_2_PI) * TNum.Exp(NEGATIVE_HALF * TNum.Pow((x - MEAN) / STD_DEV, TWO)); | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class StudentTNu1<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum HALF = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum TWO = TNum.CreateChecked(2f); | ||||
|     private static readonly TNum NU = TNum.One; | ||||
|     private static readonly TNum START = TNum.Zero; | ||||
|     private static readonly TNum COMPRESS = TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(3.14190548592729f); | ||||
|          | ||||
|     private static readonly TNum DIVIDEND; | ||||
|     private static readonly TNum DIVISOR; | ||||
|     private static readonly TNum EXPONENT; | ||||
|          | ||||
|     static StudentTNu1() | ||||
|     { | ||||
|         DIVIDEND = MathToolsFloatingPoint<TNum>.Gamma((NU + TNum.One) * HALF); | ||||
|         DIVISOR = TNum.Sqrt(NU * TNum.Pi) * MathToolsFloatingPoint<TNum>.Gamma(NU * HALF); | ||||
|         EXPONENT = -((NU +  TNum.One) * HALF); | ||||
|     } | ||||
|          | ||||
|     public StudentTNu1(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * TNum.Pow((DIVIDEND / DIVISOR) * TNum.Pow( TNum.One + TNum.Pow(START + x * COMPRESS, TWO) / NU, EXPONENT), COMPRESS); | ||||
| } | ||||
| @ -1,58 +0,0 @@ | ||||
| using System; | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class Uniform<TNum> : IDistribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private readonly IRandom<TNum> rng; | ||||
|          | ||||
|     public Uniform(IRandom<TNum> rng) | ||||
|     { | ||||
|         this.rng = rng ?? throw new ArgumentNullException(nameof(rng), "An IRandom<TNum> implementation is needed."); | ||||
|     } | ||||
| 
 | ||||
|     public TNum GetDistributedValue(CancellationToken token = default) => this.rng.GetUniform(token); | ||||
|          | ||||
|     public uint NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default) | ||||
|     { | ||||
|         // Swap the values if the range start is greater than the range end: | ||||
|         if (rangeStart > rangeEnd) | ||||
|             (rangeStart, rangeEnd) = (rangeEnd, rangeStart); | ||||
| 
 | ||||
|         var range = rangeEnd - rangeStart; | ||||
|         var distributedValue = this.GetDistributedValue(cancel); | ||||
|         return (uint) ((float.CreateChecked(distributedValue) * range) + rangeStart); | ||||
|     } | ||||
| 
 | ||||
|     public ulong NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default) | ||||
|     { | ||||
|         // Swap the values if the range start is greater than the range end: | ||||
|         if (rangeStart > rangeEnd) | ||||
|             (rangeStart, rangeEnd) = (rangeEnd, rangeStart); | ||||
| 
 | ||||
|         var range = rangeEnd - rangeStart; | ||||
|         var distributedValue = this.GetDistributedValue(cancel); | ||||
|         return (ulong) ((double.CreateChecked(distributedValue) * range) + rangeStart); | ||||
|     } | ||||
| 
 | ||||
|     public TNum NextNumber(TNum rangeStart, TNum rangeEnd, CancellationToken cancel = default) | ||||
|     { | ||||
|         // Swap the values if the range start is greater than the range end: | ||||
|         if (rangeStart > rangeEnd) | ||||
|             (rangeStart, rangeEnd) = (rangeEnd, rangeStart); | ||||
| 
 | ||||
|         var range = rangeEnd - rangeStart; | ||||
|         var distributedValue = this.GetDistributedValue(cancel); | ||||
|         return (distributedValue * range) + rangeStart; | ||||
|     } | ||||
| 
 | ||||
|     public TNum NextNumber(CancellationToken cancel = default) => this.NextNumber(TNum.Zero,  TNum.One, cancel); | ||||
|          | ||||
|     public bool HasDecisionBeenMade(TNum above, TNum below, CancellationToken cancel = default) | ||||
|     { | ||||
|         var number = this.NextNumber(cancel); | ||||
|         return number > above && number < below; | ||||
|     } | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng.Distributions; | ||||
| 
 | ||||
| public sealed class WeibullK05La1<TNum> : Distribution<TNum> where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     private static readonly TNum K = TNum.CreateChecked(0.5f); | ||||
|     private static readonly TNum LAMBDA = TNum.One; | ||||
|     private static readonly TNum CONSTANT = TNum.CreateChecked(0.221034183615129f); | ||||
| 
 | ||||
|     public WeibullK05La1(IRandom<TNum> rng) : base(rng) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override TNum ShapeFunction(TNum x) => CONSTANT * ( (K / LAMBDA) * TNum.Pow(x / LAMBDA, K -  TNum.One) * TNum.Exp(-TNum.Pow(x/LAMBDA, K))); | ||||
| } | ||||
							
								
								
									
										19
									
								
								FastRng/Double/Distributions/BetaA2B2.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FastRng/Double/Distributions/BetaA2B2.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class BetaA2B2 : Distribution | ||||
|     { | ||||
|         private const double ALPHA = 2; | ||||
|         private const double BETA = 2; | ||||
|         private const double CONSTANT = 4; | ||||
| 
 | ||||
|         public BetaA2B2(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * Math.Pow(x, ALPHA - 1) * Math.Pow(1 - x, BETA - 1); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								FastRng/Double/Distributions/BetaA2B5.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FastRng/Double/Distributions/BetaA2B5.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class BetaA2B5 : Distribution | ||||
|     { | ||||
|         private const double ALPHA = 2; | ||||
|         private const double BETA = 5; | ||||
|         private const double CONSTANT = 12.2; | ||||
| 
 | ||||
|         public BetaA2B5(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * Math.Pow(x, ALPHA - 1) * Math.Pow(1 - x, BETA - 1); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								FastRng/Double/Distributions/BetaA5B2.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FastRng/Double/Distributions/BetaA5B2.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class BetaA5B2 : Distribution | ||||
|     { | ||||
|         private const double ALPHA = 5; | ||||
|         private const double BETA = 2; | ||||
|         private const double CONSTANT = 12.2; | ||||
| 
 | ||||
|         public BetaA5B2(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * Math.Pow(x, ALPHA - 1) * Math.Pow(1 - x, BETA - 1); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								FastRng/Double/Distributions/CauchyLorentzX0.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FastRng/Double/Distributions/CauchyLorentzX0.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class CauchyLorentzX0 : Distribution | ||||
|     { | ||||
|         private const double CONSTANT = 0.31; | ||||
|         private const double SCALE = 0.1; | ||||
|         private const double MEDIAN = 0.0; | ||||
| 
 | ||||
|         public CauchyLorentzX0(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * (1.0 / (Math.PI * SCALE)) * ((SCALE * SCALE) / (Math.Pow(x - MEDIAN, 2) + (SCALE * SCALE))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								FastRng/Double/Distributions/CauchyLorentzX1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FastRng/Double/Distributions/CauchyLorentzX1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class CauchyLorentzX1 : Distribution | ||||
|     { | ||||
|         private const double CONSTANT = 0.31; | ||||
|         private const double SCALE = 0.1; | ||||
|         private const double MEDIAN = 1.0; | ||||
| 
 | ||||
|         public CauchyLorentzX1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * (1.0 / (Math.PI * SCALE)) * ((SCALE * SCALE) / (Math.Pow(x - MEDIAN, 2) + (SCALE * SCALE))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								FastRng/Double/Distributions/ChiSquareK1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								FastRng/Double/Distributions/ChiSquareK1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class ChiSquareK1 : Distribution | ||||
|     { | ||||
|         private const double K = 1.0; | ||||
|         private const double K_HALF = K * 0.5d; | ||||
|         private const double K_HALF_MINUS_ONE = K_HALF - 1.0d; | ||||
|         private const double CONSTANT = 0.252; | ||||
| 
 | ||||
|         private static readonly double DIVISOR; | ||||
|          | ||||
|         static ChiSquareK1() | ||||
|         { | ||||
|             var twoToTheKHalf = Math.Pow(2, K_HALF); | ||||
|             var gammaKHalf = MathTools.Gamma(K_HALF); | ||||
|             DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|         } | ||||
| 
 | ||||
|         public ChiSquareK1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * ((Math.Pow(x, K_HALF_MINUS_ONE) * Math.Exp(-x * 0.5d)) / DIVISOR); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								FastRng/Double/Distributions/ChiSquareK10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								FastRng/Double/Distributions/ChiSquareK10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class ChiSquareK10 : Distribution | ||||
|     { | ||||
|         private const double K = 10.0; | ||||
|         private const double K_HALF = K * 0.5d; | ||||
|         private const double K_HALF_MINUS_ONE = K_HALF - 1.0d; | ||||
|         private const double CONSTANT = 0.252; | ||||
| 
 | ||||
|         private static readonly double DIVISOR; | ||||
|          | ||||
|         static ChiSquareK10() | ||||
|         { | ||||
|             var twoToTheKHalf = Math.Pow(2, K_HALF); | ||||
|             var gammaKHalf = MathTools.Gamma(K_HALF); | ||||
|             DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|         } | ||||
|          | ||||
|         public ChiSquareK10(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * ((Math.Pow(x, K_HALF_MINUS_ONE) * Math.Exp(-x * 0.5d)) / DIVISOR); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								FastRng/Double/Distributions/ChiSquareK4.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								FastRng/Double/Distributions/ChiSquareK4.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class ChiSquareK4 : Distribution | ||||
|     { | ||||
|         private const double K = 4.0; | ||||
|         private const double K_HALF = K * 0.5d; | ||||
|         private const double K_HALF_MINUS_ONE = K_HALF - 1.0d; | ||||
|         private const double CONSTANT = 0.252; | ||||
| 
 | ||||
|         private static readonly double DIVISOR; | ||||
|          | ||||
|         static ChiSquareK4() | ||||
|         { | ||||
|             var twoToTheKHalf = Math.Pow(2, K_HALF); | ||||
|             var gammaKHalf = MathTools.Gamma(K_HALF); | ||||
|             DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|         } | ||||
|          | ||||
|         public ChiSquareK4(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * ((Math.Pow(x, K_HALF_MINUS_ONE) * Math.Exp(-x * 0.5d)) / DIVISOR); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										75
									
								
								FastRng/Double/Distributions/Distribution.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								FastRng/Double/Distributions/Distribution.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public abstract class Distribution : IDistribution | ||||
|     { | ||||
|         private readonly ShapeFitter fitter; | ||||
|         private readonly IRandom random; | ||||
| 
 | ||||
|         protected Distribution(IRandom rng) | ||||
|         { | ||||
|             if (rng == null) | ||||
|                 throw new ArgumentNullException(nameof(rng), "An IRandom implementation is needed."); | ||||
|                  | ||||
|             this.random = rng; | ||||
|             this.fitter = new ShapeFitter(this.ShapeFunction, this.random, 100); | ||||
|         } | ||||
| 
 | ||||
|         protected abstract double ShapeFunction(double x); | ||||
|          | ||||
|         public async ValueTask<double> GetDistributedValue(CancellationToken token = default) => await this.fitter.NextNumber(token); | ||||
|          | ||||
|         public async ValueTask<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (uint) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (ulong) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<double> NextNumber(double rangeStart, double rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (distributedValue * range) + rangeStart; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<double> NextNumber(CancellationToken cancel = default) => await this.NextNumber(0.0, 1.0, cancel); | ||||
| 
 | ||||
|         public async ValueTask<bool> HasDecisionBeenMade(double above, double below = 1, CancellationToken cancel = default) | ||||
|         { | ||||
|             var number = await this.NextNumber(cancel); | ||||
|             return number > above && number < below; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								FastRng/Double/Distributions/ExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								FastRng/Double/Distributions/ExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class ExponentialLa10 : Distribution | ||||
|     { | ||||
|         private const double LAMBDA = 10.0; | ||||
|         private const double CONSTANT = 0.1106; | ||||
| 
 | ||||
|         public ExponentialLa10(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * LAMBDA * Math.Exp(-LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								FastRng/Double/Distributions/ExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								FastRng/Double/Distributions/ExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class ExponentialLa5 : Distribution | ||||
|     { | ||||
|         private const double LAMBDA = 5.0; | ||||
|         private const double CONSTANT = 0.2103; | ||||
| 
 | ||||
|         public ExponentialLa5(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * LAMBDA * Math.Exp(-LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								FastRng/Double/Distributions/GammaA5B15.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								FastRng/Double/Distributions/GammaA5B15.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class GammaA5B15 : Distribution | ||||
|     { | ||||
|         private const double ALPHA = 5.0; | ||||
|         private const double BETA = 15.0; | ||||
|         private const double CONSTANT = 0.341344210715475; | ||||
| 
 | ||||
|         private static readonly double GAMMA_ALPHA; | ||||
|         private static readonly double BETA_TO_THE_ALPHA; | ||||
|          | ||||
|         static GammaA5B15() | ||||
|         { | ||||
|             GAMMA_ALPHA = MathTools.Gamma(ALPHA); | ||||
|             BETA_TO_THE_ALPHA = Math.Pow(BETA, ALPHA); | ||||
|         } | ||||
|          | ||||
|         public GammaA5B15(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * ((BETA_TO_THE_ALPHA * Math.Pow(x, ALPHA - 1.0d) * Math.Exp(-BETA * x)) / GAMMA_ALPHA); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								FastRng/Double/Distributions/IDistribution.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								FastRng/Double/Distributions/IDistribution.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public interface IDistribution | ||||
|     { | ||||
|         public ValueTask<double> GetDistributedValue(CancellationToken token); | ||||
|          | ||||
|         public ValueTask<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<double> NextNumber(double rangeStart, double rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<double> NextNumber(CancellationToken cancel = default); | ||||
| 
 | ||||
|         public ValueTask<bool> HasDecisionBeenMade(double above, double below = 1.0, CancellationToken cancel = default); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								FastRng/Double/Distributions/InverseExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								FastRng/Double/Distributions/InverseExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class InverseExponentialLa10 : Distribution | ||||
|     { | ||||
|         private const double LAMBDA = 10.0; | ||||
|         private const double CONSTANT = 4.539992976248453e-06; | ||||
| 
 | ||||
|         public InverseExponentialLa10(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * LAMBDA * Math.Exp(LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								FastRng/Double/Distributions/InverseExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								FastRng/Double/Distributions/InverseExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class InverseExponentialLa5 : Distribution | ||||
|     { | ||||
|         private const double LAMBDA = 5.0; | ||||
|         private const double CONSTANT = 0.001347589399817; | ||||
| 
 | ||||
|         public InverseExponentialLa5(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * LAMBDA * Math.Exp(LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								FastRng/Double/Distributions/InverseGammaA3B05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								FastRng/Double/Distributions/InverseGammaA3B05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class InverseGammaA3B05 : Distribution | ||||
|     { | ||||
|         private const double ALPHA = 3.0; | ||||
|         private const double BETA = 0.5; | ||||
|         private const double CONSTANT = 0.213922656884911; | ||||
| 
 | ||||
|         private static readonly double FACTOR_LEFT; | ||||
|          | ||||
|         static InverseGammaA3B05() | ||||
|         { | ||||
|             var gammaAlpha = MathTools.Gamma(ALPHA); | ||||
|             var betaToTheAlpha = Math.Pow(BETA, ALPHA); | ||||
|              | ||||
|             FACTOR_LEFT = CONSTANT * (betaToTheAlpha / gammaAlpha); | ||||
|         } | ||||
|          | ||||
|         public InverseGammaA3B05(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => FACTOR_LEFT * Math.Pow(x, -ALPHA - 1.0d) * Math.Exp(-BETA / x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								FastRng/Double/Distributions/LaplaceB01M0.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								FastRng/Double/Distributions/LaplaceB01M0.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class LaplaceB01M0 : Distribution | ||||
|     { | ||||
|         private const double B = 0.1; | ||||
|         private const double MU = 0.0; | ||||
|         private const double CONSTANT = 0.221034183615129; | ||||
|          | ||||
|         private static readonly double FACTOR_LEFT; | ||||
|          | ||||
|         static LaplaceB01M0() | ||||
|         { | ||||
|             FACTOR_LEFT = CONSTANT / (2.0d * B); | ||||
|         } | ||||
|          | ||||
|         public LaplaceB01M0(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => FACTOR_LEFT * Math.Exp(-Math.Abs(x - MU) / B); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								FastRng/Double/Distributions/LaplaceB01M05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								FastRng/Double/Distributions/LaplaceB01M05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class LaplaceB01M05 : Distribution | ||||
|     { | ||||
|         private const double B = 0.1; | ||||
|         private const double MU = 0.5; | ||||
|         private const double CONSTANT = 0.2; | ||||
|          | ||||
|         private static readonly double FACTOR_LEFT; | ||||
|          | ||||
|         static LaplaceB01M05() | ||||
|         { | ||||
|             FACTOR_LEFT = CONSTANT / (2.0d * B); | ||||
|         } | ||||
|          | ||||
|         public LaplaceB01M05(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => FACTOR_LEFT * Math.Exp(-Math.Abs(x - MU) / B); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								FastRng/Double/Distributions/LogNormalS1M0.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								FastRng/Double/Distributions/LogNormalS1M0.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class LogNormalS1M0 : Distribution | ||||
|     { | ||||
|         private const double SIGMA = 1.0; | ||||
|         private const double MU = 0.0; | ||||
|         private const double CONSTANT = 1.51998658387455; | ||||
|          | ||||
|         private static readonly double FACTOR; | ||||
|          | ||||
|         static LogNormalS1M0() | ||||
|         { | ||||
|             FACTOR = SIGMA * Math.Sqrt(2 * Math.PI); | ||||
|         } | ||||
|          | ||||
|         public LogNormalS1M0(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => (CONSTANT / (x * FACTOR)) * Math.Exp( -(Math.Pow(Math.Log(x) - MU, 2) / (2 * Math.Pow(SIGMA, 2)))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								FastRng/Double/Distributions/NormalS02M05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								FastRng/Double/Distributions/NormalS02M05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| using System; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class NormalS02M05 : Distribution | ||||
|     { | ||||
|         private const double SQRT_2_PI = 2.506628275; | ||||
|         private const double STDDEV = 0.2; | ||||
|         private const double MEAN = 0.5; | ||||
| 
 | ||||
|         public NormalS02M05(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => 1.0 / (STDDEV * SQRT_2_PI) * Math.Exp(-0.5 * Math.Pow((x - MEAN) / STDDEV, 2.0)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								FastRng/Double/Distributions/StudentTNu1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								FastRng/Double/Distributions/StudentTNu1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class StudentTNu1 : Distribution | ||||
|     { | ||||
|         private const double NU = 1.0; | ||||
|         private const double START = 0.0; | ||||
|         private const double COMPRESS = 1.0; | ||||
|         private const double CONSTANT = 3.14190548592729; | ||||
|          | ||||
|         private static readonly double DIVIDEND; | ||||
|         private static readonly double DIVISOR; | ||||
|         private static readonly double EXPONENT; | ||||
|          | ||||
|         static StudentTNu1() | ||||
|         { | ||||
|             DIVIDEND = MathTools.Gamma((NU + 1.0d) * 0.5d); | ||||
|             DIVISOR = Math.Sqrt(NU * Math.PI) * MathTools.Gamma(NU * 0.5d); | ||||
|             EXPONENT = -((NU + 1.0d) * 0.5d); | ||||
|         } | ||||
|          | ||||
|         public StudentTNu1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * Math.Pow((DIVIDEND / DIVISOR) * Math.Pow(1.0d + Math.Pow(START + x * COMPRESS, 2) / NU, EXPONENT), COMPRESS); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										71
									
								
								FastRng/Double/Distributions/Uniform.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								FastRng/Double/Distributions/Uniform.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class Uniform : IDistribution | ||||
|     { | ||||
|         private readonly IRandom rng; | ||||
|          | ||||
|         public Uniform(IRandom rng) | ||||
|         { | ||||
|             if (rng == null) | ||||
|                 throw new ArgumentNullException(nameof(rng), "An IRandom implementation is needed."); | ||||
|              | ||||
|             this.rng = rng; | ||||
|         } | ||||
|          | ||||
|         public async ValueTask<double> GetDistributedValue(CancellationToken token = default) => await this.rng.GetUniform(token); | ||||
|          | ||||
|         public async ValueTask<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (uint) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (ulong) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<double> NextNumber(double rangeStart, double rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (distributedValue * range) + rangeStart; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<double> NextNumber(CancellationToken cancel = default) => await this.NextNumber(0.0, 1.0, cancel); | ||||
|          | ||||
|         public async ValueTask<bool> HasDecisionBeenMade(double above, double below = 1, CancellationToken cancel = default) | ||||
|         { | ||||
|             var number = await this.NextNumber(cancel); | ||||
|             return number > above && number < below; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								FastRng/Double/Distributions/WeibullK05La1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FastRng/Double/Distributions/WeibullK05La1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Double.Distributions | ||||
| { | ||||
|     public sealed class WeibullK05La1 : Distribution | ||||
|     { | ||||
|         private const double K = 0.5; | ||||
|         private const double LAMBDA = 1.0; | ||||
|         private const double CONSTANT = 0.221034183615129; | ||||
| 
 | ||||
|         public WeibullK05La1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override double ShapeFunction(double x) => CONSTANT * ( (K / LAMBDA) * Math.Pow(x / LAMBDA, K - 1.0d) * Math.Exp(-Math.Pow(x/LAMBDA, K))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								FastRng/Double/IRandom.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								FastRng/Double/IRandom.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng.Double.Distributions; | ||||
| 
 | ||||
| namespace FastRng.Double | ||||
| { | ||||
|     public interface IRandom : IDisposable | ||||
|     { | ||||
|         public ValueTask<double> GetUniform(CancellationToken cancel = default); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										61
									
								
								FastRng/Double/MathTools.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								FastRng/Double/MathTools.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Double | ||||
| { | ||||
|     public static class MathTools | ||||
|     { | ||||
|         private static readonly double SQRT_2 = Math.Sqrt(2.0); | ||||
|         private static readonly double SQRT_PI = Math.Sqrt(Math.PI); | ||||
|          | ||||
|         public static double Gamma(double z) | ||||
|         { | ||||
|             // Source: http://rosettacode.org/wiki/Gamma_function#Go | ||||
|              | ||||
|             const double F1 = 6.5; | ||||
|             const double A1 = .99999999999980993; | ||||
|             const double A2 = 676.5203681218851; | ||||
|             const double A3 = 1259.1392167224028; | ||||
|             const double A4 = 771.32342877765313; | ||||
|             const double A5 = 176.61502916214059; | ||||
|             const double A6 = 12.507343278686905; | ||||
|             const double A7 = .13857109526572012; | ||||
|             const double A8 = 9.9843695780195716e-6; | ||||
|             const double A9 = 1.5056327351493116e-7; | ||||
| 
 | ||||
|             var t = z + F1; | ||||
|             var x =  A1 + | ||||
|                      A2 / z - | ||||
|                      A3 / (z + 1) + | ||||
|                      A4 / (z + 2) - | ||||
|                      A5 / (z + 3) + | ||||
|                      A6 / (z + 4) - | ||||
|                      A7 / (z + 5) + | ||||
|                      A8 / (z + 6) + | ||||
|                      A9 / (z + 7); | ||||
| 
 | ||||
|             return MathTools.SQRT_2 * MathTools.SQRT_PI * Math.Pow(t, z - 0.5) * Math.Exp(-t) * x; | ||||
|         } | ||||
|          | ||||
|         public static double Factorial(double x) => MathTools.Gamma(x + 1.0); | ||||
| 
 | ||||
|         public static ulong Factorial(uint x) | ||||
|         { | ||||
|             if (x > 20) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(x), $"Cannot compute {x}!, since ulong.max is 18_446_744_073_709_551_615."); | ||||
|              | ||||
|             ulong accumulator = 1; | ||||
|             for (uint factor = 1; factor <= x; factor++) | ||||
|                 accumulator *= factor; | ||||
| 
 | ||||
|             return accumulator; | ||||
|         } | ||||
| 
 | ||||
|         public static ulong Factorial(int x) | ||||
|         { | ||||
|             if(x < 0) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(x), "Given value must be greater as zero."); | ||||
| 
 | ||||
|             return MathTools.Factorial((uint) x); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										308
									
								
								FastRng/Double/MultiThreadedRng.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								FastRng/Double/MultiThreadedRng.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,308 @@ | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng.Double.Distributions; | ||||
| 
 | ||||
| namespace FastRng.Double | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A fast multi-threaded pseudo random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// Please note, that Math.NET's (https://www.mathdotnet.com/) random number generator is in some situations faster. | ||||
|     /// Unlike Math.NET, MultiThreadedRng is multi-threaded and async. Consumers can await the next number without | ||||
|     /// blocking resources. Additionally, consumers can use a token to cancel e.g. timeout an operation as well.<br/><br/> | ||||
|     /// | ||||
|     /// MultiThreadedRng using a shape fitter (a rejection sampler) to enforce arbitrary shapes of probabilities for | ||||
|     /// desired distributions. By using the shape fitter, it is even easy to define discontinuous, arbitrary functions | ||||
|     /// as shapes. Any consumer can define and use own distributions.<br/><br/> | ||||
|     ///  | ||||
|     /// This class uses the George Marsaglia's MWC algorithm. The algorithm's implementation based loosely on John D. | ||||
|     /// Cook's (johndcook.com) implementation (https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation). | ||||
|     /// Thanks John for the inspiration.<br/><br/> | ||||
|     /// | ||||
|     /// Please notice: When using the debug environment, MultiThreadedRng uses a smaller buffer size. Please ensure, | ||||
|     /// that the production environment uses a release build, though. | ||||
|     /// </remarks> | ||||
|     public sealed class MultiThreadedRng : IRandom, IDisposable | ||||
|     { | ||||
|         #if DEBUG | ||||
|             private const int BUFFER_SIZE = 10_000; | ||||
|         #else | ||||
|             private const int BUFFER_SIZE = 1_000_000; | ||||
|         #endif | ||||
| 
 | ||||
|         // The queue size means, how many buffer we store in a queue at the same time: | ||||
|         private const int QUEUE_SIZE = 2; | ||||
|          | ||||
|         // Gets used to stop the producer threads: | ||||
|         private readonly CancellationTokenSource producerTokenSource = new CancellationTokenSource(); | ||||
|          | ||||
|         // The time a thread waits e.g. to check if the queue needs a new buffer: | ||||
|         private readonly TimeSpan waiter = TimeSpan.FromMilliseconds(10); | ||||
|          | ||||
|         // The first queue, where to store buffers of random uint numbers: | ||||
|         private readonly ConcurrentQueue<uint[]> queueIntegers = new ConcurrentQueue<uint[]>(); | ||||
|          | ||||
|         // The second queue, where to store buffers of uniform random double numbers: | ||||
|         private readonly ConcurrentQueue<double[]> queueDoubles = new ConcurrentQueue<double[]>(); | ||||
| 
 | ||||
|         // The uint producer thread: | ||||
|         private Thread producerRandomUint; | ||||
|          | ||||
|         // The uniform double producer thread: | ||||
|         private Thread producerRandomUniformDistributedDouble; | ||||
|          | ||||
|         // Variable w and z for the uint generator. Both get used | ||||
|         // as seeding variable as well (cf. constructors) | ||||
|         private uint mW; | ||||
|         private uint mZ; | ||||
|          | ||||
|         // This is the current buffer for the consumer side i.e. the public interfaces: | ||||
|         private double[] currentBuffer = Array.Empty<double>(); | ||||
|          | ||||
|         // The current pointer to the next current buffer's address to read from: | ||||
|         private int currentBufferPointer = BUFFER_SIZE; | ||||
| 
 | ||||
|         #region Constructors | ||||
| 
 | ||||
|         public MultiThreadedRng() | ||||
|         { | ||||
|             // | ||||
|             // Initialize the mW and mZ by using | ||||
|             // the system's time. | ||||
|             // | ||||
|             var now = DateTime.Now; | ||||
|             var ticks = now.Ticks; | ||||
|             this.mW = (uint) (ticks >> 16); | ||||
|             this.mZ = (uint) (ticks % 4_294_967_296); | ||||
|             this.StartProducerThreads(); | ||||
|         } | ||||
| 
 | ||||
|         public MultiThreadedRng(uint seedU) | ||||
|         { | ||||
|             this.mW = seedU; | ||||
|             this.mZ = 362_436_069; | ||||
|             this.StartProducerThreads(); | ||||
|         } | ||||
|          | ||||
|         public MultiThreadedRng(uint seedU, uint seedV) | ||||
|         { | ||||
|             this.mW = seedU; | ||||
|             this.mZ = seedV; | ||||
|             this.StartProducerThreads(); | ||||
|         } | ||||
| 
 | ||||
|         private void StartProducerThreads() | ||||
|         { | ||||
|             this.producerRandomUint = new Thread(() => this.RandomProducerUint(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|             this.producerRandomUint.Start(); | ||||
|             this.producerRandomUniformDistributedDouble = new Thread(() => this.RandomProducerUniformDistributedDouble(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|             this.producerRandomUniformDistributedDouble.Start(); | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
|         #region Producers | ||||
| 
 | ||||
|         [ExcludeFromCodeCoverage] | ||||
|         private async void RandomProducerUint(CancellationToken cancellationToken) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     // A local next buffer, which gets filled next: | ||||
|                     var nextBuffer = new uint[BUFFER_SIZE]; | ||||
|                      | ||||
|                     // Produce the necessary number of random uints: | ||||
|                     for (var n = 0; n < nextBuffer.Length && !cancellationToken.IsCancellationRequested; n++) | ||||
|                     { | ||||
|                         this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16); | ||||
|                         this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16); | ||||
|                         nextBuffer[n] = (this.mZ << 16) + this.mW; | ||||
|                     } | ||||
| 
 | ||||
|                     // Inside this loop, we try to enqueue the produced buffer: | ||||
|                     while (!cancellationToken.IsCancellationRequested) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             // Ensure, that we do not produce more buffers, as configured:  | ||||
|                             if (this.queueIntegers.Count < QUEUE_SIZE) | ||||
|                             { | ||||
|                                 this.queueIntegers.Enqueue(nextBuffer); | ||||
|                                 break; | ||||
|                             } | ||||
| 
 | ||||
|                             // The queue was full. Wait a moment and try it again: | ||||
|                             await Task.Delay(this.waiter, cancellationToken); | ||||
|                         } | ||||
|                         catch (TaskCanceledException) | ||||
|                         { | ||||
|                             // The producers should be stopped: | ||||
|                             return; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (OperationCanceledException) | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         [ExcludeFromCodeCoverage] | ||||
|         private async void RandomProducerUniformDistributedDouble(CancellationToken cancellationToken) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     // A local source buffer of uints:  | ||||
|                     uint[] bufferSource = null; | ||||
|                      | ||||
|                     // Try to get the next source buffer: | ||||
|                     while (!this.queueIntegers.TryDequeue(out bufferSource) && !cancellationToken.IsCancellationRequested) | ||||
|                         await Task.Delay(this.waiter, cancellationToken); | ||||
| 
 | ||||
|                     // Case: The producers should be stopped: | ||||
|                     if(bufferSource == null) | ||||
|                         return; | ||||
|                      | ||||
|                     // A local buffer to fill with uniform doubles: | ||||
|                     var nextBuffer = new double[BUFFER_SIZE]; | ||||
|                      | ||||
|                     // Generate the necessary number of doubles: | ||||
|                     for (var n = 0; n < nextBuffer.Length && !cancellationToken.IsCancellationRequested; n++) | ||||
|                         nextBuffer[n] = (bufferSource[n] + 1.0) * 2.328306435454494e-10; | ||||
| 
 | ||||
|                     // Inside this loop, we try to enqueue the generated buffer: | ||||
|                     while (!cancellationToken.IsCancellationRequested) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             // Ensure, that the queue contains only the configured number of buffers: | ||||
|                             if (this.queueDoubles.Count < QUEUE_SIZE) | ||||
|                             { | ||||
|                                 this.queueDoubles.Enqueue(nextBuffer); | ||||
|                                 break; | ||||
|                             } | ||||
| 
 | ||||
|                             // The queue was full. Wait a moment and try it again: | ||||
|                             await Task.Delay(this.waiter, cancellationToken); | ||||
|                         } | ||||
|                         catch (TaskCanceledException) | ||||
|                         { | ||||
|                             return; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (OperationCanceledException) | ||||
|             { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
|         #region Implementing interface | ||||
| 
 | ||||
|         public async ValueTask<double> GetUniform(CancellationToken cancel = default) | ||||
|         { | ||||
|             while (!cancel.IsCancellationRequested) | ||||
|             { | ||||
|                 // Check, if we need a new buffer to read from: | ||||
|                 if (this.currentBufferPointer >= BUFFER_SIZE) | ||||
|                 { | ||||
|                     // Create a local copy of the current buffer's pointer: | ||||
|                     var currentBufferReference = this.currentBuffer; | ||||
|                      | ||||
|                     // Here, we store the next buffer until we implement it: | ||||
|                     var nextBuffer = Array.Empty<double>(); | ||||
|                      | ||||
|                     // Try to get the next buffer from the queue: | ||||
|                     while (this.currentBufferPointer >= BUFFER_SIZE && currentBufferReference == this.currentBuffer && !this.queueDoubles.TryDequeue(out nextBuffer)) | ||||
|                     { | ||||
|                         // | ||||
|                         // Case: There is no next buffer available. | ||||
|                         // Must wait for producer(s) to provide next. | ||||
|                         // | ||||
|                         try | ||||
|                         { | ||||
|                             await Task.Delay(this.waiter, cancel); | ||||
|                         } | ||||
|                         catch (TaskCanceledException) | ||||
|                         { | ||||
|                             // | ||||
|                             // Case: The consumer cancelled the request. | ||||
|                             // | ||||
|                             return double.NaN; | ||||
|                         } | ||||
|                     } | ||||
|                  | ||||
|                     // | ||||
|                     // Note: In general, it does not matter if the following compare-exchange is successful. | ||||
|                     // 1st case: It was successful -- everything is fine. But we are responsible to re-set the currentBufferPointer. | ||||
|                     // 2nd case: It was not successful. This means, that another thread was successful, though. | ||||
|                     //           That case is fine as well. But we would loose one buffer of work. Thus, we | ||||
|                     //           check for this case and preserve the buffer full of work. | ||||
|                     // | ||||
|                      | ||||
|                     // Try to implement the dequeued buffer without locking other threads: | ||||
|                     if (Interlocked.CompareExchange(ref this.currentBuffer, nextBuffer, currentBufferReference) != currentBufferReference) | ||||
|                     { | ||||
|                         // | ||||
|                         // Case: Another thread updated the buffer already. | ||||
|                         // Thus, we enqueue our copy of the next buffer to preserve it. | ||||
|                         // | ||||
|                         this.queueDoubles.Enqueue(nextBuffer); | ||||
|                          | ||||
|                         // Next? We can go ahead and yield a random number... | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         // | ||||
|                         // Case: We updated the buffer. | ||||
|                         // | ||||
|                         this.currentBufferPointer = 0; | ||||
|                          | ||||
|                         // Next? We can go ahead and yield a random number... | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Made a local copy of the current pointer: | ||||
|                 var myPointer = this.currentBufferPointer; | ||||
|                  | ||||
|                 // Increment the pointer for the next thread or call: | ||||
|                 var nextPointer = myPointer + 1; | ||||
|                  | ||||
|                 // Try to update the pointer without locking other threads: | ||||
|                 if (Interlocked.CompareExchange(ref this.currentBufferPointer, nextPointer, myPointer) == myPointer) | ||||
|                 { | ||||
|                     // | ||||
|                     // Case: Success. We updated the pointer and, thus, can use the pointer to read a number. | ||||
|                     // | ||||
|                     return this.currentBuffer[myPointer]; | ||||
|                 } | ||||
|                  | ||||
|                 // | ||||
|                 // Case: Another thread updated the pointer already. Must restart the process | ||||
|                 // to get a random number. | ||||
|                 // | ||||
|             } | ||||
| 
 | ||||
|             // | ||||
|             // Case: The consumer cancelled the request. | ||||
|             // | ||||
|             return double.NaN; | ||||
|         } | ||||
|          | ||||
|         private void StopProducer() => this.producerTokenSource.Cancel(); | ||||
| 
 | ||||
|         public void Dispose() => this.StopProducer(); | ||||
| 
 | ||||
|         #endregion | ||||
|     } | ||||
| } | ||||
							
								
								
									
										68
									
								
								FastRng/Double/ShapeFitter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								FastRng/Double/ShapeFitter.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| using System; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng.Double.Distributions; | ||||
| 
 | ||||
| namespace FastRng.Double | ||||
| { | ||||
|     /// <summary> | ||||
|     /// ShapeFitter is a rejection sampler, cf. https://en.wikipedia.org/wiki/Rejection_sampling | ||||
|     /// </summary> | ||||
|     public sealed class ShapeFitter | ||||
|     { | ||||
|         private readonly double[] probabilities; | ||||
|         private readonly IRandom rng; | ||||
|         private readonly double max; | ||||
|         private readonly double sampleSize; | ||||
|         private readonly IDistribution uniform; | ||||
| 
 | ||||
|         public ShapeFitter(Func<double, double> shapeFunction, IRandom rng, ushort sampleSize = 50) | ||||
|         { | ||||
|             this.rng = rng; | ||||
|             this.uniform = new Uniform(rng); | ||||
|             this.sampleSize = sampleSize; | ||||
|             this.probabilities = new double[sampleSize]; | ||||
| 
 | ||||
|             var sampleStepSize = 1.0d / sampleSize; | ||||
|             var nextStep = 0.0 + sampleStepSize; | ||||
|             var maxValue = 0.0d; | ||||
|             for (var n = 0; n < sampleSize; n++) | ||||
|             { | ||||
|                 this.probabilities[n] = shapeFunction(nextStep); | ||||
|                 if (this.probabilities[n] > maxValue) | ||||
|                     maxValue = this.probabilities[n]; | ||||
|                  | ||||
|                 nextStep += sampleStepSize; | ||||
|             } | ||||
| 
 | ||||
|             this.max = maxValue; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<double> NextNumber(CancellationToken token = default) | ||||
|         { | ||||
|             while (!token.IsCancellationRequested) | ||||
|             { | ||||
|                 var x = await this.rng.GetUniform(token); | ||||
|                 if (double.IsNaN(x)) | ||||
|                     return x; | ||||
|                  | ||||
|                 var nextBucket = (int)Math.Floor(x * this.sampleSize); | ||||
|                 if (nextBucket >= this.probabilities.Length) | ||||
|                     nextBucket = this.probabilities.Length - 1; | ||||
|                  | ||||
|                 var threshold = this.probabilities[nextBucket]; | ||||
|                 var y = await this.uniform.NextNumber(0.0d, this.max, token); | ||||
|                 if (double.IsNaN(y)) | ||||
|                     return y; | ||||
|                  | ||||
|                 if(y > threshold) | ||||
|                     continue; | ||||
| 
 | ||||
|                 return x; | ||||
|             } | ||||
| 
 | ||||
|             return double.NaN; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,28 +1,7 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
|     <PropertyGroup> | ||||
|         <TargetFramework>net8.0</TargetFramework> | ||||
|         <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
|         <AssemblyVersion>1.2.0</AssemblyVersion> | ||||
|         <FileVersion>1.2.0</FileVersion> | ||||
|         <PackageVersion>1.2.0</PackageVersion> | ||||
|         <Authors>Thorsten Sommer</Authors> | ||||
|         <PackageProjectUrl>https://devops.tsommer.org/open-source/dotnet/FastRng</PackageProjectUrl> | ||||
|         <RepositoryUrl>https://devops.tsommer.org/open-source/dotnet/FastRng</RepositoryUrl> | ||||
|         <PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression> | ||||
|         <LangVersion>latest</LangVersion> | ||||
|         <PackageReadmeFile>README.md</PackageReadmeFile> | ||||
|         <TargetFramework>net5.0</TargetFramework> | ||||
|     </PropertyGroup> | ||||
| 
 | ||||
|     <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | ||||
|       <DocumentationFile>bin\Debug\FastRng.xml</DocumentationFile> | ||||
|     </PropertyGroup> | ||||
| 
 | ||||
|     <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | ||||
|       <DocumentationFile>bin\Release\FastRng.xml</DocumentationFile> | ||||
|     </PropertyGroup> | ||||
| 
 | ||||
|     <ItemGroup> | ||||
|         <None Include="..\README.md" Pack="true" PackagePath="\"/> | ||||
|     </ItemGroup> | ||||
| </Project> | ||||
|  | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/BetaA2B2.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/BetaA2B2.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class BetaA2B2 : Distribution | ||||
|     { | ||||
|         private const float ALPHA = 2f; | ||||
|         private const float BETA = 2f; | ||||
|         private const float CONSTANT = 4f; | ||||
| 
 | ||||
|         public BetaA2B2(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * MathF.Pow(x, ALPHA - 1f) * MathF.Pow(1f - x, BETA - 1f); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/BetaA2B5.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/BetaA2B5.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class BetaA2B5 : Distribution | ||||
|     { | ||||
|         private const float ALPHA = 2f; | ||||
|         private const float BETA = 5f; | ||||
|         private const float CONSTANT = 12.2f; | ||||
| 
 | ||||
|         public BetaA2B5(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * MathF.Pow(x, ALPHA - 1f) * MathF.Pow(1f - x, BETA - 1f); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/BetaA5B2.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/BetaA5B2.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class BetaA5B2 : Distribution | ||||
|     { | ||||
|         private const float ALPHA = 5f; | ||||
|         private const float BETA = 2f; | ||||
|         private const float CONSTANT = 12.2f; | ||||
| 
 | ||||
|         public BetaA5B2(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * MathF.Pow(x, ALPHA - 1f) * MathF.Pow(1f - x, BETA - 1f); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/CauchyLorentzX0.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/CauchyLorentzX0.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class CauchyLorentzX0 : Distribution | ||||
|     { | ||||
|         private const float CONSTANT = 0.31f; | ||||
|         private const float SCALE = 0.1f; | ||||
|         private const float MEDIAN = 0.0f; | ||||
| 
 | ||||
|         public CauchyLorentzX0(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * (1.0f / (MathF.PI * SCALE)) * ((SCALE * SCALE) / (MathF.Pow(x - MEDIAN, 2f) + (SCALE * SCALE))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/CauchyLorentzX1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/CauchyLorentzX1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class CauchyLorentzX1 : Distribution | ||||
|     { | ||||
|         private const float CONSTANT = 0.31f; | ||||
|         private const float SCALE = 0.1f; | ||||
|         private const float MEDIAN = 1.0f; | ||||
| 
 | ||||
|         public CauchyLorentzX1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * (1.0f / (MathF.PI * SCALE)) * ((SCALE * SCALE) / (MathF.Pow(x - MEDIAN, 2f) + (SCALE * SCALE))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								FastRng/Float/Distributions/ChiSquareK1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FastRng/Float/Distributions/ChiSquareK1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class ChiSquareK1 : Distribution | ||||
|     { | ||||
|         private const float K = 1.0f; | ||||
|         private const float K_HALF = K * 0.5f; | ||||
|         private const float K_HALF_MINUS_ONE = K_HALF - 1.0f; | ||||
|         private const float CONSTANT = 0.252f; | ||||
| 
 | ||||
|         private static readonly float DIVISOR; | ||||
|          | ||||
|         static ChiSquareK1() | ||||
|         { | ||||
|             var twoToTheKHalf = MathF.Pow(2f, K_HALF); | ||||
|             var gammaKHalf = MathTools.Gamma(K_HALF); | ||||
|             DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|         } | ||||
|          | ||||
|         public ChiSquareK1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * ((MathF.Pow(x, K_HALF_MINUS_ONE) * MathF.Exp(-x * 0.5f)) / DIVISOR); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								FastRng/Float/Distributions/ChiSquareK10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FastRng/Float/Distributions/ChiSquareK10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class ChiSquareK10 : Distribution | ||||
|     { | ||||
|         private const float K = 10.0f; | ||||
|         private const float K_HALF = K * 0.5f; | ||||
|         private const float K_HALF_MINUS_ONE = K_HALF - 1.0f; | ||||
|         private const float CONSTANT = 0.252f; | ||||
| 
 | ||||
|         private static readonly float DIVISOR; | ||||
|          | ||||
|         static ChiSquareK10() | ||||
|         { | ||||
|             var twoToTheKHalf = MathF.Pow(2f, K_HALF); | ||||
|             var gammaKHalf = MathTools.Gamma(K_HALF); | ||||
|             DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|         } | ||||
|          | ||||
|         public ChiSquareK10(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * ((MathF.Pow(x, K_HALF_MINUS_ONE) * MathF.Exp(-x * 0.5f)) / DIVISOR); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								FastRng/Float/Distributions/ChiSquareK4.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FastRng/Float/Distributions/ChiSquareK4.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class ChiSquareK4 : Distribution | ||||
|     { | ||||
|         private const float K = 4.0f; | ||||
|         private const float K_HALF = K * 0.5f; | ||||
|         private const float K_HALF_MINUS_ONE = K_HALF - 1.0f; | ||||
|         private const float CONSTANT = 0.252f; | ||||
| 
 | ||||
|         private static readonly float DIVISOR; | ||||
|          | ||||
|         static ChiSquareK4() | ||||
|         { | ||||
|             var twoToTheKHalf = MathF.Pow(2, K_HALF); | ||||
|             var gammaKHalf = MathTools.Gamma(K_HALF); | ||||
|             DIVISOR = twoToTheKHalf * gammaKHalf; | ||||
|         } | ||||
| 
 | ||||
|         public ChiSquareK4(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * ((MathF.Pow(x, K_HALF_MINUS_ONE) * MathF.Exp(-x * 0.5f)) / DIVISOR); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										75
									
								
								FastRng/Float/Distributions/Distribution.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								FastRng/Float/Distributions/Distribution.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public abstract class Distribution : IDistribution | ||||
|     { | ||||
|         private readonly ShapeFitter fitter; | ||||
|         private readonly IRandom random; | ||||
| 
 | ||||
|         protected Distribution(IRandom rng) | ||||
|         { | ||||
|             if (rng == null) | ||||
|                 throw new ArgumentNullException(nameof(rng), "An IRandom implementation is needed."); | ||||
|                  | ||||
|             this.random = rng; | ||||
|             this.fitter = new ShapeFitter(this.ShapeFunction, this.random, 100); | ||||
|         } | ||||
| 
 | ||||
|         protected abstract float ShapeFunction(float x); | ||||
|          | ||||
|         public async ValueTask<float> GetDistributedValue(CancellationToken token = default) => await this.fitter.NextNumber(token); | ||||
|          | ||||
|         public async ValueTask<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (uint) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (ulong) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<float> NextNumber(float rangeStart, float rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (distributedValue * range) + rangeStart; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<float> NextNumber(CancellationToken cancel = default) => await this.NextNumber(0.0f, 1.0f, cancel); | ||||
|          | ||||
|         public async ValueTask<bool> HasDecisionBeenMade(float above, float below = 1, CancellationToken cancel = default) | ||||
|         { | ||||
|             var number = await this.NextNumber(cancel); | ||||
|             return number > above && number < below; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								FastRng/Float/Distributions/ExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								FastRng/Float/Distributions/ExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class ExponentialLa10 : Distribution | ||||
|     { | ||||
|         private const float LAMBDA = 10.0f; | ||||
|         private const float CONSTANT = 0.1106f; | ||||
| 
 | ||||
|         public ExponentialLa10(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * LAMBDA * MathF.Exp(-LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								FastRng/Float/Distributions/ExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								FastRng/Float/Distributions/ExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class ExponentialLa5 : Distribution | ||||
|     { | ||||
|         private const float LAMBDA = 5.0f; | ||||
|         private const float CONSTANT = 0.2103f; | ||||
| 
 | ||||
|         public ExponentialLa5(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * LAMBDA * MathF.Exp(-LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								FastRng/Float/Distributions/GammaA5B15.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								FastRng/Float/Distributions/GammaA5B15.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class GammaA5B15 : Distribution | ||||
|     { | ||||
|         private const float ALPHA = 5.0f; | ||||
|         private const float BETA = 15.0f; | ||||
|         private const float CONSTANT = 0.341344210715475f; | ||||
| 
 | ||||
|         private static readonly float GAMMA_ALPHA; | ||||
|         private static readonly float BETA_TO_THE_ALPHA; | ||||
|          | ||||
|         static GammaA5B15() | ||||
|         { | ||||
|             GAMMA_ALPHA = MathTools.Gamma(ALPHA); | ||||
|             BETA_TO_THE_ALPHA = MathF.Pow(BETA, ALPHA); | ||||
|         } | ||||
| 
 | ||||
|         public GammaA5B15(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * ((BETA_TO_THE_ALPHA * MathF.Pow(x, ALPHA - 1.0f) * MathF.Exp(-BETA * x)) / GAMMA_ALPHA); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								FastRng/Float/Distributions/IDistribution.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								FastRng/Float/Distributions/IDistribution.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public interface IDistribution | ||||
|     { | ||||
|         public ValueTask<float> GetDistributedValue(CancellationToken token); | ||||
|          | ||||
|         public ValueTask<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<float> NextNumber(float rangeStart, float rangeEnd, CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<float> NextNumber(CancellationToken cancel = default); | ||||
|          | ||||
|         public ValueTask<bool> HasDecisionBeenMade(float above, float below = 1.0f, CancellationToken cancel = default); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								FastRng/Float/Distributions/InverseExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								FastRng/Float/Distributions/InverseExponentialLa10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class InverseExponentialLa10 : Distribution | ||||
|     { | ||||
|         private const float LAMBDA = 10.0f; | ||||
|         private const float CONSTANT = 4.539992976248453e-06f; | ||||
| 
 | ||||
|         public InverseExponentialLa10(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * LAMBDA * MathF.Exp(LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								FastRng/Float/Distributions/InverseExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								FastRng/Float/Distributions/InverseExponentialLa5.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class InverseExponentialLa5 : Distribution | ||||
|     { | ||||
|         private const float LAMBDA = 5.0f; | ||||
|         private const float CONSTANT = 0.001347589399817f; | ||||
| 
 | ||||
|         public InverseExponentialLa5(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * LAMBDA * MathF.Exp(LAMBDA * x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								FastRng/Float/Distributions/InverseGammaA3B05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								FastRng/Float/Distributions/InverseGammaA3B05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class InverseGammaA3B05 : Distribution | ||||
|     { | ||||
|         private const float ALPHA = 3.0f; | ||||
|         private const float BETA = 0.5f; | ||||
|         private const float CONSTANT = 0.213922656884911f; | ||||
| 
 | ||||
|         private static readonly float FACTOR_LEFT; | ||||
|          | ||||
|         static InverseGammaA3B05() | ||||
|         { | ||||
|             var gammaAlpha = MathTools.Gamma(ALPHA); | ||||
|             var betaToTheAlpha = MathF.Pow(BETA, ALPHA); | ||||
|              | ||||
|             FACTOR_LEFT = CONSTANT * (betaToTheAlpha / gammaAlpha); | ||||
|         } | ||||
|          | ||||
|         public InverseGammaA3B05(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => FACTOR_LEFT * MathF.Pow(x, -ALPHA - 1.0f) * MathF.Exp(-BETA / x); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								FastRng/Float/Distributions/LaplaceB01M0.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								FastRng/Float/Distributions/LaplaceB01M0.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class LaplaceB01M0 : Distribution | ||||
|     { | ||||
|         private const float B = 0.1f; | ||||
|         private const float MU = 0.0f; | ||||
|         private const float CONSTANT = 0.221034183615129f; | ||||
|          | ||||
|         private static readonly float FACTOR_LEFT; | ||||
|          | ||||
|         static LaplaceB01M0() | ||||
|         { | ||||
|             FACTOR_LEFT = CONSTANT / (2.0f * B); | ||||
|         } | ||||
|          | ||||
|         public LaplaceB01M0(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => FACTOR_LEFT * MathF.Exp(-MathF.Abs(x - MU) / B); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								FastRng/Float/Distributions/LaplaceB01M05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								FastRng/Float/Distributions/LaplaceB01M05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class LaplaceB01M05 : Distribution | ||||
|     { | ||||
|         private const float B = 0.1f; | ||||
|         private const float MU = 0.5f; | ||||
|         private const float CONSTANT = 0.2f; | ||||
|          | ||||
|         private static readonly float FACTOR_LEFT; | ||||
|          | ||||
|         static LaplaceB01M05() | ||||
|         { | ||||
|             FACTOR_LEFT = CONSTANT / (2.0f * B); | ||||
|         } | ||||
|          | ||||
|         public LaplaceB01M05(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => FACTOR_LEFT * MathF.Exp(-MathF.Abs(x - MU) / B); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								FastRng/Float/Distributions/LogNormalS1M0.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								FastRng/Float/Distributions/LogNormalS1M0.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class LogNormalS1M0 : Distribution | ||||
|     { | ||||
|         private const float SIGMA = 1.0f; | ||||
|         private const float MU = 0.0f; | ||||
|         private const float CONSTANT = 1.51998658387455f; | ||||
|          | ||||
|         private static readonly float FACTOR; | ||||
|          | ||||
|         static LogNormalS1M0() | ||||
|         { | ||||
|             FACTOR = SIGMA * MathF.Sqrt(2f * MathF.PI); | ||||
|         } | ||||
|          | ||||
|         public LogNormalS1M0(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => (CONSTANT / (x * FACTOR)) * MathF.Exp( -(MathF.Pow(MathF.Log(x) - MU, 2f) / (2f * MathF.Pow(SIGMA, 2f)))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/NormalS02M05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/NormalS02M05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class NormalS02M05 : Distribution | ||||
|     { | ||||
|         private const float SQRT_2_PI = 2.506628275f; | ||||
|         private const float STDDEV = 0.2f; | ||||
|         private const float MEAN = 0.5f; | ||||
| 
 | ||||
|         public NormalS02M05(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => 1.0f / (STDDEV * SQRT_2_PI) * MathF.Exp(-0.5f * MathF.Pow((x - MEAN) / STDDEV, 2.0f)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								FastRng/Float/Distributions/StudentTNu1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								FastRng/Float/Distributions/StudentTNu1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class StudentTNu1 : Distribution | ||||
|     { | ||||
|         private const float NU = 1.0f; | ||||
|         private const float START = 0.0f; | ||||
|         private const float COMPRESS = 1.0f; | ||||
|         private const float CONSTANT = 3.14190548592729f; | ||||
|          | ||||
|         private static readonly float DIVIDEND; | ||||
|         private static readonly float DIVISOR; | ||||
|         private static readonly float EXPONENT; | ||||
|          | ||||
|         static StudentTNu1() | ||||
|         { | ||||
|             DIVIDEND = MathTools.Gamma((NU + 1.0f) * 0.5f); | ||||
|             DIVISOR = MathF.Sqrt(NU * MathF.PI) * MathTools.Gamma(NU * 0.5f); | ||||
|             EXPONENT = -((NU + 1.0f) * 0.5f); | ||||
|         } | ||||
|          | ||||
|         public StudentTNu1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * MathF.Pow((DIVIDEND / DIVISOR) * MathF.Pow(1.0f + MathF.Pow(START + x * COMPRESS, 2f) / NU, EXPONENT), COMPRESS); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										71
									
								
								FastRng/Float/Distributions/Uniform.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								FastRng/Float/Distributions/Uniform.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class Uniform : IDistribution | ||||
|     { | ||||
|         private readonly IRandom rng; | ||||
|          | ||||
|         public Uniform(IRandom rng) | ||||
|         { | ||||
|             if (rng == null) | ||||
|                 throw new ArgumentNullException(nameof(rng), "An IRandom implementation is needed."); | ||||
|              | ||||
|             this.rng = rng; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<float> GetDistributedValue(CancellationToken token = default) => await this.rng.GetUniform(token); | ||||
|          | ||||
|         public async ValueTask<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (uint) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (ulong) ((distributedValue * range) + rangeStart); | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<float> NextNumber(float rangeStart, float rangeEnd, CancellationToken cancel = default(CancellationToken)) | ||||
|         { | ||||
|             if (rangeStart > rangeEnd) | ||||
|             { | ||||
|                 var tmp = rangeStart; | ||||
|                 rangeStart = rangeEnd; | ||||
|                 rangeEnd = tmp; | ||||
|             } | ||||
|              | ||||
|             var range = rangeEnd - rangeStart; | ||||
|             var distributedValue = await this.GetDistributedValue(cancel); | ||||
|             return (distributedValue * range) + rangeStart; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<float> NextNumber(CancellationToken cancel = default) => await this.NextNumber(0.0f, 1.0f, cancel); | ||||
|          | ||||
|         public async ValueTask<bool> HasDecisionBeenMade(float above, float below = 1, CancellationToken cancel = default) | ||||
|         { | ||||
|             var number = await this.NextNumber(cancel); | ||||
|             return number > above && number < below; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								FastRng/Float/Distributions/WeibullK05La1.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								FastRng/Float/Distributions/WeibullK05La1.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float.Distributions | ||||
| { | ||||
|     public sealed class WeibullK05La1 : Distribution | ||||
|     { | ||||
|         private const float K = 0.5f; | ||||
|         private const float LAMBDA = 1.0f; | ||||
|         private const float CONSTANT = 0.221034183615129f; | ||||
| 
 | ||||
|         public WeibullK05La1(IRandom rng) : base(rng) | ||||
|         { | ||||
|         } | ||||
|          | ||||
|         protected override float ShapeFunction(float x) => CONSTANT * ( (K / LAMBDA) * MathF.Pow(x / LAMBDA, K - 1.0f) * MathF.Exp(-MathF.Pow(x/LAMBDA, K))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								FastRng/Float/IRandom.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								FastRng/Float/IRandom.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng.Float.Distributions; | ||||
| 
 | ||||
| namespace FastRng.Float | ||||
| { | ||||
|     public interface IRandom : IDisposable | ||||
|     { | ||||
|         public ValueTask<float> GetUniform(CancellationToken cancel = default); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										61
									
								
								FastRng/Float/MathTools.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								FastRng/Float/MathTools.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng.Float | ||||
| { | ||||
|     public static class MathTools | ||||
|     { | ||||
|         private static readonly float SQRT_2 = MathF.Sqrt(2.0f); | ||||
|         private static readonly float SQRT_PI = MathF.Sqrt(MathF.PI); | ||||
|          | ||||
|         public static float Gamma(float z) | ||||
|         { | ||||
|             // Source: http://rosettacode.org/wiki/Gamma_function#Go | ||||
|              | ||||
|             const float F1 = 6.5f; | ||||
|             const float A1 = .99999999999980993f; | ||||
|             const float A2 = 676.5203681218851f; | ||||
|             const float A3 = 1259.1392167224028f; | ||||
|             const float A4 = 771.32342877765313f; | ||||
|             const float A5 = 176.61502916214059f; | ||||
|             const float A6 = 12.507343278686905f; | ||||
|             const float A7 = .13857109526572012f; | ||||
|             const float A8 = 9.9843695780195716e-6f; | ||||
|             const float A9 = 1.5056327351493116e-7f; | ||||
| 
 | ||||
|             var t = z + F1; | ||||
|             var x =  A1 + | ||||
|                      A2 / z - | ||||
|                      A3 / (z + 1) + | ||||
|                      A4 / (z + 2) - | ||||
|                      A5 / (z + 3) + | ||||
|                      A6 / (z + 4) - | ||||
|                      A7 / (z + 5) + | ||||
|                      A8 / (z + 6) + | ||||
|                      A9 / (z + 7); | ||||
| 
 | ||||
|             return MathTools.SQRT_2 * MathTools.SQRT_PI * MathF.Pow(t, z - 0.5f) * MathF.Exp(-t) * x; | ||||
|         } | ||||
|          | ||||
|         public static float Factorial(float x) => MathTools.Gamma(x + 1.0f); | ||||
| 
 | ||||
|         public static ulong Factorial(uint x) | ||||
|         { | ||||
|             if (x > 20) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(x), $"Cannot compute {x}!, since ulong.max is 18_446_744_073_709_551_615."); | ||||
|              | ||||
|             ulong accumulator = 1; | ||||
|             for (uint factor = 1; factor <= x; factor++) | ||||
|                 accumulator *= factor; | ||||
| 
 | ||||
|             return accumulator; | ||||
|         } | ||||
| 
 | ||||
|         public static ulong Factorial(int x) | ||||
|         { | ||||
|             if(x < 0) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(x), "Given value must be greater as zero."); | ||||
| 
 | ||||
|             return MathTools.Factorial((uint) x); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										308
									
								
								FastRng/Float/MultiThreadedRng.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								FastRng/Float/MultiThreadedRng.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,308 @@ | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng.Float.Distributions; | ||||
| 
 | ||||
| namespace FastRng.Float | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A fast multi-threaded pseudo random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// Please note, that Math.NET's (https://www.mathdotnet.com/) random number generator is in some situations faster. | ||||
|     /// Unlike Math.NET, MultiThreadedRng is multi-threaded and async. Consumers can await the next number without | ||||
|     /// blocking resources. Additionally, consumers can use a token to cancel e.g. timeout an operation as well.<br/><br/> | ||||
|     /// | ||||
|     /// MultiThreadedRng using a shape fitter (a rejection sampler) to enforce arbitrary shapes of probabilities for | ||||
|     /// desired distributions. By using the shape fitter, it is even easy to define discontinuous, arbitrary functions | ||||
|     /// as shapes. Any consumer can define and use own distributions.<br/><br/> | ||||
|     ///  | ||||
|     /// This class uses the George Marsaglia's MWC algorithm. The algorithm's implementation based loosely on John D. | ||||
|     /// Cook's (johndcook.com) implementation (https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation). | ||||
|     /// Thanks John for the inspiration.<br/><br/> | ||||
|     /// | ||||
|     /// Please notice: When using the debug environment, MultiThreadedRng uses a smaller buffer size. Please ensure, | ||||
|     /// that the production environment uses a release build, though. | ||||
|     /// </remarks> | ||||
|     public sealed class MultiThreadedRng : IRandom, IDisposable | ||||
|     { | ||||
|         #if DEBUG | ||||
|             private const int BUFFER_SIZE = 10_000; | ||||
|         #else | ||||
|             private const int BUFFER_SIZE = 1_000_000; | ||||
|         #endif | ||||
| 
 | ||||
|         // The queue size means, how many buffer we store in a queue at the same time: | ||||
|         private const int QUEUE_SIZE = 2; | ||||
|          | ||||
|         // Gets used to stop the producer threads: | ||||
|         private readonly CancellationTokenSource producerTokenSource = new CancellationTokenSource(); | ||||
|          | ||||
|         // The time a thread waits e.g. to check if the queue needs a new buffer: | ||||
|         private readonly TimeSpan waiter = TimeSpan.FromMilliseconds(10); | ||||
|          | ||||
|         // The first queue, where to store buffers of random uint numbers: | ||||
|         private readonly ConcurrentQueue<uint[]> queueIntegers = new ConcurrentQueue<uint[]>(); | ||||
|          | ||||
|         // The second queue, where to store buffers of uniform random floating point numbers: | ||||
|         private readonly ConcurrentQueue<float[]> queueFloats = new ConcurrentQueue<float[]>(); | ||||
| 
 | ||||
|         // The uint producer thread: | ||||
|         private Thread producerRandomUint; | ||||
|          | ||||
|         // The uniform float producer thread: | ||||
|         private Thread producerRandomUniformDistributedFloat; | ||||
|          | ||||
|         // Variable w and z for the uint generator. Both get used | ||||
|         // as seeding variable as well (cf. constructors) | ||||
|         private uint mW; | ||||
|         private uint mZ; | ||||
|          | ||||
|         // This is the current buffer for the consumer side i.e. the public interfaces: | ||||
|         private float[] currentBuffer = Array.Empty<float>(); | ||||
|          | ||||
|         // The current pointer to the next current buffer's address to read from: | ||||
|         private int currentBufferPointer = BUFFER_SIZE; | ||||
| 
 | ||||
|         #region Constructors | ||||
| 
 | ||||
|         public MultiThreadedRng() | ||||
|         { | ||||
|             // | ||||
|             // Initialize the mW and mZ by using | ||||
|             // the system's time. | ||||
|             // | ||||
|             var now = DateTime.Now; | ||||
|             var ticks = now.Ticks; | ||||
|             this.mW = (uint) (ticks >> 16); | ||||
|             this.mZ = (uint) (ticks % 4_294_967_296); | ||||
|             this.StartProducerThreads(); | ||||
|         } | ||||
| 
 | ||||
|         public MultiThreadedRng(uint seedU) | ||||
|         { | ||||
|             this.mW = seedU; | ||||
|             this.mZ = 362_436_069; | ||||
|             this.StartProducerThreads(); | ||||
|         } | ||||
|          | ||||
|         public MultiThreadedRng(uint seedU, uint seedV) | ||||
|         { | ||||
|             this.mW = seedU; | ||||
|             this.mZ = seedV; | ||||
|             this.StartProducerThreads(); | ||||
|         } | ||||
| 
 | ||||
|         private void StartProducerThreads() | ||||
|         { | ||||
|             this.producerRandomUint = new Thread(() => this.RandomProducerUint(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|             this.producerRandomUint.Start(); | ||||
|             this.producerRandomUniformDistributedFloat = new Thread(() => this.RandomProducerUniformDistributedFloat(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|             this.producerRandomUniformDistributedFloat.Start(); | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
|         #region Producers | ||||
| 
 | ||||
|         [ExcludeFromCodeCoverage] | ||||
|         private async void RandomProducerUint(CancellationToken cancellationToken) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     // A local next buffer, which gets filled next: | ||||
|                     var nextBuffer = new uint[BUFFER_SIZE]; | ||||
|                      | ||||
|                     // Produce the necessary number of random uints: | ||||
|                     for (var n = 0; n < nextBuffer.Length && !cancellationToken.IsCancellationRequested; n++) | ||||
|                     { | ||||
|                         this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16); | ||||
|                         this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16); | ||||
|                         nextBuffer[n] = (this.mZ << 16) + this.mW; | ||||
|                     } | ||||
| 
 | ||||
|                     // Inside this loop, we try to enqueue the produced buffer: | ||||
|                     while (!cancellationToken.IsCancellationRequested) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             // Ensure, that we do not produce more buffers, as configured:  | ||||
|                             if (this.queueIntegers.Count < QUEUE_SIZE) | ||||
|                             { | ||||
|                                 this.queueIntegers.Enqueue(nextBuffer); | ||||
|                                 break; | ||||
|                             } | ||||
| 
 | ||||
|                             // The queue was full. Wait a moment and try it again: | ||||
|                             await Task.Delay(this.waiter, cancellationToken); | ||||
|                         } | ||||
|                         catch (TaskCanceledException) | ||||
|                         { | ||||
|                             // The producers should be stopped: | ||||
|                             return; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (OperationCanceledException) | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         [ExcludeFromCodeCoverage] | ||||
|         private async void RandomProducerUniformDistributedFloat(CancellationToken cancellationToken) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     // A local source buffer of uints:  | ||||
|                     uint[] bufferSource = null; | ||||
|                      | ||||
|                     // Try to get the next source buffer: | ||||
|                     while (!this.queueIntegers.TryDequeue(out bufferSource) && !cancellationToken.IsCancellationRequested) | ||||
|                         await Task.Delay(this.waiter, cancellationToken); | ||||
| 
 | ||||
|                     // Case: The producers should be stopped: | ||||
|                     if(bufferSource == null) | ||||
|                         return; | ||||
|                      | ||||
|                     // A local buffer to fill with uniform floats: | ||||
|                     var nextBuffer = new float[BUFFER_SIZE]; | ||||
|                      | ||||
|                     // Generate the necessary number of floats: | ||||
|                     for (var n = 0; n < nextBuffer.Length && !cancellationToken.IsCancellationRequested; n++) | ||||
|                         nextBuffer[n] = (bufferSource[n] + 1.0f) * 2.328306435454494e-10f; | ||||
| 
 | ||||
|                     // Inside this loop, we try to enqueue the generated buffer: | ||||
|                     while (!cancellationToken.IsCancellationRequested) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             // Ensure, that the queue contains only the configured number of buffers: | ||||
|                             if (this.queueFloats.Count < QUEUE_SIZE) | ||||
|                             { | ||||
|                                 this.queueFloats.Enqueue(nextBuffer); | ||||
|                                 break; | ||||
|                             } | ||||
| 
 | ||||
|                             // The queue was full. Wait a moment and try it again: | ||||
|                             await Task.Delay(this.waiter, cancellationToken); | ||||
|                         } | ||||
|                         catch (TaskCanceledException) | ||||
|                         { | ||||
|                             return; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (OperationCanceledException) | ||||
|             { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
|         #region Implementing interface | ||||
| 
 | ||||
|         public async ValueTask<float> GetUniform(CancellationToken cancel = default) | ||||
|         { | ||||
|             while (!cancel.IsCancellationRequested) | ||||
|             { | ||||
|                 // Check, if we need a new buffer to read from: | ||||
|                 if (this.currentBufferPointer >= BUFFER_SIZE) | ||||
|                 { | ||||
|                     // Create a local copy of the current buffer's pointer: | ||||
|                     var currentBufferReference = this.currentBuffer; | ||||
|                      | ||||
|                     // Here, we store the next buffer until we implement it: | ||||
|                     var nextBuffer = Array.Empty<float>(); | ||||
|                      | ||||
|                     // Try to get the next buffer from the queue: | ||||
|                     while (this.currentBufferPointer >= BUFFER_SIZE && currentBufferReference == this.currentBuffer && !this.queueFloats.TryDequeue(out nextBuffer)) | ||||
|                     { | ||||
|                         // | ||||
|                         // Case: There is no next buffer available. | ||||
|                         // Must wait for producer(s) to provide next. | ||||
|                         // | ||||
|                         try | ||||
|                         { | ||||
|                             await Task.Delay(this.waiter, cancel); | ||||
|                         } | ||||
|                         catch (TaskCanceledException) | ||||
|                         { | ||||
|                             // | ||||
|                             // Case: The consumer cancelled the request. | ||||
|                             // | ||||
|                             return float.NaN; | ||||
|                         } | ||||
|                     } | ||||
|                  | ||||
|                     // | ||||
|                     // Note: In general, it does not matter if the following compare-exchange is successful. | ||||
|                     // 1st case: It was successful -- everything is fine. But we are responsible to re-set the currentBufferPointer. | ||||
|                     // 2nd case: It was not successful. This means, that another thread was successful, though. | ||||
|                     //           That case is fine as well. But we would loose one buffer of work. Thus, we | ||||
|                     //           check for this case and preserve the buffer full of work. | ||||
|                     // | ||||
|                      | ||||
|                     // Try to implement the dequeued buffer without locking other threads: | ||||
|                     if (Interlocked.CompareExchange(ref this.currentBuffer, nextBuffer, currentBufferReference) != currentBufferReference) | ||||
|                     { | ||||
|                         // | ||||
|                         // Case: Another thread updated the buffer already. | ||||
|                         // Thus, we enqueue our copy of the next buffer to preserve it. | ||||
|                         // | ||||
|                         this.queueFloats.Enqueue(nextBuffer); | ||||
|                          | ||||
|                         // Next? We can go ahead and yield a random number... | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         // | ||||
|                         // Case: We updated the buffer. | ||||
|                         // | ||||
|                         this.currentBufferPointer = 0; | ||||
|                          | ||||
|                         // Next? We can go ahead and yield a random number... | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Made a local copy of the current pointer: | ||||
|                 var myPointer = this.currentBufferPointer; | ||||
|                  | ||||
|                 // Increment the pointer for the next thread or call: | ||||
|                 var nextPointer = myPointer + 1; | ||||
|                  | ||||
|                 // Try to update the pointer without locking other threads: | ||||
|                 if (Interlocked.CompareExchange(ref this.currentBufferPointer, nextPointer, myPointer) == myPointer) | ||||
|                 { | ||||
|                     // | ||||
|                     // Case: Success. We updated the pointer and, thus, can use the pointer to read a number. | ||||
|                     // | ||||
|                     return this.currentBuffer[myPointer]; | ||||
|                 } | ||||
|                  | ||||
|                 // | ||||
|                 // Case: Another thread updated the pointer already. Must restart the process | ||||
|                 // to get a random number. | ||||
|                 // | ||||
|             } | ||||
| 
 | ||||
|             // | ||||
|             // Case: The consumer cancelled the request. | ||||
|             // | ||||
|             return float.NaN; | ||||
|         } | ||||
|          | ||||
|         private void StopProducer() => this.producerTokenSource.Cancel(); | ||||
| 
 | ||||
|         public void Dispose() => this.StopProducer(); | ||||
| 
 | ||||
|         #endregion | ||||
|     } | ||||
| } | ||||
							
								
								
									
										67
									
								
								FastRng/Float/ShapeFitter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								FastRng/Float/ShapeFitter.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng.Float.Distributions; | ||||
| 
 | ||||
| namespace FastRng.Float | ||||
| { | ||||
|     /// <summary> | ||||
|     /// ShapeFitter is a rejection sampler, cf. https://en.wikipedia.org/wiki/Rejection_sampling | ||||
|     /// </summary> | ||||
|     public sealed class ShapeFitter | ||||
|     { | ||||
|         private readonly float[] probabilities; | ||||
|         private readonly IRandom rng; | ||||
|         private readonly float max; | ||||
|         private readonly float sampleSize; | ||||
|         private readonly IDistribution uniform; | ||||
| 
 | ||||
|         public ShapeFitter(Func<float, float> shapeFunction, IRandom rng, ushort sampleSize = 50) | ||||
|         { | ||||
|             this.rng = rng; | ||||
|             this.uniform = new Uniform(rng); | ||||
|             this.sampleSize = sampleSize; | ||||
|             this.probabilities = new float[sampleSize]; | ||||
| 
 | ||||
|             var sampleStepSize = 1.0f / sampleSize; | ||||
|             var nextStep = 0.0f + sampleStepSize; | ||||
|             var maxValue = 0.0f; | ||||
|             for (var n = 0; n < sampleSize; n++) | ||||
|             { | ||||
|                 this.probabilities[n] = shapeFunction(nextStep); | ||||
|                 if (this.probabilities[n] > maxValue) | ||||
|                     maxValue = this.probabilities[n]; | ||||
|                  | ||||
|                 nextStep += sampleStepSize; | ||||
|             } | ||||
| 
 | ||||
|             this.max = maxValue; | ||||
|         } | ||||
| 
 | ||||
|         public async ValueTask<float> NextNumber(CancellationToken token = default) | ||||
|         { | ||||
|             while (!token.IsCancellationRequested) | ||||
|             { | ||||
|                 var x = await this.rng.GetUniform(token); | ||||
|                 if (float.IsNaN(x)) | ||||
|                     return x; | ||||
|                  | ||||
|                 var nextBucket = (int)MathF.Floor(x * this.sampleSize); | ||||
|                 if (nextBucket >= this.probabilities.Length) | ||||
|                     nextBucket = this.probabilities.Length - 1; | ||||
|                  | ||||
|                 var threshold = this.probabilities[nextBucket]; | ||||
|                 var y = await this.uniform.NextNumber(0.0f, this.max, token); | ||||
|                 if (float.IsNaN(y)) | ||||
|                     return y; | ||||
|                  | ||||
|                 if(y > threshold) | ||||
|                     continue; | ||||
| 
 | ||||
|                 return x; | ||||
|             } | ||||
| 
 | ||||
|             return float.NaN; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,46 +0,0 @@ | ||||
| using System; | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Interface for random number generators. | ||||
| /// </summary> | ||||
| public interface IRandom<TNum> : IDisposable where TNum : IFloatingPointIeee754<TNum> | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Returns a uniform distributed pseudo-random number from the interval (0,1]. | ||||
|     /// This means, the result 0 is impossible, whereas 1 is possible. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// This method is thread-safe. You can consume numbers from the same generator | ||||
|     /// by using multiple threads at the same time.  | ||||
|     /// </remarks> | ||||
|     public TNum GetUniform(CancellationToken cancel = default); | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Get a uniform distributed pseudo-random number from the interval [0, max). | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// This method is thread-safe. You can consume numbers from the same generator | ||||
|     /// by using multiple threads at the same time.  | ||||
|     /// </remarks> | ||||
|     /// <param name="max">The maximum value (exclusive). The max value returned will be max - 1.</param> | ||||
|     /// <param name="cancel">The cancellation token.</param> | ||||
|     /// <returns>A pseudo-random number from the interval [0, max).</returns> | ||||
|     public int GetUniformInt(int max, CancellationToken cancel = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Get a uniform distributed pseudo-random number from the interval [min, max). | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// This method is thread-safe. You can consume numbers from the same generator | ||||
|     /// by using multiple threads at the same time.  | ||||
|     /// </remarks> | ||||
|     /// <param name="min">The minimum value (inclusive).</param> | ||||
|     /// <param name="max">The maximum value (exclusive). The max value returned will be max - 1.</param> | ||||
|     /// <param name="cancel">The cancellation token.</param> | ||||
|     /// <returns>A pseudo-random number from the interval [min, max).</returns>  | ||||
|     public int GetUniformInt(int min, int max, CancellationToken cancel = default); | ||||
| } | ||||
| @ -1,62 +0,0 @@ | ||||
| using System.Numerics; | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Provides some mathematical function, which are not available within in .NET itself. | ||||
| /// </summary> | ||||
| public static class MathToolsFloatingPoint<TNum> where TNum : IFloatingPointIeee754<TNum>, IAdditionOperators<TNum, TNum, TNum> | ||||
| { | ||||
|     private static readonly TNum SQRT_2 = TNum.Sqrt(TNum.One + TNum.One); | ||||
|     private static readonly TNum SQRT_PI = TNum.Sqrt(TNum.Pi); | ||||
|      | ||||
|     // Source: http://rosettacode.org/wiki/Gamma_function#Go | ||||
|     private static readonly TNum F1 = TNum.CreateChecked(6.5f); | ||||
|     private static readonly TNum A1 = TNum.CreateChecked(.99999999999980993f); | ||||
|     private static readonly TNum A2 = TNum.CreateChecked(676.5203681218851f); | ||||
|     private static readonly TNum A3 = TNum.CreateChecked(1259.1392167224028f); | ||||
|     private static readonly TNum A4 = TNum.CreateChecked(771.32342877765313f); | ||||
|     private static readonly TNum A5 = TNum.CreateChecked(176.61502916214059f); | ||||
|     private static readonly TNum A6 = TNum.CreateChecked(12.507343278686905f); | ||||
|     private static readonly TNum A7 = TNum.CreateChecked(.13857109526572012f); | ||||
|     private static readonly TNum A8 = TNum.CreateChecked(9.9843695780195716e-6f); | ||||
|     private static readonly TNum A9 = TNum.CreateChecked(1.5056327351493116e-7f); | ||||
| 
 | ||||
|     private static readonly TNum CONST1 = TNum.One; | ||||
|     private static readonly TNum CONST2 = CONST1 + TNum.One; | ||||
|     private static readonly TNum CONST3 = CONST2 + TNum.One; | ||||
|     private static readonly TNum CONST4 = CONST3 + TNum.One; | ||||
|     private static readonly TNum CONST5 = CONST4 + TNum.One; | ||||
|     private static readonly TNum CONST6 = CONST5 + TNum.One; | ||||
|     private static readonly TNum CONST7 = CONST6 + TNum.One; | ||||
|      | ||||
|     private static readonly TNum CONST_HALF = TNum.CreateChecked(0.5f); | ||||
|          | ||||
|     /// <summary> | ||||
|     /// The mathematical gamma function. | ||||
|     /// </summary> | ||||
|     /// <param name="z">The value for which you want calculate gamma.</param> | ||||
|     public static TNum Gamma(TNum z) | ||||
|     { | ||||
|         // Source: http://rosettacode.org/wiki/Gamma_function#Go | ||||
| 
 | ||||
|         var t = z + F1; | ||||
|         var x =  A1 + | ||||
|                  A2 / z - | ||||
|                  A3 / (z + CONST1) + | ||||
|                  A4 / (z + CONST2) - | ||||
|                  A5 / (z + CONST3) + | ||||
|                  A6 / (z + CONST4) - | ||||
|                  A7 / (z + CONST5) + | ||||
|                  A8 / (z + CONST6) + | ||||
|                  A9 / (z + CONST7); | ||||
| 
 | ||||
|         return SQRT_2 * SQRT_PI * TNum.Pow(t, z - CONST_HALF) * TNum.Exp(-t) * x; | ||||
|     } | ||||
|          | ||||
|     /// <summary> | ||||
|     /// The mathematical factorial function for floating-point numbers. | ||||
|     /// </summary> | ||||
|     /// <param name="x">The value, for which you want to know the factorial.</param> | ||||
|     public static TNum Factorial(TNum x) => Gamma(x + CONST1); | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Provides some mathematical function, which are not available within in .NET itself. | ||||
| /// </summary> | ||||
| public static class MathToolsInteger | ||||
| { | ||||
|     /// <summary> | ||||
|     /// The mathematical factorial function for integer numbers. | ||||
|     /// </summary> | ||||
|     /// <param name="x">The value, for which you want to know the factorial.</param> | ||||
|     /// <exception cref="ArgumentOutOfRangeException">Throws, when x is greater than 20. Due to limitations of 64bit ulong type.</exception> | ||||
|     public static ulong Factorial(uint x) | ||||
|     { | ||||
|         if (x > 20) | ||||
|             throw new ArgumentOutOfRangeException(nameof(x), $"Cannot compute {x}!, since ulong.max is 18_446_744_073_709_551_615."); | ||||
| 
 | ||||
|         ulong accumulator = 1; | ||||
|         for (uint factor = 1; factor <= x; factor++) | ||||
|             accumulator *= factor; | ||||
| 
 | ||||
|         return accumulator; | ||||
|     } | ||||
| } | ||||
| @ -1,185 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| using System.Threading.Channels; | ||||
| 
 | ||||
| // ReSharper disable RedundantExtendsListEntry | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| public sealed class MultiChannelRng<TNum> : IRandom<TNum>, IDisposable where TNum : IFloatingPointIeee754<TNum>, IAdditionOperators<TNum, TNum, TNum> | ||||
| { | ||||
| #if DEBUG | ||||
|     private const int BUFFER_SIZE = 1_000_000; | ||||
| #else | ||||
|     private const int BUFFER_SIZE = 1_000_000; | ||||
| #endif | ||||
|      | ||||
|     private readonly Channel<uint> channelIntegers = Channel.CreateBounded<uint>(new BoundedChannelOptions(capacity: BUFFER_SIZE * 2) { FullMode = BoundedChannelFullMode.Wait, SingleWriter = true, SingleReader = true }); | ||||
|     private readonly Channel<TNum> channelFloats = Channel.CreateBounded<TNum>(new BoundedChannelOptions(capacity: BUFFER_SIZE) { FullMode = BoundedChannelFullMode.Wait, SingleWriter = true, SingleReader = false }); | ||||
|      | ||||
|     private static readonly TNum CONST_FLOAT_CONVERSION = TNum.CreateChecked(2.328306435454494e-10f); | ||||
|      | ||||
|     // Gets used to stop the producer threads: | ||||
|     private readonly CancellationTokenSource producerTokenSource = new(); | ||||
|      | ||||
|     // The uint producer thread: | ||||
|     private Thread producerRandomUint; | ||||
|          | ||||
|     // The uniform float producer thread: | ||||
|     private Thread producerRandomUniformDistributedFloat; | ||||
|          | ||||
|     // Variable w and z for the uint generator. Both get used | ||||
|     // as seeding variable as well (cf. constructors) | ||||
|     private uint mW; | ||||
|     private uint mZ; | ||||
|      | ||||
|     #region Constructors | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a multithreaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// This constructor uses the user's current local time | ||||
|     /// to derive the necessary parameters for the generator. | ||||
|     /// Thus, the results depend on the time when the generator was created. | ||||
|     /// </remarks> | ||||
|     public MultiChannelRng() | ||||
|     { | ||||
|         // | ||||
|         // Initialize the mW and mZ by using | ||||
|         // the system's time. | ||||
|         // | ||||
|         var now = DateTime.Now; | ||||
|         var ticks = now.Ticks; | ||||
|         this.mW = (uint) (ticks >> 16); | ||||
|         this.mZ = (uint) (ticks % 4_294_967_296); | ||||
|         this.StartProducerThreads(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a multithreaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// A multi-threaded random number generator created by this constructor is | ||||
|     /// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/> | ||||
|     /// | ||||
|     /// <b>Please note:</b> Although the number generator and all distributions are deterministic, | ||||
|     /// the behavior of the consuming application might be non-deterministic. This is possible if | ||||
|     /// the application with multiple threads consumes the numbers. The scheduling of the threads | ||||
|     /// is up to the operating system and might not be predictable.  | ||||
|     /// </remarks> | ||||
|     /// <param name="seedU">A seed value to generate a deterministic generator.</param> | ||||
|     public MultiChannelRng(uint seedU) | ||||
|     { | ||||
|         this.mW = seedU; | ||||
|         this.mZ = 362_436_069; | ||||
|         this.StartProducerThreads(); | ||||
|     } | ||||
|          | ||||
|     /// <summary> | ||||
|     /// Creates a multi-threaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// A multi-threaded random number generator created by this constructor is | ||||
|     /// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/> | ||||
|     /// | ||||
|     /// <b>Please note:</b> Although the number generator and all distributions are deterministic, | ||||
|     /// the behavior of the consuming application might be non-deterministic. This is possible if | ||||
|     /// the application with multiple threads consumes the numbers. The scheduling of the threads | ||||
|     /// is up to the operating system and might not be predictable.  | ||||
|     /// </remarks> | ||||
|     /// <param name="seedU">The first seed value.</param> | ||||
|     /// <param name="seedV">The second seed value.</param> | ||||
|     public MultiChannelRng(uint seedU, uint seedV) | ||||
|     { | ||||
|         this.mW = seedU; | ||||
|         this.mZ = seedV; | ||||
|         this.StartProducerThreads(); | ||||
|     } | ||||
| 
 | ||||
|     private void StartProducerThreads() | ||||
|     { | ||||
|         this.producerRandomUint = new Thread(() => this.RandomProducerUint(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|         this.producerRandomUint.Start(); | ||||
|         this.producerRandomUniformDistributedFloat = new Thread(() => this.RandomProducerUniformDistributedFloat(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|         this.producerRandomUniformDistributedFloat.Start(); | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
|      | ||||
|     #region Producers | ||||
| 
 | ||||
|     [ExcludeFromCodeCoverage] | ||||
|     private async void RandomProducerUint(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             while (!cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16); | ||||
|                 this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16); | ||||
|                 await this.channelIntegers.Writer.WriteAsync((this.mZ << 16) + this.mW, cancellationToken); | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
|          | ||||
|     [ExcludeFromCodeCoverage] | ||||
|     private async void RandomProducerUniformDistributedFloat(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             while (!cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 await this.channelFloats.Writer.WriteAsync((TNum.CreateChecked(await this.channelIntegers.Reader.ReadAsync(cancellationToken)) + TNum.One) * CONST_FLOAT_CONVERSION, cancellationToken); | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
|      | ||||
|     #region Implementing interfaces | ||||
|      | ||||
|     #region Implementation of IDisposable | ||||
| 
 | ||||
|     private void StopProducer() => this.producerTokenSource.Cancel(); | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public void Dispose() => this.StopProducer(); | ||||
| 
 | ||||
|     #endregion | ||||
| 
 | ||||
|     #region Implementation of IRandom<TNum> | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public TNum GetUniform(CancellationToken cancel = default) | ||||
|     { | ||||
|         var valueTask = this.channelFloats.Reader.ReadAsync(cancel); | ||||
|         return valueTask.AsTask().Result; | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public int GetUniformInt(int max, CancellationToken cancel = default) | ||||
|     { | ||||
|         var valueTask = this.channelIntegers.Reader.ReadAsync(cancel); | ||||
|         return (int) (valueTask.AsTask().Result % (uint) max); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public int GetUniformInt(int min, int max, CancellationToken cancel = default) | ||||
|     { | ||||
|         var valueTask = this.channelIntegers.Reader.ReadAsync(cancel); | ||||
|         return (int) (valueTask.AsTask().Result % (uint) (max - min) + (uint) min); | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
|      | ||||
|     #endregion | ||||
| } | ||||
| @ -1,346 +0,0 @@ | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| // ReSharper disable RedundantExtendsListEntry | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// A fast multi-threaded pseudo random number generator. | ||||
| /// </summary> | ||||
| /// <remarks> | ||||
| /// Please note, that Math.NET's (https://www.mathdotnet.com/) random number generator is in some situations faster. | ||||
| /// Unlike Math.NET, MultiThreadedRng is multithreaded. Consumers can use a token to cancel an operation.<br/><br/> | ||||
| /// | ||||
| /// MultiThreadedRng using a shape fitter (a rejection sampler) to enforce arbitrary shapes of probabilities for | ||||
| /// desired distributions. By using the shape fitter, it is even easier to define discontinuous, arbitrary functions | ||||
| /// as shapes. Any consumer can define and use own distributions.<br/><br/> | ||||
| ///  | ||||
| /// This class uses the George Marsaglia's MWC algorithm. The algorithm's implementation is based loosely on John D. | ||||
| /// Cook's (johndcook.com) implementation (https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation). | ||||
| /// Thanks, John, for the inspiration.<br/><br/> | ||||
| /// </remarks> | ||||
| public sealed class MultiThreadedRng<TNum> : IRandom<TNum>, IDisposable where TNum : IFloatingPointIeee754<TNum>, IAdditionOperators<TNum, TNum, TNum> | ||||
| { | ||||
| #if DEBUG | ||||
|     private const int BUFFER_SIZE = 1_000_000; | ||||
| #else | ||||
|     private const int BUFFER_SIZE = 1_000_000; | ||||
| #endif | ||||
| 
 | ||||
|     // The queue size means, how many buffers we store in a queue at the same time: | ||||
|     private const int QUEUE_SIZE_FLOAT = 2; | ||||
|      | ||||
|     // The queue size means, how many buffers we store in a queue at the same time: | ||||
|     private const int QUEUE_SIZE_INT = QUEUE_SIZE_FLOAT * 2; | ||||
| 
 | ||||
|     private static readonly TNum CONST_FLOAT_CONVERSION = TNum.CreateChecked(2.328306435454494e-10f); | ||||
|      | ||||
|     // ReSharper disable StaticMemberInGenericType | ||||
|     private static readonly object LOCKER = new(); | ||||
|     // ReSharper restore StaticMemberInGenericType | ||||
|          | ||||
|     // Gets used to stop the producer threads: | ||||
|     private readonly CancellationTokenSource producerTokenSource = new(); | ||||
|          | ||||
|     // The time a thread waits e.g. to check if the queue needs a new buffer: | ||||
|     private readonly TimeSpan waiter = TimeSpan.FromMilliseconds(10); | ||||
|          | ||||
|     // The first queue, where to store buffers of random uint numbers: | ||||
|     private readonly ConcurrentQueue<uint[]> queueIntegers = new(); | ||||
|          | ||||
|     // The second queue, where to store buffers of uniform random floating point numbers: | ||||
|     private readonly ConcurrentQueue<TNum[]> queueFloats = new(); | ||||
| 
 | ||||
|     // The uint producer thread: | ||||
|     private Thread producerRandomUint; | ||||
|          | ||||
|     // The uniform float producer thread: | ||||
|     private Thread producerRandomUniformDistributedFloat; | ||||
|      | ||||
|     private readonly UIntChannelProducer independentUIntProducer = new(); | ||||
|          | ||||
|     // Variable w and z for the uint generator. Both get used | ||||
|     // as seeding variable as well (cf. constructors) | ||||
|     private uint mW; | ||||
|     private uint mZ; | ||||
|          | ||||
|     // This is the current buffer for the consumer side, i.e., the public interfaces: | ||||
|     private TNum[] currentBuffer = []; | ||||
|          | ||||
|     // The current pointer to the next current buffer's address to read from: | ||||
|     private int currentBufferPointer = BUFFER_SIZE; | ||||
| 
 | ||||
|     #region Constructors | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a multi-threaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// This constructor uses the user's current local time to derive the necessary parameters for the generator. | ||||
|     /// Thus, the results depend on the time when the generator was created. | ||||
|     /// </remarks> | ||||
|     public MultiThreadedRng() | ||||
|     { | ||||
|         // | ||||
|         // Initialize the mW and mZ by using | ||||
|         // the system's time. | ||||
|         // | ||||
|         var now = DateTime.Now; | ||||
|         var ticks = now.Ticks; | ||||
|         this.mW = (uint) (ticks >> 16); | ||||
|         this.mZ = (uint) (ticks % 4_294_967_296); | ||||
|         this.StartProducerThreads(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a multi-threaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// A multi-threaded random number generator created by this constructor is | ||||
|     /// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/> | ||||
|     /// | ||||
|     /// <b>Please note:</b> Although the number generator and all distributions are deterministic, | ||||
|     /// the behavior of the consuming application might be non-deterministic. This is possible if | ||||
|     /// the application with multiple threads consumes the numbers. The scheduling of the threads | ||||
|     /// is up to the operating system and might not be predictable.  | ||||
|     /// </remarks> | ||||
|     /// <param name="seedU">A seed value to generate a deterministic generator.</param> | ||||
|     public MultiThreadedRng(uint seedU) | ||||
|     { | ||||
|         this.mW = seedU; | ||||
|         this.mZ = 362_436_069; | ||||
|         this.StartProducerThreads(); | ||||
|     } | ||||
|          | ||||
|     /// <summary> | ||||
|     /// Creates a multi-threaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// A multi-threaded random number generator created by this constructor is | ||||
|     /// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/> | ||||
|     /// | ||||
|     /// <b>Please note:</b> Although the number generator and all distributions are deterministic, | ||||
|     /// the behavior of the consuming application might be non-deterministic. This is possible if | ||||
|     /// the application with multiple threads consumes the numbers. The scheduling of the threads | ||||
|     /// is up to the operating system and might not be predictable.  | ||||
|     /// </remarks> | ||||
|     /// <param name="seedU">The first seed value.</param> | ||||
|     /// <param name="seedV">The second seed value.</param> | ||||
|     public MultiThreadedRng(uint seedU, uint seedV) | ||||
|     { | ||||
|         this.mW = seedU; | ||||
|         this.mZ = seedV; | ||||
|         this.StartProducerThreads(); | ||||
|     } | ||||
| 
 | ||||
|     private void StartProducerThreads() | ||||
|     { | ||||
|         this.producerRandomUint = new Thread(() => this.RandomProducerUint(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|         this.producerRandomUint.Start(); | ||||
|         this.producerRandomUniformDistributedFloat = new Thread(() => this.RandomProducerUniformDistributedFloat(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|         this.producerRandomUniformDistributedFloat.Start(); | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
| 
 | ||||
|     #region Producers | ||||
| 
 | ||||
|     [ExcludeFromCodeCoverage] | ||||
|     private async void RandomProducerUint(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             while (!cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 // A local next buffer, which gets filled next: | ||||
|                 var nextBuffer = new uint[BUFFER_SIZE]; | ||||
|                      | ||||
|                 // Produce the necessary number of random uints: | ||||
|                 for (var n = 0; n < nextBuffer.Length && !cancellationToken.IsCancellationRequested; n++) | ||||
|                 { | ||||
|                     this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16); | ||||
|                     this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16); | ||||
|                     nextBuffer[n] = (this.mZ << 16) + this.mW; | ||||
|                 } | ||||
| 
 | ||||
|                 // Inside this loop, we try to enqueue the produced buffer: | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         // Ensure that we do not produce more buffers, as configured:  | ||||
|                         if (this.queueIntegers.Count < QUEUE_SIZE_INT) | ||||
|                         { | ||||
|                             this.queueIntegers.Enqueue(nextBuffer); | ||||
|                             break; | ||||
|                         } | ||||
| 
 | ||||
|                         // The queue was full. Wait a moment and try it again: | ||||
|                         await Task.Delay(this.waiter, cancellationToken); | ||||
|                     } | ||||
|                     catch (TaskCanceledException) | ||||
|                     { | ||||
|                         // The producers should be stopped: | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
|          | ||||
|     [ExcludeFromCodeCoverage] | ||||
|     private async void RandomProducerUniformDistributedFloat(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             while (!cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 // A local source buffer of uints:  | ||||
|                 uint[] bufferSource; | ||||
|                      | ||||
|                 // Try to get the next source buffer: | ||||
|                 while (!this.queueIntegers.TryDequeue(out bufferSource) && !cancellationToken.IsCancellationRequested) | ||||
|                     await Task.Delay(this.waiter, cancellationToken); | ||||
| 
 | ||||
|                 // Case: The producers should be stopped: | ||||
|                 if(bufferSource == null) | ||||
|                     return; | ||||
|                      | ||||
|                 // A local buffer to fill with uniform floats: | ||||
|                 var nextBuffer = new TNum[BUFFER_SIZE]; | ||||
|                      | ||||
|                 // Generate the necessary number of floats: | ||||
|                 for (var n = 0; n < nextBuffer.Length && !cancellationToken.IsCancellationRequested; n++) | ||||
|                     nextBuffer[n] = (TNum.CreateChecked(bufferSource[n]) + TNum.One) * CONST_FLOAT_CONVERSION; | ||||
| 
 | ||||
|                 // Inside this loop, we try to enqueue the generated buffer: | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         // Ensure that the queue contains only the configured number of buffers: | ||||
|                         if (this.queueFloats.Count < QUEUE_SIZE_FLOAT) | ||||
|                         { | ||||
|                             this.queueFloats.Enqueue(nextBuffer); | ||||
|                             break; | ||||
|                         } | ||||
| 
 | ||||
|                         // The queue was full. Wait a moment and try it again: | ||||
|                         await Task.Delay(this.waiter, cancellationToken); | ||||
|                     } | ||||
|                     catch (TaskCanceledException) | ||||
|                     { | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
| 
 | ||||
|     #region Implementing interface | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Returns a uniform distributed pseudo-random number from the interval (0,1]. | ||||
|     /// This means, the result 0 is impossible, whereas 1 is possible. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// This method is thread-safe. You can consume numbers from the same generator | ||||
|     /// by using multiple threads at the same time.  | ||||
|     /// </remarks> | ||||
|     public TNum GetUniform(CancellationToken cancel = default) | ||||
|     { | ||||
|         if (cancel.IsCancellationRequested) | ||||
|             return TNum.NaN; | ||||
|          | ||||
|         Start: | ||||
|          | ||||
|         // Check if we have to load the next buffer: | ||||
|         if (this.currentBufferPointer >= BUFFER_SIZE) | ||||
|         { | ||||
|             // We have to get the next buffer from the queue. This is a critical | ||||
|             // section, because we have to ensure, that only one thread at a time | ||||
|             // can get the next buffer: | ||||
|             lock (LOCKER) | ||||
|             { | ||||
|                 // We might not the first thread, which has to get the next buffer. | ||||
|                 // When some other thread has already got the next buffer, the pointer | ||||
|                 // was already reset to zero. In this case, we start over again: | ||||
|                 if(this.currentBufferPointer < BUFFER_SIZE) | ||||
|                     goto Start; | ||||
|                  | ||||
|                 while (!this.queueFloats.TryDequeue(out this.currentBuffer)) | ||||
|                 { | ||||
|                     if (cancel.IsCancellationRequested) | ||||
|                         return TNum.NaN; | ||||
|                  | ||||
|                     Thread.Sleep(TimeSpan.FromMilliseconds(6)); | ||||
|                 } | ||||
|                  | ||||
|                 // Reset the pointer for the next thread or call: | ||||
|                 this.currentBufferPointer = 0; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // Made a local copy of the current pointer: | ||||
|         var myPointer = this.currentBufferPointer; | ||||
|          | ||||
|         // Issue #8: This might happen when another thread interrupted the current thread, and | ||||
|         // the other thread has already updated the pointer. In this case, we start over again. | ||||
|         if (myPointer >= BUFFER_SIZE) | ||||
|             goto Start; | ||||
|          | ||||
|         // Increment the pointer for the next thread or call: | ||||
|         var nextPointer = myPointer + 1; | ||||
|          | ||||
|         // Try to update the pointer without locking other threads: | ||||
|         if (Interlocked.CompareExchange(ref this.currentBufferPointer, nextPointer, myPointer) != myPointer) | ||||
|             goto Start; | ||||
| 
 | ||||
|         // | ||||
|         // Case: Success. We updated the pointer and, thus, can use it to read the number. | ||||
|         // | ||||
|         return this.currentBuffer[myPointer]; | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public int GetUniformInt(int max, CancellationToken cancel = default) | ||||
|     { | ||||
|         var valueTask = this.independentUIntProducer.GetNextAsync(cancel); | ||||
|         return (int) (valueTask.AsTask().Result % (uint) max); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public int GetUniformInt(int min, int max, CancellationToken cancel = default) | ||||
|     { | ||||
|         var valueTask = this.independentUIntProducer.GetNextAsync(cancel); | ||||
|         return (int) (valueTask.AsTask().Result % (uint) (max - min) + (uint) min); | ||||
|     } | ||||
|          | ||||
|     private void StopProducer() => this.producerTokenSource.Cancel(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Disposes this generator. It is important to dispose a generator, | ||||
|     /// when it is no longer needed. Otherwise, the background threads | ||||
|     /// are still running. | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         this.independentUIntProducer.Dispose(); | ||||
|         this.StopProducer(); | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
| } | ||||
| @ -1,77 +0,0 @@ | ||||
| using System; | ||||
| using System.Numerics; | ||||
| using System.Threading; | ||||
| using FastRng.Distributions; | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// ShapeFitter is a rejection sampler, cf. https://en.wikipedia.org/wiki/Rejection_sampling | ||||
| /// </summary> | ||||
| public sealed class ShapeFitter<TNum> where TNum : IFloatingPointIeee754<TNum>, IDivisionOperators<TNum, TNum, TNum> | ||||
| { | ||||
|     private readonly TNum[] probabilities; | ||||
|     private readonly IRandom<TNum> rng; | ||||
|     private readonly TNum max; | ||||
|     private readonly TNum sampleSize; | ||||
|     private readonly IDistribution<TNum> uniform; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a shape fitter instance. | ||||
|     /// </summary> | ||||
|     /// <param name="shapeFunction">The function which describes the desired shape.</param> | ||||
|     /// <param name="rng">The random number generator instance to use.</param> | ||||
|     /// <param name="sampleSize">The number of sampling steps to sample the given function.</param> | ||||
|     public ShapeFitter(Func<TNum, TNum> shapeFunction, IRandom<TNum> rng, ushort sampleSize = 50) | ||||
|     { | ||||
|         this.rng = rng; | ||||
|         this.uniform = new Uniform<TNum>(rng); | ||||
|         this.sampleSize = TNum.CreateChecked(sampleSize); | ||||
|         this.probabilities = new TNum[sampleSize]; | ||||
| 
 | ||||
|         var sampleStepSize = TNum.One / TNum.CreateChecked(sampleSize); | ||||
|         var nextStep = TNum.Zero + sampleStepSize; | ||||
|         var maxValue = TNum.Zero; | ||||
|         for (var n = 0; n < sampleSize; n++) | ||||
|         { | ||||
|             this.probabilities[n] = shapeFunction(nextStep); | ||||
|             if (this.probabilities[n] > maxValue) | ||||
|                 maxValue = this.probabilities[n]; | ||||
|                  | ||||
|             nextStep += sampleStepSize; | ||||
|         } | ||||
| 
 | ||||
|         this.max = maxValue; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Returns a random number regarding the given shape. | ||||
|     /// </summary> | ||||
|     /// <param name="token">An optional cancellation token.</param> | ||||
|     /// <returns>The next value regarding the given shape.</returns> | ||||
|     public TNum NextNumber(CancellationToken token = default) | ||||
|     { | ||||
|         while (!token.IsCancellationRequested) | ||||
|         { | ||||
|             var x = this.rng.GetUniform(token); | ||||
|             if (TNum.IsNaN(x)) | ||||
|                 return x; | ||||
|                  | ||||
|             var nextBucket = int.CreateChecked(TNum.Floor(x * this.sampleSize)); | ||||
|             if (nextBucket >= this.probabilities.Length) | ||||
|                 nextBucket = this.probabilities.Length - 1; | ||||
|                  | ||||
|             var threshold = this.probabilities[nextBucket]; | ||||
|             var y = this.uniform.NextNumber(TNum.Zero, this.max, token); | ||||
|             if (TNum.IsNaN(y)) | ||||
|                 return y; | ||||
|                  | ||||
|             if(y > threshold) | ||||
|                 continue; | ||||
| 
 | ||||
|             return x; | ||||
|         } | ||||
| 
 | ||||
|         return TNum.NaN; | ||||
|     } | ||||
| } | ||||
| @ -1,121 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Threading; | ||||
| using System.Threading.Channels; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace FastRng; | ||||
| 
 | ||||
| internal sealed class UIntChannelProducer : IDisposable | ||||
| { | ||||
| #if DEBUG | ||||
|     private const int BUFFER_SIZE = 1_000_000; | ||||
| #else | ||||
|     private const int BUFFER_SIZE = 1_000_000; | ||||
| #endif | ||||
|      | ||||
|     private readonly Channel<uint> channelIntegers = Channel.CreateBounded<uint>(new BoundedChannelOptions(capacity: BUFFER_SIZE * 2) { FullMode = BoundedChannelFullMode.Wait, SingleWriter = true, SingleReader = true }); | ||||
|      | ||||
|     // Gets used to stop the producer thread: | ||||
|     private readonly CancellationTokenSource producerTokenSource = new(); | ||||
|      | ||||
|     // The uint producer thread: | ||||
|     private Thread producerRandomUint; | ||||
|      | ||||
|     // Variable w and z for the uint generator. Both get used | ||||
|     // as seeding variable as well (cf. constructors) | ||||
|     private uint mW; | ||||
|     private uint mZ; | ||||
|      | ||||
|     #region Constructors | ||||
|      | ||||
|     public UIntChannelProducer() | ||||
|     { | ||||
|         // | ||||
|         // Initialize the mW and mZ by using | ||||
|         // the system's time. | ||||
|         // | ||||
|         var now = DateTime.Now; | ||||
|         var ticks = now.Ticks; | ||||
|         this.mW = (uint) (ticks >> 16); | ||||
|         this.mZ = (uint) (ticks % 4_294_967_296); | ||||
|         this.StartProducerThread(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a multithreaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// A multi-threaded random number generator created by this constructor is | ||||
|     /// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/> | ||||
|     /// | ||||
|     /// <b>Please note:</b> Although the number generator and all distributions are deterministic, | ||||
|     /// the behavior of the consuming application might be non-deterministic. This is possible if | ||||
|     /// the application with multiple threads consumes the numbers. The scheduling of the threads | ||||
|     /// is up to the operating system and might not be predictable.  | ||||
|     /// </remarks> | ||||
|     /// <param name="seedU">A seed value to generate a deterministic generator.</param> | ||||
|     public UIntChannelProducer(uint seedU) | ||||
|     { | ||||
|         this.mW = seedU; | ||||
|         this.mZ = 362_436_069; | ||||
|         this.StartProducerThread(); | ||||
|     } | ||||
|          | ||||
|     /// <summary> | ||||
|     /// Creates a multi-threaded random number generator. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// A multi-threaded random number generator created by this constructor is | ||||
|     /// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/> | ||||
|     /// | ||||
|     /// <b>Please note:</b> Although the number generator and all distributions are deterministic, | ||||
|     /// the behavior of the consuming application might be non-deterministic. This is possible if | ||||
|     /// the application with multiple threads consumes the numbers. The scheduling of the threads | ||||
|     /// is up to the operating system and might not be predictable.  | ||||
|     /// </remarks> | ||||
|     /// <param name="seedU">The first seed value.</param> | ||||
|     /// <param name="seedV">The second seed value.</param> | ||||
|     public UIntChannelProducer(uint seedU, uint seedV) | ||||
|     { | ||||
|         this.mW = seedU; | ||||
|         this.mZ = seedV; | ||||
|         this.StartProducerThread(); | ||||
|     } | ||||
| 
 | ||||
|     private void StartProducerThread() | ||||
|     { | ||||
|         this.producerRandomUint = new Thread(() => this.RandomProducerUint(this.producerTokenSource.Token)) {IsBackground = true}; | ||||
|         this.producerRandomUint.Start(); | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
|      | ||||
|     [ExcludeFromCodeCoverage] | ||||
|     private async void RandomProducerUint(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             while (!cancellationToken.IsCancellationRequested) | ||||
|             { | ||||
|                 this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16); | ||||
|                 this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16); | ||||
|                 await this.channelIntegers.Writer.WriteAsync((this.mZ << 16) + this.mW, cancellationToken); | ||||
|             } | ||||
|         } | ||||
|         catch (OperationCanceledException) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public async ValueTask<uint> GetNextAsync(CancellationToken cancellationToken = default) => await this.channelIntegers.Reader.ReadAsync(cancellationToken); | ||||
|      | ||||
|     #region Implementation of IDisposable | ||||
| 
 | ||||
|     private void StopProducer() => this.producerTokenSource.Cancel(); | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public void Dispose() => this.StopProducer(); | ||||
| 
 | ||||
|     #endregion | ||||
| } | ||||
| @ -1,60 +0,0 @@ | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using FastRng; | ||||
| using FastRng.Distributions; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class DecisionTester | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public async Task DecisionUniform01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new Uniform<float>(rng); | ||||
| 
 | ||||
|         var neededCoinTossesA = 0; | ||||
|         var neededCoinTossesB = 0; | ||||
|         var neededCoinTossesC = 0; | ||||
| 
 | ||||
|         for(var n = 0; n < 100; n++) while (!dist.HasDecisionBeenMade(0.0f, 0.1f)) neededCoinTossesA++; | ||||
|         for(var n = 0; n < 100; n++) while (!dist.HasDecisionBeenMade(0.5f, 0.6f)) neededCoinTossesB++; | ||||
|         for(var n = 0; n < 100; n++) while (!dist.HasDecisionBeenMade(0.8f, 0.9f)) neededCoinTossesC++; | ||||
| 
 | ||||
|         var values = new[] {neededCoinTossesA, neededCoinTossesB, neededCoinTossesC}; | ||||
|         var max = values.Max(); | ||||
|         var min = values.Min(); | ||||
|              | ||||
|         TestContext.WriteLine($"Coin tosses: a={neededCoinTossesA}, b={neededCoinTossesB}, c={neededCoinTossesC}"); | ||||
|         Assert.That(max - min, Is.LessThanOrEqualTo(250)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public async Task DecisionWeibull01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new WeibullK05La1<float>(rng); | ||||
| 
 | ||||
|         var neededCoinTossesA = 0; | ||||
|         var neededCoinTossesB = 0; | ||||
|         var neededCoinTossesC = 0; | ||||
| 
 | ||||
|         for(var n = 0; n < 100; n++) while (!dist.HasDecisionBeenMade(0.0f, 0.1f)) neededCoinTossesA++; | ||||
|         for(var n = 0; n < 100; n++) while (!dist.HasDecisionBeenMade(0.5f, 0.6f)) neededCoinTossesB++; | ||||
|         for(var n = 0; n < 100; n++) while (!dist.HasDecisionBeenMade(0.8f, 0.9f)) neededCoinTossesC++; | ||||
| 
 | ||||
|         var values = new[] {neededCoinTossesA, neededCoinTossesB, neededCoinTossesC}; | ||||
|         var max = values.Max(); | ||||
|         var min = values.Min(); | ||||
|              | ||||
|         TestContext.WriteLine($"Coin tosses: a={neededCoinTossesA}, b={neededCoinTossesB}, c={neededCoinTossesC}"); | ||||
|         Assert.That(max - min, Is.LessThanOrEqualTo(2_800)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class BetaA2B2 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.BetaA2B2<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
|              | ||||
|         Assert.That(result[0], Is.EqualTo(0.0396f).Within(0.3f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.0784f).Within(0.3f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.1164f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.6864f).Within(0.3f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.7084f).Within(0.3f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.7296f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.9996f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.7296f).Within(0.3f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.4816f).Within(0.3f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.3276f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.0784f).Within(0.3f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.0396f).Within(0.3f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.0000f).Within(0.3f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var samples = new float[1_000]; | ||||
|         var dist = new FastRng.Distributions.BetaA2B2<float>(rng); | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
| 
 | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var samples = new float[1_000]; | ||||
|         var dist = new FastRng.Distributions.BetaA2B2<float>(rng); | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.BetaA2B2<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class BetaA2B5 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.BetaA2B5<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
|              | ||||
|         Assert.That(result[0], Is.EqualTo(0.11719271f).Within(0.3f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.22505783f).Within(0.3f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.32401717f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.99348410f).Within(0.3f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.98639433f).Within(0.3f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.97684451f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.35868592f).Within(0.3f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.03076227f).Within(0.03f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.00403061f).Within(0.03f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.00109800f).Within(0.01f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.00000191f).Within(0.000003f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.00000012f).Within(0.0000003f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.00000000f).Within(0.0000003f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var samples = new float[1_000]; | ||||
|         var dist = new FastRng.Distributions.BetaA2B5<float>(rng); | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
| 
 | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var samples = new float[1_000]; | ||||
|         var dist = new FastRng.Distributions.BetaA2B5<float>(rng); | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.BetaA2B5<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class BetaA5B2 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.BetaA5B2<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
|              | ||||
|         Assert.That(result[0], Is.EqualTo(0.0000001f).Within(0.0000003f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.0000019f).Within(0.00001f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.0000096f).Within(0.0004f)); | ||||
| 
 | ||||
|         Assert.That(result[21], Is.EqualTo(0.0222918f).Within(0.03f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.0262883f).Within(0.03f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.0307623f).Within(0.03f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.4044237f).Within(0.2f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.9768445f).Within(0.15f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.9552714f).Within(0.15f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.8004420f).Within(0.35f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.2250578f).Within(0.03f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.1171927f).Within(0.03f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0f).Within(0.0004f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var samples = new float[1_000]; | ||||
|         var dist = new FastRng.Distributions.BetaA5B2<float>(rng); | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
| 
 | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestBetaGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var samples = new float[1_000]; | ||||
|         var dist = new FastRng.Distributions.BetaA5B2<float>(rng); | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.BetaA5B2<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,85 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class CauchyLorentzX0 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestCauchyDistribution01() | ||||
|     { | ||||
|         // The properties of the cauchy distribution cannot be tested by mean, media or variance,   | ||||
|         // cf. https://en.wikipedia.org/wiki/Cauchy_distribution#Explanation_of_undefined_moments | ||||
|              | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.CauchyLorentzX0<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
|              | ||||
|         Assert.That(result[0], Is.EqualTo(0.976990739772031f).Within(0.06f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.948808314586299f).Within(0.06f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.905284997403441f).Within(0.06f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.168965864241396f).Within(0.04f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.156877686354491f).Within(0.04f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.145970509936354f).Within(0.04f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.036533159835978f).Within(0.01f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.016793067514802f).Within(0.01f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.01316382933791f).Within(0.005f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.011773781734516f).Within(0.005f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.010168596941156f).Within(0.005f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.009966272570142f).Within(0.005f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.00976990739772f).Within(0.005f)); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestCauchyGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.CauchyLorentzX0<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestCauchyGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.CauchyLorentzX0<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.CauchyLorentzX0<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,85 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class CauchyLorentzX1 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestCauchyDistribution01() | ||||
|     { | ||||
|         // The properties of the cauchy distribution cannot be tested by mean, media or variance,   | ||||
|         // cf. https://en.wikipedia.org/wiki/Cauchy_distribution#Explanation_of_undefined_moments | ||||
|              | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.CauchyLorentzX1<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
|              | ||||
|         Assert.That(result[0], Is.EqualTo(0.009966272570142f).Within(0.003f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.010168596941156f).Within(0.004f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.010377123221893f).Within(0.005f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.015956672819692f).Within(0.005f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.016366904083094f).Within(0.005f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.016793067514802f).Within(0.005f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.039454644029179f).Within(0.015f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.145970509936354f).Within(0.03f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.333365083503296f).Within(0.1f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.545171628270584f).Within(0.1f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.948808314586302f).Within(0.06f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.976990739772032f).Within(0.03f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.986760647169751f).Within(0.02f)); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestCauchyGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.CauchyLorentzX0<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestCauchyGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.CauchyLorentzX0<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.CauchyLorentzX1<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,85 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class ChiSquareK1 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK1<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|         { | ||||
|             var value = dist.NextNumber(); | ||||
|             fqa.CountThis(value); | ||||
|         } | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
| 
 | ||||
|         Assert.That(result[0], Is.EqualTo(1.00032041964207f).Within(0.004f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.70380551227703f).Within(0.05f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.571788691668126f).Within(0.05f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.192011337664754f).Within(0.07f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.186854182385981f).Within(0.07f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.182007652359976f).Within(0.07f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.109088865614875f).Within(0.06f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.07886274821701f).Within(0.02f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.070520397849883f).Within(0.02f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.066863009640287f).Within(0.02f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.062214737436948f).Within(0.02f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.061590997922187f).Within(0.02f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.060976622578824f).Within(0.02f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK1<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK1<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.ChiSquareK1<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,85 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class ChiSquareK10 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK10<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|         { | ||||
|             var value = dist.NextNumber(); | ||||
|             fqa.CountThis(value); | ||||
|         } | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
| 
 | ||||
|         Assert.That(result[0], Is.EqualTo(0.0000000164021588f).Within(0.0000002f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.0000002611256437f).Within(0.000003f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.0000013153553250f).Within(0.00002f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.003459320622874f).Within(0.005f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.004111875573379f).Within(0.005f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.004850674298859f).Within(0.005f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.086418773275056f).Within(0.05f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.376092741436046f).Within(0.08f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.586569751611096f).Within(0.08f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.717189736168766f).Within(0.08f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.931477764640217f).Within(0.08f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.965244855212136f).Within(0.08f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.999827884370044f).Within(0.08f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK10<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK10<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.ChiSquareK10<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class ChiSquareK4 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK4<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
| 
 | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
| 
 | ||||
|         Assert.That(result[0], Is.EqualTo(0.016417705906679f).Within(0.02f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.032671644513723f).Within(0.02f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.048763041010352f).Within(0.02f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.32518779111264f).Within(0.05f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.338273451612642f).Within(0.05f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.351220492939994f).Within(0.05f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.65209223303425f).Within(0.08f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.857562207152294f).Within(0.099f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.923072405412387f).Within(0.099f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.952623623874265f).Within(0.099f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.990616879396201f).Within(0.099f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.995734077068522f).Within(0.099f)); | ||||
|         Assert.That(result[99], Is.EqualTo(1.00077558852585f).Within(0.1f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK4<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestChiSquareGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ChiSquareK4<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.ChiSquareK4<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class ExponentialLa10 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestExponentialDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ExponentialLa10<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
|              | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
| 
 | ||||
|         Assert.That(result[0], Is.EqualTo(1.00075018434777f).Within(0.05f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.905516212904248f).Within(0.05f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.81934495207398f).Within(0.05f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.122548293148741f).Within(0.12f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.110886281157421f).Within(0.12f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.10033405633809f).Within(0.12f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.00674300170146f).Within(0.005f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.000553499285385f).Within(0.001f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.000203621007796f).Within(0.001f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.00012350238419f).Within(0.001f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.0000613294689720f).Within(0.0008f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.0000554931983541f).Within(0.0008f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.0000502123223173f).Within(0.0008f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestExponentialGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ExponentialLa10<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestExponentialGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ExponentialLa10<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.ExponentialLa10<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class ExponentialLa5 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestExponentialDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ExponentialLa5<float>(rng); | ||||
|         var fqa = new FrequencyAnalysis(); | ||||
| 
 | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fqa.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fqa.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
| 
 | ||||
|         Assert.That(result[0], Is.EqualTo(1.0002177398625f).Within(0.05f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.951436545064811f).Within(0.05f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.905034437210948f).Within(0.05f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.35001394450853f).Within(0.05f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.332943563002074f).Within(0.05f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.31670571382568f).Within(0.05f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.082102871800213f).Within(0.01f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.023522866606758f).Within(0.01f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.014267339801329f).Within(0.01f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.011111415409621f).Within(0.01f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.007830082099077f).Within(0.008f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.007448204488898f).Within(0.008f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.007084951269538f).Within(0.008f)); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestExponentialGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ExponentialLa5<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestExponentialGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.ExponentialLa5<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.ExponentialLa5<float>(null)); | ||||
|     } | ||||
| } | ||||
| @ -1,82 +0,0 @@ | ||||
| using System; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using FastRng; | ||||
| using NUnit.Framework; | ||||
| 
 | ||||
| namespace FastRngTests.Distributions; | ||||
| 
 | ||||
| [ExcludeFromCodeCoverage] | ||||
| public class GammaA5B15 | ||||
| { | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestGammaDistribution01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.GammaA5B15<float>(rng); | ||||
|         var fra = new FrequencyAnalysis(); | ||||
|              | ||||
|         for (var n = 0; n < 100_000; n++) | ||||
|             fra.CountThis(dist.NextNumber()); | ||||
|              | ||||
|         var result = fra.NormalizeAndPlotEvents(TestContext.WriteLine); | ||||
| 
 | ||||
|         Assert.That(result[0], Is.EqualTo(0.0000929594237282f).Within(0.0008f)); | ||||
|         Assert.That(result[1], Is.EqualTo(0.0012801746797876f).Within(0.002f)); | ||||
|         Assert.That(result[2], Is.EqualTo(0.0055781488254349f).Within(0.004f)); | ||||
|              | ||||
|         Assert.That(result[21], Is.EqualTo(0.9331608887752720f).Within(0.09f)); | ||||
|         Assert.That(result[22], Is.EqualTo(0.9594734828891280f).Within(0.09f)); | ||||
|         Assert.That(result[23], Is.EqualTo(0.9790895765535350f).Within(0.09f)); | ||||
|              | ||||
|         Assert.That(result[50], Is.EqualTo(0.3478287795336570f).Within(0.06f)); | ||||
|              | ||||
|         Assert.That(result[75], Is.EqualTo(0.0403399049422936f).Within(0.009f)); | ||||
|         Assert.That(result[85], Is.EqualTo(0.0163628388658126f).Within(0.009f)); | ||||
|         Assert.That(result[90], Is.EqualTo(0.0097147611446660f).Within(0.005f)); | ||||
|              | ||||
|         Assert.That(result[97], Is.EqualTo(0.0041135143233153f).Within(0.008f)); | ||||
|         Assert.That(result[98], Is.EqualTo(0.0036872732029996f).Within(0.008f)); | ||||
|         Assert.That(result[99], Is.EqualTo(0.0033038503429554f).Within(0.008f)); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestGammaGeneratorWithRange01() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.GammaA5B15<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(-1.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(-1.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
|          | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void TestGammaGeneratorWithRange02() | ||||
|     { | ||||
|         using var rng = new MultiThreadedRng<float>(); | ||||
|         var dist = new FastRng.Distributions.GammaA5B15<float>(rng); | ||||
|         var samples = new float[1_000]; | ||||
|         for (var n = 0; n < samples.Length; n++) | ||||
|             samples[n] = dist.NextNumber(0.0f, 1.0f); | ||||
|              | ||||
|         Assert.That(samples.Min(), Is.GreaterThanOrEqualTo(0.0f), "Min is out of range"); | ||||
|         Assert.That(samples.Max(), Is.LessThanOrEqualTo(1.0f), "Max is out of range"); | ||||
|     } | ||||
| 
 | ||||
|     [Test] | ||||
|     [Category(TestCategories.COVER)] | ||||
|     [Category(TestCategories.NORMAL)] | ||||
|     public void NoRandomNumberGenerator01() | ||||
|     { | ||||
|         Assert.Throws<ArgumentNullException>(() => new FastRng.Distributions.GammaA5B15<float>(null)); | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user