mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-09-23 22:44:58 +02:00
27f24484d4
Based on patches and review comments by Riastradh and Catalyst. Co-authored-by: Taylor R Campbell <campbell+tor@mumble.net> Co-authored-by: Taylor Yu <catalyst@torproject.org>
254 lines
6.9 KiB
C
254 lines
6.9 KiB
C
|
|
/**
|
|
* \file prob_distr.h
|
|
*
|
|
* \brief Header for prob_distr.c
|
|
**/
|
|
|
|
#ifndef TOR_PROB_DISTR_H
|
|
#define TOR_PROB_DISTR_H
|
|
|
|
#include "lib/cc/compat_compiler.h"
|
|
#include "lib/cc/torint.h"
|
|
#include "lib/testsupport/testsupport.h"
|
|
|
|
/**
|
|
* Container for distribution parameters for sampling, CDF, &c.
|
|
*/
|
|
struct dist {
|
|
const struct dist_ops *ops;
|
|
};
|
|
|
|
/**
|
|
* Untyped initializer element for struct dist using the specified
|
|
* struct dist_ops pointer. Don't actually use this directly -- use
|
|
* the type-specific macro built out of DIST_BASE_TYPED below -- but if
|
|
* you did use this directly, it would be something like:
|
|
*
|
|
* struct weibull mydist = {
|
|
* DIST_BASE(&weibull_ops),
|
|
* .lambda = ...,
|
|
* .k = ...,
|
|
* };
|
|
*
|
|
* Note there is NO COMPILER FEEDBACK if you accidentally do something
|
|
* like
|
|
*
|
|
* struct geometric mydist = {
|
|
* DIST_BASE(&weibull_ops),
|
|
* ...
|
|
* };
|
|
*/
|
|
#define DIST_BASE(OPS) { .ops = (OPS) }
|
|
|
|
/** A compile-time type-checking macro for use with DIST_BASE_TYPED.
|
|
*
|
|
* This macro works by checking that &OBJ is a pointer type that is the same
|
|
* type (except for qualifiers) as (const TYPE *)&OBJ. It's a C constraint
|
|
* violation (which requires a diagnostic) if two pointers are different types
|
|
* and are subtracted. The sizeof() forces compile-time evaluation, and the
|
|
* multiplication by zero is to discard the result of the sizeof() from the
|
|
* expression.
|
|
*
|
|
* We define this conditionally to suppress false positives from
|
|
* Coverity, which gets confused by the sizeof business.
|
|
*/
|
|
#ifdef __COVERITY___
|
|
#define TYPE_CHECK_OBJ(OPS, OBJ, TYPE) 0
|
|
#else
|
|
#define TYPE_CHECK_OBJ(OPS, OBJ, TYPE) \
|
|
(0*sizeof(&(OBJ) - (const TYPE *)&(OBJ)))
|
|
#endif
|
|
|
|
/**
|
|
* Typed initializer element for struct dist using the specified struct
|
|
* dist_ops pointer. Don't actually use this directly -- use a
|
|
* type-specific macro built out of it -- but if you did use this
|
|
* directly, it would be something like:
|
|
*
|
|
* struct weibull mydist = {
|
|
* DIST_BASE_TYPED(&weibull_ops, mydist, struct weibull),
|
|
* .lambda = ...,
|
|
* .k = ...,
|
|
* };
|
|
*
|
|
* If you want to define a distribution type, define a canonical set of
|
|
* operations and define a type-specific initializer element like so:
|
|
*
|
|
* struct foo {
|
|
* struct dist base;
|
|
* int omega;
|
|
* double tau;
|
|
* double phi;
|
|
* };
|
|
*
|
|
* struct dist_ops foo_ops = ...;
|
|
*
|
|
* #define FOO(OBJ) DIST_BASE_TYPED(&foo_ops, OBJ, struct foo)
|
|
*
|
|
* Then users can do:
|
|
*
|
|
* struct foo mydist = {
|
|
* FOO(mydist),
|
|
* .omega = ...,
|
|
* .tau = ...,
|
|
* .phi = ...,
|
|
* };
|
|
*
|
|
* If you accidentally write
|
|
*
|
|
* struct bar mydist = {
|
|
* FOO(mydist),
|
|
* ...
|
|
* };
|
|
*
|
|
* then the compiler will report a type mismatch in the sizeof
|
|
* expression, which otherwise evaporates at runtime.
|
|
*/
|
|
#define DIST_BASE_TYPED(OPS, OBJ, TYPE) \
|
|
DIST_BASE((OPS) + TYPE_CHECK_OBJ(OPS,OBJ,TYPE))
|
|
|
|
/**
|
|
* Generic operations on distributions. These simply defer to the
|
|
* corresponding dist_ops function. In the parlance of C++, these call
|
|
* virtual member functions.
|
|
*/
|
|
const char *dist_name(const struct dist *);
|
|
double dist_sample(const struct dist *);
|
|
double dist_cdf(const struct dist *, double x);
|
|
double dist_sf(const struct dist *, double x);
|
|
double dist_icdf(const struct dist *, double p);
|
|
double dist_isf(const struct dist *, double p);
|
|
|
|
/**
|
|
* Set of operations on a potentially parametric family of
|
|
* distributions. In the parlance of C++, this would be called a
|
|
* `vtable' and the members are virtual member functions.
|
|
*/
|
|
struct dist_ops {
|
|
const char *name;
|
|
double (*sample)(const struct dist *);
|
|
double (*cdf)(const struct dist *, double x);
|
|
double (*sf)(const struct dist *, double x);
|
|
double (*icdf)(const struct dist *, double p);
|
|
double (*isf)(const struct dist *, double p);
|
|
};
|
|
|
|
/* Geometric distribution on positive number of trials before first success */
|
|
|
|
struct geometric {
|
|
struct dist base;
|
|
double p; /* success probability */
|
|
};
|
|
|
|
extern const struct dist_ops geometric_ops;
|
|
|
|
#define GEOMETRIC(OBJ) \
|
|
DIST_BASE_TYPED(&geometric_ops, OBJ, struct geometric)
|
|
|
|
/* Pareto distribution */
|
|
|
|
struct genpareto {
|
|
struct dist base;
|
|
double mu;
|
|
double sigma;
|
|
double xi;
|
|
};
|
|
|
|
extern const struct dist_ops genpareto_ops;
|
|
|
|
#define GENPARETO(OBJ) \
|
|
DIST_BASE_TYPED(&genpareto_ops, OBJ, struct genpareto)
|
|
|
|
/* Weibull distribution */
|
|
|
|
struct weibull {
|
|
struct dist base;
|
|
double lambda;
|
|
double k;
|
|
};
|
|
|
|
extern const struct dist_ops weibull_ops;
|
|
|
|
#define WEIBULL(OBJ) \
|
|
DIST_BASE_TYPED(&weibull_ops, OBJ, struct weibull)
|
|
|
|
/* Log-logistic distribution */
|
|
|
|
struct log_logistic {
|
|
struct dist base;
|
|
double alpha;
|
|
double beta;
|
|
};
|
|
|
|
extern const struct dist_ops log_logistic_ops;
|
|
|
|
#define LOG_LOGISTIC(OBJ) \
|
|
DIST_BASE_TYPED(&log_logistic_ops, OBJ, struct log_logistic)
|
|
|
|
/* Logistic distribution */
|
|
|
|
struct logistic {
|
|
struct dist base;
|
|
double mu;
|
|
double sigma;
|
|
};
|
|
|
|
extern const struct dist_ops logistic_ops;
|
|
|
|
#define LOGISTIC(OBJ) \
|
|
DIST_BASE_TYPED(&logistic_ops, OBJ, struct logistic)
|
|
|
|
/* Uniform distribution */
|
|
|
|
struct uniform {
|
|
struct dist base;
|
|
double a;
|
|
double b;
|
|
};
|
|
|
|
extern const struct dist_ops uniform_ops;
|
|
|
|
#define UNIFORM(OBJ) \
|
|
DIST_BASE_TYPED(&uniform_ops, OBJ, struct uniform)
|
|
|
|
/** Only by unittests */
|
|
|
|
#ifdef PROB_DISTR_PRIVATE
|
|
|
|
STATIC double logithalf(double p0);
|
|
STATIC double logit(double p);
|
|
|
|
STATIC double random_uniform_01(void);
|
|
|
|
STATIC double logistic(double x);
|
|
STATIC double cdf_logistic(double x, double mu, double sigma);
|
|
STATIC double sf_logistic(double x, double mu, double sigma);
|
|
STATIC double icdf_logistic(double p, double mu, double sigma);
|
|
STATIC double isf_logistic(double p, double mu, double sigma);
|
|
STATIC double sample_logistic(uint32_t s, double t, double p0);
|
|
|
|
STATIC double cdf_log_logistic(double x, double alpha, double beta);
|
|
STATIC double sf_log_logistic(double x, double alpha, double beta);
|
|
STATIC double icdf_log_logistic(double p, double alpha, double beta);
|
|
STATIC double isf_log_logistic(double p, double alpha, double beta);
|
|
STATIC double sample_log_logistic(uint32_t s, double p0);
|
|
|
|
STATIC double cdf_weibull(double x, double lambda, double k);
|
|
STATIC double sf_weibull(double x, double lambda, double k);
|
|
STATIC double icdf_weibull(double p, double lambda, double k);
|
|
STATIC double isf_weibull(double p, double lambda, double k);
|
|
STATIC double sample_weibull(uint32_t s, double p0, double lambda, double k);
|
|
|
|
STATIC double sample_uniform_interval(double p0, double a, double b);
|
|
|
|
STATIC double cdf_genpareto(double x, double mu, double sigma, double xi);
|
|
STATIC double sf_genpareto(double x, double mu, double sigma, double xi);
|
|
STATIC double icdf_genpareto(double p, double mu, double sigma, double xi);
|
|
STATIC double isf_genpareto(double p, double mu, double sigma, double xi);
|
|
STATIC double sample_genpareto(uint32_t s, double p0, double xi);
|
|
|
|
#endif
|
|
|
|
#endif
|