summaryrefslogtreecommitdiffstats
path: root/lib/gaussian_quantile.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gaussian_quantile.c')
-rw-r--r--lib/gaussian_quantile.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/lib/gaussian_quantile.c b/lib/gaussian_quantile.c
new file mode 100644
index 0000000..337262c
--- /dev/null
+++ b/lib/gaussian_quantile.c
@@ -0,0 +1,94 @@
+// Gaussian Quantile
+//
+// gaussian_quantile
+//
+// Gaussian quantile function (inverse CDF)
+//
+// Q(p) = Φ^(-1)(p)
+//
+// Inputs: 1
+// Outputs: 1
+// Constants: 0
+
+
+#include <math.h>
+#include "ts.h"
+
+
+ts_parameter_t const ts_gaussian_quantile_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 };
+
+uint_t ts_gaussian_quantile_offset( ts_constant_t const *c )
+{
+ ( void )c;
+ return ( 0 );
+}
+
+int ts_gaussian_quantile(
+ fp_t * const *y, uint_t y_n,
+ fp_t const * const *x, uint_t x_n,
+ uint_t n, ts_constant_t const *c
+)
+{
+ TS_PROLOGUE( ts_gaussian_quantile_p );
+ (void)c;
+ /* Peter Acklam's approximation for inverse normal CDF */
+ /* Coefficients for rational approximation */
+ fp_t const a[] = {
+ -3.969683028665376e+01, 2.209460984245205e+02,
+ -2.759285104469687e+02, 1.383577518672690e+02,
+ -3.066479806614716e+01, 2.506628277459239e+00
+ };
+ fp_t const b[] = {
+ -5.447609879822406e+01, 1.615858368580409e+02,
+ -1.556989798598866e+02, 6.680131188771972e+01,
+ -1.328068155288572e+01
+ };
+ fp_t const cc[] = {
+ -7.784894002430993e-03, 3.223964580411365e-01,
+ -2.400758277161838e+00, -2.549732539343734e+00,
+ 4.374664141464968e+00, 2.938163982698783e+00
+ };
+ fp_t const d[] = {
+ 7.784695709041462e-03, 3.224671290700398e-01,
+ 2.445134137142996e+00, 3.754408661907416e+00
+ };
+
+ fp_t const p_low = 0.02425;
+ fp_t const p_high = 1.0 - p_low;
+
+ for (uint_t i = 0; i < n; ++i)
+ {
+ fp_t p = x[0][i];
+ if (p <= 0.0)
+ {
+ y[0][i] = -INFINITY;
+ continue;
+ }
+ if (p >= 1.0)
+ {
+ y[0][i] = INFINITY;
+ continue;
+ }
+
+ fp_t q, r;
+
+ if (p < p_low) {
+ q = sqrt(-2.0 * log(p));
+ y[0][i] = -(((((cc[0]*q + cc[1])*q + cc[2])*q + cc[3])*q + cc[4])*q + cc[5]) /
+ ((((d[0]*q + d[1])*q + d[2])*q + d[3])*q + 1.0);
+ } else if (p <= p_high) {
+ q = p - 0.5;
+ r = q * q;
+ y[0][i] = (((((a[0]*r + a[1])*r + a[2])*r + a[3])*r + a[4])*r + a[5])*q /
+ (((((b[0]*r + b[1])*r + b[2])*r + b[3])*r + b[4])*r + 1.0);
+ } else {
+ q = sqrt(-2.0 * log(1.0 - p));
+ y[0][i] = (((((cc[0]*q + cc[1])*q + cc[2])*q + cc[3])*q + cc[4])*q + cc[5]) /
+ ((((d[0]*q + d[1])*q + d[2])*q + d[3])*q + 1.0);
+ }
+ }
+ return 0;
+}
+
+
+