diff --git a/FastRng/Distributions/Exponential.cs b/FastRng/Distributions/Exponential.cs new file mode 100644 index 0000000..c3f62d1 --- /dev/null +++ b/FastRng/Distributions/Exponential.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Distributions +{ + public sealed class Exponential : IDistribution + { + private double mean = 1; + + public IRandom Random { get; set; } + + public double Mean + { + get => this.mean; + set + { + if(value <= 0) + throw new ArgumentOutOfRangeException(message: "Mean must be greater than 0", null); + + this.mean = value; + } + } + + public async Task GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return 0; + + if(this.Mean == 1) + return -Math.Log(await this.Random.GetUniformDouble(token)); + else + return this.Mean * -Math.Log(await this.Random.GetUniformDouble(token)); + } + } +} \ No newline at end of file diff --git a/FastRng/Distributions/Gamma.cs b/FastRng/Distributions/Gamma.cs new file mode 100644 index 0000000..88ef91f --- /dev/null +++ b/FastRng/Distributions/Gamma.cs @@ -0,0 +1,69 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Distributions +{ + public sealed class Gamma : IDistribution + { + private double shape = 1; + + public IRandom Random { get; set; } + + public double Shape + { + get => this.shape; + set + { + if(value <= 0) + throw new ArgumentOutOfRangeException(message: "Shape must be greater than 0", null); + + this.shape = value; + } + } + + public double Scale { get; set; } = 1; + + public async Task GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return 0; + + // Implementation based on "A Simple Method for Generating Gamma Variables" + // by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software + // Vol 26, No 3, September 2000, pages 363-372. + + if (shape >= 1.0) + { + var distNormal = new Normal(); + var d = shape - 1.0 / 3.0; + var c = 1.0 / Math.Sqrt(9.0 * d); + while(true) + { + double x, v; + do + { + x = await this.Random.NextNumber(0, 1, distNormal, token); + v = 1.0 + c * x; + } + while (v <= 0.0); + + v = v * v * v; + var u = await this.Random.GetUniformDouble(token); + var xSquared = x * x; + + if (u < 1.0 - 0.0331 * xSquared * xSquared || Math.Log(u) < 0.5 * xSquared + d * (1.0 - v + Math.Log(v))) + return this.Scale * d * v; + } + } + else + { + var dist = new Gamma{ Scale = 1, Shape = 1 + this.Shape}; + + var g = await this.Random.NextNumber(0.0f, 1.0f, dist, token); // TODO: Use double + var w = await this.Random.GetUniformDouble(token); + return this.Scale * g * Math.Pow(w, 1.0 / this.Shape); + } + } + } +} \ No newline at end of file diff --git a/FastRng/Distributions/Normal.cs b/FastRng/Distributions/Normal.cs new file mode 100644 index 0000000..5ecdad3 --- /dev/null +++ b/FastRng/Distributions/Normal.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Distributions +{ + public sealed class Normal : IDistribution + { + private double standardDeviation = 1; + + public IRandom Random { get; set; } + + public double Mean { get; set; } = 0; + + public double StandardDeviation + { + get => this.standardDeviation; + set + { + if(value <= 0) + throw new ArgumentOutOfRangeException(message: "Standard deviation must be greater than 0", null); + + this.standardDeviation = value; + } + } + + public async Task GetDistributedValue(CancellationToken token = default) + { + if (this.Random == null) + return 0; + + var u1 = await this.Random.GetUniformDouble(token); + var u2 = await this.Random.GetUniformDouble(token); + var r = Math.Sqrt(-2.0 * Math.Log(u1)); + var theta = 2.0 * Math.PI * u2; + var value = r * Math.Sin(theta); + + return this.Mean + this.StandardDeviation * value; + } + } +} \ No newline at end of file diff --git a/FastRng/Distributions/Uniform.cs b/FastRng/Distributions/Uniform.cs new file mode 100644 index 0000000..0799b8e --- /dev/null +++ b/FastRng/Distributions/Uniform.cs @@ -0,0 +1,12 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Distributions +{ + public sealed class Uniform : IDistribution + { + public IRandom Random { get; set; } + + public async Task GetDistributedValue(CancellationToken token = default) => this.Random == null ? 0 : await this.Random.GetUniformDouble(token); + } +} \ No newline at end of file