The OpenD Programming Language

1 /++
2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution).
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 
6 Authors: John Michael Hall
7 
8 Copyright: 2022-3 Mir Stat Authors.
9 
10 +/
11 
12 module mir.stat.distribution.uniform_discrete;
13 
14 import mir.internal.utility: isFloatingPoint;
15 
16 /++
17 Computes the discrete uniform probability mass function (PMF).
18 
19 Params:
20     x = value to evaluate PMF
21     lower = lower bound
22     upper = upper bound
23 
24 See_also:
25     $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
26 +/
27 @safe pure nothrow @nogc
28 double uniformDiscretePMF(const size_t x, const size_t lower = 0, const size_t upper = 1)
29     in (x >= lower, "x must be greater than or equal to lower bound")
30     in (x <= upper, "x must be less than or equal to upper bound")
31     in (lower <= upper, "lower must be less than or equal to upper")
32 {
33     return 1.0 / (upper - lower + 1);
34 }
35 
36 ///
37 version(mir_stat_test)
38 @safe pure nothrow @nogc
39 unittest {
40     import mir.test: shouldApprox;
41 
42     1.uniformDiscretePMF.shouldApprox == 0.5;
43     2.uniformDiscretePMF(1, 3).shouldApprox == 1.0 / 3;
44 }
45 
46 /++
47 Computes the discrete uniform cumulative distribution function (CDF).
48 
49 Params:
50     x = value to evaluate CDF
51     lower = lower bound
52     upper = upper bound
53 
54 See_also:
55     $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
56 +/
57 @safe pure nothrow @nogc
58 double uniformDiscreteCDF(const size_t x, const size_t lower = 0, const size_t upper = 1)
59     in (x >= lower, "x must be greater than or equal to lower bound")
60     in (x <= upper, "x must be less than or equal to upper bound")
61     in (lower <= upper, "lower must be less than or equal to upper")
62 {
63     return (cast(double) x - lower + 1) / (upper - lower + 1);
64 }
65 
66 ///
67 version(mir_stat_test)
68 @safe pure nothrow @nogc
69 unittest {
70     import mir.test: shouldApprox;
71 
72     0.uniformDiscreteCDF.shouldApprox == 0.5;
73     1.uniformDiscreteCDF.shouldApprox == 1.0;
74 
75     1.uniformDiscreteCDF(1, 3).shouldApprox == 1.0 / 3;
76     2.uniformDiscreteCDF(1, 3).shouldApprox == 2.0 / 3;
77     3.uniformDiscreteCDF(1, 3).shouldApprox == 1.0;
78 }
79 
80 /++
81 Computes the discrete uniform complementary cumulative distribution function (CCDF).
82 
83 Params:
84     x = value to evaluate CCDF
85     lower = lower bound
86     upper = upper bound
87 
88 See_also:
89     $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
90 +/
91 @safe pure nothrow @nogc
92 double uniformDiscreteCCDF(const size_t x, const size_t lower = 0, const size_t upper = 1)
93     in (x >= lower, "x must be greater than or equal to lower bound")
94     in (x <= upper, "x must be less than or equal to upper bound")
95     in (lower <= upper, "lower must be less than or equal to upper")
96 {
97     return (cast(double) upper - x) / (upper - lower + 1);
98 }
99 
100 ///
101 version(mir_stat_test)
102 @safe pure nothrow @nogc
103 unittest {
104     import mir.test: shouldApprox;
105 
106     0.uniformDiscreteCCDF.shouldApprox == 0.5;
107     1.uniformDiscreteCCDF.shouldApprox == 0.0;
108 
109     1.uniformDiscreteCCDF(1, 3).shouldApprox == 2.0 / 3;
110     2.uniformDiscreteCCDF(1, 3).shouldApprox == 1.0 / 3;
111     3.uniformDiscreteCCDF(1, 3).shouldApprox == 0.0;
112 }
113 
114 /++
115 Computes the discrete uniform inverse cumulative distribution function (InvCDF)
116 
117 Params:
118     p = value to evaluate InvCDF
119     lower = lower bound
120     upper = upper bound
121 
122 See_also:
123     $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
124 +/
125 @safe pure nothrow @nogc
126 size_t uniformDiscreteInvCDF(T)(const T p, const size_t lower = 0, const size_t upper = 1)
127     if (isFloatingPoint!T)
128     in (p >= 0, "p must be greater than or equal to 0")
129     in (p <= 1, "p must be less than or equal to 1")
130     in (lower < upper, "lower must be less than upper")
131 {
132     size_t n = upper - lower + 1;
133     if (p * n <= 1) {
134         return lower;
135     }
136     return cast(size_t) (p * n + lower - 1);
137 }
138 
139 ///.
140 version(mir_stat_test)
141 @safe pure nothrow @nogc
142 unittest {
143     import mir.test: should;
144 
145     0.0.uniformDiscreteInvCDF.should == 0;
146     0.5.uniformDiscreteInvCDF.should == 0;
147     1.0.uniformDiscreteInvCDF.should == 1;
148 
149     0.0.uniformDiscreteInvCDF(1, 3).should == 1;
150     0.2.uniformDiscreteInvCDF(1, 3).should == 1;
151     (1.0 / 3).uniformDiscreteInvCDF(1, 3).should == 1;
152     0.5.uniformDiscreteInvCDF(1, 3).should == 1;
153     (2.0 / 3).uniformDiscreteInvCDF(1, 3).should == 2;
154     1.0.uniformDiscreteInvCDF(1, 3).should == 3;
155 }
156 
157 /++
158 Computes the discrete uniform log probability distribution function (LPDF)
159 
160 Params:
161     x = value to evaluate LPDF
162     lower = lower bound
163     upper = upper bound
164 
165 See_also:
166     $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
167 +/
168 @safe pure nothrow @nogc
169 double uniformDiscreteLPMF(const size_t x, const size_t lower = 0, const size_t upper = 1)
170     in (x >= lower, "x must be greater than or equal to lower bound")
171     in (x <= upper, "x must be less than or equal to upper bound")
172     in (lower < upper, "lower must be less than upper")
173 {
174     import mir.math.common: log;
175 
176     return -log(cast(double) upper - lower + 1);
177 }
178 
179 ///
180 version(mir_stat_test)
181 @safe pure nothrow @nogc
182 unittest {
183     import mir.math.common: log;
184     import mir.test: shouldApprox;
185 
186     1.uniformDiscreteLPMF.shouldApprox == -log(2.0);
187     2.uniformDiscreteLPMF(1, 3).shouldApprox == -log(3.0);
188 }