laplace.c 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. /* Copyright (c) 2003, Roger Dingledine
  2. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  3. * Copyright (c) 2007-2018, The Tor Project, Inc. */
  4. /* See LICENSE for licensing information */
  5. /**
  6. * \file laplace.c
  7. *
  8. * \brief Implements a Laplace distribution, used for adding noise to things.
  9. **/
  10. #include "orconfig.h"
  11. #include "lib/math/laplace.h"
  12. #include "lib/math/fp.h"
  13. #include "lib/log/util_bug.h"
  14. #include <math.h>
  15. #include <stdlib.h>
  16. /** Transform a random value <b>p</b> from the uniform distribution in
  17. * [0.0, 1.0[ into a Laplace distributed value with location parameter
  18. * <b>mu</b> and scale parameter <b>b</b>. Truncate the final result
  19. * to be an integer in [INT64_MIN, INT64_MAX]. */
  20. int64_t
  21. sample_laplace_distribution(double mu, double b, double p)
  22. {
  23. double result;
  24. tor_assert(p >= 0.0 && p < 1.0);
  25. /* This is the "inverse cumulative distribution function" from:
  26. * http://en.wikipedia.org/wiki/Laplace_distribution */
  27. if (p <= 0.0) {
  28. /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler
  29. * options can cause the program to trap. */
  30. return INT64_MIN;
  31. }
  32. result = mu - b * (p > 0.5 ? 1.0 : -1.0)
  33. * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
  34. return clamp_double_to_int64(result);
  35. }
  36. /** Add random noise between INT64_MIN and INT64_MAX coming from a Laplace
  37. * distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b> to
  38. * <b>signal</b> based on the provided <b>random</b> value in [0.0, 1.0[.
  39. * The epsilon value must be between ]0.0, 1.0]. delta_f must be greater
  40. * than 0. */
  41. int64_t
  42. add_laplace_noise(int64_t signal_, double random_, double delta_f,
  43. double epsilon)
  44. {
  45. int64_t noise;
  46. /* epsilon MUST be between ]0.0, 1.0] */
  47. tor_assert(epsilon > 0.0 && epsilon <= 1.0);
  48. /* delta_f MUST be greater than 0. */
  49. tor_assert(delta_f > 0.0);
  50. /* Just add noise, no further signal */
  51. noise = sample_laplace_distribution(0.0,
  52. delta_f / epsilon,
  53. random_);
  54. /* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */
  55. if (noise > 0 && INT64_MAX - noise < signal_)
  56. return INT64_MAX;
  57. else if (noise < 0 && INT64_MIN - noise > signal_)
  58. return INT64_MIN;
  59. else
  60. return signal_ + noise;
  61. }