The OpenD Programming Language

1 /**
2  * Normal Distribution
3  *
4  * Copyright: Based on the CEPHES math library, which is
5  *            Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
6  * Authors:   Stephen L. Moshier, ported to D by Don Clugston and David Nadlinger. Adopted to Mir by Ilya Yaroshenko.
7 */
8 /++
9 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Normal_distribution, Normal Distribution).
10 
11 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
12 
13 Authors: John Michael Hall
14 
15 Copyright: 2022-3 Mir Stat Authors.
16 
17 +/
18 
19 module mir.stat.distribution.normal;
20 
21 ///
22 public import mir.math.func.normal: normalPDF, normalCDF, normalInvCDF;
23 
24 import mir.internal.utility: isFloatingPoint;
25 
26 /++
27 Computes the normal complementary cumulative distribution function (CCDF)
28 
29 Params:
30     x = value to evaluate CCDF
31     mean = mean
32     stdDev = standard deviation
33 
34 See_also:
35     $(LINK2 https://en.wikipedia.org/wiki/Normal_distribution, Normal Distribution)
36 +/
37 @safe pure nothrow @nogc
38 T normalCCDF(T)(const T x, const T mean, const T stdDev)
39     if (isFloatingPoint!T)
40 {
41     return normalCCDF((x - mean) / stdDev);
42 }
43 
44 /// ditto
45 @safe pure nothrow @nogc
46 T normalCCDF(T)(const T a)
47     if (isFloatingPoint!T)
48 {
49     return normalCDF(-a);
50 }
51 
52 ///
53 version(mir_stat_test)
54 @safe pure nothrow @nogc
55 unittest {
56     import mir.math.common: approxEqual;
57 
58     assert(0.5.normalCCDF.approxEqual(1 - normalCDF(0.5)));
59     assert(0.5.normalCCDF(0, 1.5).approxEqual(1 - normalCDF(0.5, 0, 1.5)));
60     assert(1.5.normalCCDF(1, 3).approxEqual(1 - normalCDF(1.5, 1, 3)));
61 }
62 
63 version(mir_stat_test)
64 @safe pure nothrow @nogc
65 unittest {
66     import mir.math.common: approxEqual;
67 
68     assert((-3.0).normalCCDF.approxEqual(1 - normalCDF(-3.0)));
69     assert(3.0.normalCCDF.approxEqual(1 - normalCDF(3.0)));
70     assert((-7.0).normalCCDF(1, 4).approxEqual(1 - normalCDF(-7.0, 1, 4)));
71     assert(9.0.normalCCDF(1, 4).approxEqual(1 - normalCDF(9.0, 1, 4)));
72 }
73 
74 version(mir_stat_test)
75 @safe pure nothrow @nogc
76 unittest {
77     import mir.math.common: approxEqual;
78     assert(normalCCDF(double.infinity).approxEqual(1 - normalCDF(double.infinity)));
79     assert(normalCCDF(-double.infinity).approxEqual(1 - normalCDF(-double.infinity)));
80 }
81 
82 /++
83 Computes the normal log probability density function (LPDF)
84 
85 Params:
86     x = value to evaluate LPDF
87     mean = mean
88     stdDev = standard deviation
89 
90 See_also:
91     $(LINK2 https://en.wikipedia.org/wiki/Normal_distribution, Normal Distribution)
92 +/
93 @safe pure nothrow @nogc
94 T normalLPDF(T)(const T x, const T mean, const T stdDev)
95     if (isFloatingPoint!T)
96 {
97     import mir.math.common: log;
98     return normalLPDF((x - mean) / stdDev) - log(stdDev);
99 }
100 
101 /// ditto
102 @safe pure nothrow @nogc
103 T normalLPDF(T)(const T x)
104     if (isFloatingPoint!T)
105 {
106     import mir.math.common: pow;
107     import mir.stat.constant: LOGSQRT2PI;
108 
109     return -0.5 * pow(x, 2) - T(LOGSQRT2PI);
110 }
111 
112 ///
113 version(mir_stat_test)
114 @safe pure nothrow @nogc
115 unittest {
116     import mir.math.common: approxEqual, log;
117     assert(0.5.normalLPDF.approxEqual(log(normalPDF(0.5))));
118     assert(0.5.normalLPDF(0, 1.5).approxEqual(log(normalPDF(0.5, 0, 1.5))));
119     assert(1.5.normalLPDF(1, 3).approxEqual(log(normalPDF(1.5, 1, 3))));
120 }