*/
tt_i64_op(INT64_MIN + 20, ==,
add_laplace_noise(20, 0.0, delta_f, epsilon));
- tt_i64_op(-60, ==, add_laplace_noise(20, 0.1, delta_f, epsilon));
- tt_i64_op(-14, ==, add_laplace_noise(20, 0.25, delta_f, epsilon));
- tt_i64_op(20, ==, add_laplace_noise(20, 0.5, delta_f, epsilon));
- tt_i64_op(54, ==, add_laplace_noise(20, 0.75, delta_f, epsilon));
- tt_i64_op(100, ==, add_laplace_noise(20, 0.9, delta_f, epsilon));
- tt_i64_op(215, ==, add_laplace_noise(20, 0.99, delta_f, epsilon));
+
+ tt_assert(-60 == add_laplace_noise(20, 0.1, delta_f, epsilon));
+ tt_assert(-14 == add_laplace_noise(20, 0.25, delta_f, epsilon));
+ tt_assert(20 == add_laplace_noise(20, 0.5, delta_f, epsilon));
+ tt_assert(54 == add_laplace_noise(20, 0.75, delta_f, epsilon));
+ tt_assert(100 == add_laplace_noise(20, 0.9, delta_f, epsilon));
+ tt_assert(215 == add_laplace_noise(20, 0.99, delta_f, epsilon));
+
+ /* Test extreme values of signal with maximally negative values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.0000000000000002 is the double epsilon (error when calculating near 1)
+ * this is approximately 1/(2^52)
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * (let's not descend into the world of subnormals)
+ * >>> laplace.ppf([0, 0.0000000000000002], loc = 0, scale = 1)
+ * array([ -inf, -35.45506713])
+ */
+ const double noscale_df = 1.0, noscale_eps = 1.0;
+
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0, 0.0, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MIN? */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(-1, 0.0, noscale_df, noscale_eps));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(INT64_MIN, 0.0,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0, 0.0, delta_f, epsilon));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0, 0.0,
+ INT64_MAX, 1));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(INT64_MIN, 0.0,
+ INT64_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_assert((INT64_MIN + INT64_MAX) ==
+ add_laplace_noise(INT64_MAX, 0.0,
+ noscale_df, noscale_eps));
+
+ /* do near-zero fractional values work? */
+ const double min_dbl_error = 0.0000000000000002;
+
+ tt_assert(-35 ==
+ add_laplace_noise(0, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_assert((-35 + INT64_MAX) ==
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0, min_dbl_error,
+ INT64_MIN, -35));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0, min_dbl_error,
+ INT64_MIN, -34));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0, min_dbl_error,
+ INT64_MIN, 1));
+ tt_assert((INT64_MIN + INT64_MAX) ==
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ INT64_MIN, 1));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ INT64_MIN, 1));
+
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0, min_dbl_error,
+ INT64_MAX, -35));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0, min_dbl_error,
+ INT64_MAX, -34));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0, min_dbl_error,
+ INT64_MAX, 1));
+ tt_assert((INT64_MAX + INT64_MIN) ==
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ INT64_MAX, 1));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ INT64_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_assert((INT64_MAX - 35) ==
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+
+ /* Test extreme values of signal with maximally positive values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.9999999999999998 is the greatest number < 1 by calculation
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * >>> laplace.ppf([1.0, 0.9999999999999998], loc = 0, scale = 1)
+ * array([inf, 35.35050621])
+ * but the function rejects p == 1.0, so we just use max_dbl_lt_one
+ */
+ const double max_dbl_lt_one = 0.9999999999999998;
+
+ /* do near-one fractional values work? */
+ tt_assert(35 ==
+ add_laplace_noise(0, max_dbl_lt_one, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MAX? */
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(INT64_MAX - 35, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(INT64_MAX - 34, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ delta_f, epsilon));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0, max_dbl_lt_one,
+ INT64_MAX, 35));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0, max_dbl_lt_one,
+ INT64_MAX, 34));
+ tt_assert((INT64_MIN + INT64_MAX) ==
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ INT64_MAX, 1));
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ INT64_MAX, 1));
+ tt_assert((INT64_MAX + INT64_MIN) ==
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ INT64_MIN, 1));
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ INT64_MIN, 1));
+
+ /* does it play nice with INT64_MIN? */
+ tt_assert((INT64_MIN + 35) ==
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+
+ /* Test extreme values of b = delta_f / epsilon
+ * >>> laplace.ppf([0.5], loc = 0, scale = 1)
+ * array([0. ])
+ */
+
+ /* Make sure edge cases don't depend on architecture,
+ * optimisation level, or other compiler flags.
+ * XXXX Are these edge cases important enough to make consistent? */
+
+ /* b = positive zero, p yields positive zero */
+ tt_assert(0.0 ==
+ add_laplace_noise(0.0, 0.5, 0.0, 1.0))
+ /* b = negative zero, p yields positive zero */
+ tt_assert(0.0 ==
+ add_laplace_noise(0.0, 0.5, 0.0, -1.0))
+ /* b = positive infinity, p yields positive zero, result is -NaN -> -Inf */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.5, 1.0, 0.0))
+ /* b = negative infinity, p yields positive zero, result is -NaN -> -Inf */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.5, -1.0, 0.0))
+ /* b = positive NaN (rounded to -Inf), p yields positive zero,
+ * result is -NaN -> -Inf */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.5, -0.0, -0.0))
+ /* b = negative NaN (rounded to -Inf), p yields positive zero,
+ * result is -NaN -> -Inf*/
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.5, -0.0, 0.0))
+
+ /* b = positive zero, p yields negative infinity, result is -NaN -> -Inf */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.0, 0.0, 1.0))
+ /* b = negative zero, p yields negative infinity, result is -NaN -> -Inf */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.0, 0.0, -1.0))
+ /* b = positive infinity, p yields negative infinity */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.0, 1.0, 0.0))
+ /* b = negative infinity, p yields negative infinity */
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0.0, 0.0, -1.0, 0.0))
+ /* b = positive NaN (rounded to -Inf), p yields negative infinity,
+ * result is -NaN -> -Inf */
+ tt_assert(INT64_MIN ==
+ add_laplace_noise(0.0, 0.0, -0.0, -0.0))
+ /* b = negative NaN (rounded to -Inf), p yields negative infinity,
+ * result is NaN -> Inf */
+ tt_assert(INT64_MAX ==
+ add_laplace_noise(0.0, 0.0, -0.0, 0.0))
done:
;