aboutsummaryrefslogtreecommitdiff
path: root/src/lib/math
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-06-28 12:24:45 -0400
committerNick Mathewson <nickm@torproject.org>2018-06-28 12:24:45 -0400
commitbdea94a6653240e0e477eda186ceccbe6be00535 (patch)
tree64e57e8684f65fba4b0e953ef2a99d7fdb353a06 /src/lib/math
parent6178a9f758d60f5fc896644f1b9b0aaf4c32f2a5 (diff)
downloadtor-bdea94a6653240e0e477eda186ceccbe6be00535.tar.gz
tor-bdea94a6653240e0e477eda186ceccbe6be00535.zip
Move floating-point math functions into a new lib/math
Diffstat (limited to 'src/lib/math')
-rw-r--r--src/lib/math/.may_include5
-rw-r--r--src/lib/math/fp.c113
-rw-r--r--src/lib/math/fp.h17
-rw-r--r--src/lib/math/include.am20
-rw-r--r--src/lib/math/laplace.c67
-rw-r--r--src/lib/math/laplace.h16
6 files changed, 238 insertions, 0 deletions
diff --git a/src/lib/math/.may_include b/src/lib/math/.may_include
new file mode 100644
index 0000000000..1fd26864dc
--- /dev/null
+++ b/src/lib/math/.may_include
@@ -0,0 +1,5 @@
+orconfig.h
+
+lib/cc/*.h
+lib/log/*.h
+lib/math/*.h
diff --git a/src/lib/math/fp.c b/src/lib/math/fp.c
new file mode 100644
index 0000000000..d1c4428251
--- /dev/null
+++ b/src/lib/math/fp.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "lib/math/fp.h"
+
+#include <math.h>
+
+/**
+ * Returns the natural logarithm of d base e. We defined this wrapper here so
+ * to avoid conflicts with old versions of tor_log(), which were named log().
+ */
+double
+tor_mathlog(double d)
+{
+ return log(d);
+}
+
+/** Return the long integer closest to <b>d</b>. We define this wrapper
+ * here so that not all users of math.h need to use the right incantations
+ * to get the c99 functions. */
+long
+tor_lround(double d)
+{
+#if defined(HAVE_LROUND)
+ return lround(d);
+#elif defined(HAVE_RINT)
+ return (long)rint(d);
+#else
+ return (long)(d > 0 ? d + 0.5 : ceil(d - 0.5));
+#endif /* defined(HAVE_LROUND) || ... */
+}
+
+/** Return the 64-bit integer closest to d. We define this wrapper here so
+ * that not all users of math.h need to use the right incantations to get the
+ * c99 functions. */
+int64_t
+tor_llround(double d)
+{
+#if defined(HAVE_LLROUND)
+ return (int64_t)llround(d);
+#elif defined(HAVE_RINT)
+ return (int64_t)rint(d);
+#else
+ return (int64_t)(d > 0 ? d + 0.5 : ceil(d - 0.5));
+#endif /* defined(HAVE_LLROUND) || ... */
+}
+
+/** Cast a given double value to a int64_t. Return 0 if number is NaN.
+ * Returns either INT64_MIN or INT64_MAX if number is outside of the int64_t
+ * range. */
+int64_t
+clamp_double_to_int64(double number)
+{
+ int exponent;
+
+#if defined(MINGW_ANY) && GCC_VERSION >= 409
+/*
+ Mingw's math.h uses gcc's __builtin_choose_expr() facility to declare
+ isnan, isfinite, and signbit. But as implemented in at least some
+ versions of gcc, __builtin_choose_expr() can generate type warnings
+ even from branches that are not taken. So, suppress those warnings.
+*/
+#define PROBLEMATIC_FLOAT_CONVERSION_WARNING
+DISABLE_GCC_WARNING(float-conversion)
+#endif /* defined(MINGW_ANY) && GCC_VERSION >= 409 */
+
+/*
+ With clang 4.0 we apparently run into "double promotion" warnings here,
+ since clang thinks we're promoting a double to a long double.
+ */
+#if defined(__clang__)
+#if __has_warning("-Wdouble-promotion")
+#define PROBLEMATIC_DOUBLE_PROMOTION_WARNING
+DISABLE_GCC_WARNING(double-promotion)
+#endif
+#endif /* defined(__clang__) */
+
+ /* NaN is a special case that can't be used with the logic below. */
+ if (isnan(number)) {
+ return 0;
+ }
+
+ /* Time to validate if result can overflows a int64_t value. Fun with
+ * float! Find that exponent exp such that
+ * number == x * 2^exp
+ * for some x with abs(x) in [0.5, 1.0). Note that this implies that the
+ * magnitude of number is strictly less than 2^exp.
+ *
+ * If number is infinite, the call to frexp is legal but the contents of
+ * are exponent unspecified. */
+ frexp(number, &exponent);
+
+ /* If the magnitude of number is strictly less than 2^63, the truncated
+ * version of number is guaranteed to be representable. The only
+ * representable integer for which this is not the case is INT64_MIN, but
+ * it is covered by the logic below. */
+ if (isfinite(number) && exponent <= 63) {
+ return (int64_t)number;
+ }
+
+ /* Handle infinities and finite numbers with magnitude >= 2^63. */
+ return signbit(number) ? INT64_MIN : INT64_MAX;
+
+#ifdef PROBLEMATIC_DOUBLE_PROMOTION_WARNING
+ENABLE_GCC_WARNING(double-promotion)
+#endif
+#ifdef PROBLEMATIC_FLOAT_CONVERSION_WARNING
+ENABLE_GCC_WARNING(float-conversion)
+#endif
+}
diff --git a/src/lib/math/fp.h b/src/lib/math/fp.h
new file mode 100644
index 0000000000..b35c18a1c7
--- /dev/null
+++ b/src/lib/math/fp.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_FP_H
+#define TOR_FP_H
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+double tor_mathlog(double d) ATTR_CONST;
+long tor_lround(double d) ATTR_CONST;
+int64_t tor_llround(double d) ATTR_CONST;
+int64_t clamp_double_to_int64(double number);
+
+#endif
diff --git a/src/lib/math/include.am b/src/lib/math/include.am
new file mode 100644
index 0000000000..b088b3f3cc
--- /dev/null
+++ b/src/lib/math/include.am
@@ -0,0 +1,20 @@
+
+noinst_LIBRARIES += src/lib/libtor-math.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-math-testing.a
+endif
+
+src_lib_libtor_math_a_SOURCES = \
+ src/lib/math/fp.c \
+ src/lib/math/laplace.c
+
+
+src_lib_libtor_math_testing_a_SOURCES = \
+ $(src_lib_libtor_math_a_SOURCES)
+src_lib_libtor_math_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_math_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+noinst_HEADERS += \
+ src/lib/math/fp.h \
+ src/lib/math/laplace.h
diff --git a/src/lib/math/laplace.c b/src/lib/math/laplace.c
new file mode 100644
index 0000000000..8e45a1fb33
--- /dev/null
+++ b/src/lib/math/laplace.c
@@ -0,0 +1,67 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "lib/math/laplace.h"
+#include "lib/math/fp.h"
+
+#include "lib/log/util_bug.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+/** Transform a random value <b>p</b> from the uniform distribution in
+ * [0.0, 1.0[ into a Laplace distributed value with location parameter
+ * <b>mu</b> and scale parameter <b>b</b>. Truncate the final result
+ * to be an integer in [INT64_MIN, INT64_MAX]. */
+int64_t
+sample_laplace_distribution(double mu, double b, double p)
+{
+ double result;
+ tor_assert(p >= 0.0 && p < 1.0);
+
+ /* This is the "inverse cumulative distribution function" from:
+ * http://en.wikipedia.org/wiki/Laplace_distribution */
+ if (p <= 0.0) {
+ /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler
+ * options can cause the program to trap. */
+ return INT64_MIN;
+ }
+
+ result = mu - b * (p > 0.5 ? 1.0 : -1.0)
+ * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
+
+ return clamp_double_to_int64(result);
+}
+
+/** Add random noise between INT64_MIN and INT64_MAX coming from a Laplace
+ * distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b> to
+ * <b>signal</b> based on the provided <b>random</b> value in [0.0, 1.0[.
+ * The epsilon value must be between ]0.0, 1.0]. delta_f must be greater
+ * than 0. */
+int64_t
+add_laplace_noise(int64_t signal_, double random_, double delta_f,
+ double epsilon)
+{
+ int64_t noise;
+
+ /* epsilon MUST be between ]0.0, 1.0] */
+ tor_assert(epsilon > 0.0 && epsilon <= 1.0);
+ /* delta_f MUST be greater than 0. */
+ tor_assert(delta_f > 0.0);
+
+ /* Just add noise, no further signal */
+ noise = sample_laplace_distribution(0.0,
+ delta_f / epsilon,
+ random_);
+
+ /* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */
+ if (noise > 0 && INT64_MAX - noise < signal_)
+ return INT64_MAX;
+ else if (noise < 0 && INT64_MIN - noise > signal_)
+ return INT64_MIN;
+ else
+ return signal_ + noise;
+}
diff --git a/src/lib/math/laplace.h b/src/lib/math/laplace.h
new file mode 100644
index 0000000000..b22862e64a
--- /dev/null
+++ b/src/lib/math/laplace.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_LAPLACE_H
+#define TOR_LAPLACE_H
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+int64_t sample_laplace_distribution(double mu, double b, double p);
+int64_t add_laplace_noise(int64_t signal, double random, double delta_f,
+ double epsilon);
+
+#endif