diff --git a/FastRng/Double/Distributions/Beta.cs b/FastRng/Double/Distributions/Beta.cs new file mode 100644 index 0000000..a1fe3b0 --- /dev/null +++ b/FastRng/Double/Distributions/Beta.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class Beta : IDistribution + { + private double a = 1.0; + private double b = 1.0; + public IRandom Random { get; set; } + + public double A + { + get => this.a; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Parameter must be greater than 0", null); + + this.a = value; + } + } + + public double B + { + get => this.b; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Parameter must be greater than 0", null); + + this.b = value; + } + } + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + // There are more efficient methods for generating beta samples. + // However such methods are a little more efficient and much more complicated. + // For an explanation of why the following method works, see + // http://www.johndcook.com/distribution_chart.html#gamma_beta + + var u = await this.Random.NextNumber(new Gamma{Shape = this.A, Scale = 1.0}, token); + var v = await this.Random.NextNumber(new Gamma{Shape = this.B, Scale = 1.0}, token); + return u / (u + v); + } + } +} \ No newline at end of file diff --git a/FastRng/Double/Distributions/Cauchy.cs b/FastRng/Double/Distributions/Cauchy.cs new file mode 100644 index 0000000..088d85c --- /dev/null +++ b/FastRng/Double/Distributions/Cauchy.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class Cauchy : IDistribution + { + private double scale = 1.0; + + public IRandom Random { get; set; } + + public double Scale + { + get => this.scale; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Scale must be greater than 0", null); + + this.scale = value; + } + } + + public double Median { get; set; } = 0.0; + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + var value = await this.Random.GetUniform(token); + return this.Median + scale * Math.Tan(Math.PI * (value - 0.5)); + } + } +} \ No newline at end of file diff --git a/FastRng/Double/Distributions/InverseGamma.cs b/FastRng/Double/Distributions/InverseGamma.cs new file mode 100644 index 0000000..cc027fc --- /dev/null +++ b/FastRng/Double/Distributions/InverseGamma.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class InverseGamma : IDistribution + { + private double shape = 1.0; + + public IRandom Random { get; set; } + + public double Shape + { + get => this.shape; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Shape must be greater than 0", null); + + this.shape = value; + } + } + + public double Scale { get; set; } = 1.0; + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + var gammaDist = new Gamma{ Shape = this.Shape, Scale = 1.0 / this.Scale }; + return 1.0 / await this.Random.NextNumber(gammaDist, token); + } + } +} \ No newline at end of file diff --git a/FastRng/Double/Distributions/Laplace.cs b/FastRng/Double/Distributions/Laplace.cs new file mode 100644 index 0000000..e57dd63 --- /dev/null +++ b/FastRng/Double/Distributions/Laplace.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class Laplace : IDistribution + { + public IRandom Random { get; set; } + + public double Mean { get; set; } = 0.0; + + public double Scale { get; set; } = 1.0; + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + var value = await this.Random.GetUniform(token); + + if (value < 0.5) + return this.Mean + this.Scale * Math.Log(2.0 * value); + else + return this.Mean - this.Scale * Math.Log(2.0 * (1.0 - value)); + } + } +} \ No newline at end of file diff --git a/FastRng/Double/Distributions/LogNormal.cs b/FastRng/Double/Distributions/LogNormal.cs new file mode 100644 index 0000000..cd23685 --- /dev/null +++ b/FastRng/Double/Distributions/LogNormal.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class LogNormal : IDistribution + { + private double sigma = 1.0; + public IRandom Random { get; set; } + + public double Mu { get; set; } = 0.0; + + public double Sigma + { + get => this.sigma; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Sigma must be greater than 0", null); + + this.sigma = value; + } + } + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + var normal = await this.Random.NextNumber(new Normal {Mean = this.Mu, StandardDeviation = this.Sigma}, token); + return Math.Exp(normal); + } + } +} \ No newline at end of file diff --git a/FastRng/Double/Distributions/StudentT.cs b/FastRng/Double/Distributions/StudentT.cs new file mode 100644 index 0000000..c32ec2b --- /dev/null +++ b/FastRng/Double/Distributions/StudentT.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class StudentT : IDistribution + { + private static readonly IDistribution NORMAL_DISTRIBUTED = new Normal(); + private double degreesOfFreedom = 1.0; + + public IRandom Random { get; set; } + + public double DegreesOfFreedom + { + get => this.degreesOfFreedom; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "DegreesOfFreedom must be greater than 0", null); + + this.degreesOfFreedom = value; + } + } + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + var normal = await this.Random.NextNumber(NORMAL_DISTRIBUTED, token); + var chiSquare = await this.Random.NextNumber(new ChiSquare {DegreesOfFreedom = this.DegreesOfFreedom}, token); + return normal / Math.Sqrt(chiSquare / this.DegreesOfFreedom); + } + } +} \ No newline at end of file diff --git a/FastRng/Double/Distributions/Weibull.cs b/FastRng/Double/Distributions/Weibull.cs new file mode 100644 index 0000000..604903c --- /dev/null +++ b/FastRng/Double/Distributions/Weibull.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FastRng.Double.Distributions +{ + public sealed class Weibull : IDistribution + { + private double shape = 1.0; + private double scale = 1.0; + + public IRandom Random { get; set; } + + public double Shape + { + get => this.shape; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Shape must be greater than 0", null); + + this.shape = value; + } + } + + public double Scale + { + get => this.scale; + set + { + if(value <= 0.0) + throw new ArgumentOutOfRangeException(message: "Scale must be greater than 0", null); + + this.scale = value; + } + } + + public async ValueTask GetDistributedValue(CancellationToken token) + { + if (this.Random == null) + return System.Double.NaN; + + var value = await this.Random.GetUniform(token); + return this.Scale * Math.Pow(-Math.Log(value), 1.0 / this.Shape); + } + } +} \ No newline at end of file