/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2020 Telegram Systems LLP */ #include "LossSender.h" #include "td/utils/logging.h" #if TON_HAVE_GSL #include #endif #include namespace ton { namespace rldp2 { namespace { // works for 1e-x, where x in {1..10} double ndtri_fast(double p) { if (p < 2e-10) { return 6.361340902404; } if (p < 2e-9) { return 5.997807015008; } if (p < 2e-8) { return 5.612001244175; } if (p < 2e-7) { return 5.199337582193; } if (p < 2e-6) { return 4.753424308823; } if (p < 2e-5) { return 4.264890793923; } if (p < 2e-4) { return 3.719016485456; } if (p < 2e-3) { return 3.090232306168; } if (p < 2e-2) { return 2.326347874041; } return 1.281551565545; } } // namespace LossSender::LossSender(double loss, double p) : loss_(loss), p_(p) { v_.resize(2); v_[0] = 1; res_.push_back(0); S_ = ndtri_fast(p_); sigma_ = p * (1 - p); //LOG(ERROR) << S_ << " " << ndtri(1 - p_); //CHECK(fabs(S_ - ndtri(1 - p_)) < 1e-6); } int LossSender::send_n(int n) { if (n < 50) { return send_n_exact(n); } return send_n_approx_nbd(n); } int LossSender::send_n_exact(int n) { while ((int)res_.size() <= n) { step(); } return res_[n]; } int LossSender::send_n_approx_norm(int n) { double a = (1 - loss_) * (1 - loss_); double b = loss_ * (loss_ - 1) * (2 * n + S_ * S_); double c = loss_ * loss_ * n * n + S_ * S_ * n * loss_ * (loss_ - 1); double x = ((-b + sqrt(b * b - 4 * a * c)) / (2 * a)); return (int)(x + n + 1); } int LossSender::send_n_approx_nbd(int n) { #if TON_HAVE_GSL auto mean = n * loss_ / (1 - loss_); auto var = sqrt(mean / (1 - loss_)); auto min_k = static_cast(mean + var); auto max_k = min_k + static_cast(var + 1) * 15; while (min_k + 1 < max_k) { int k = (min_k + max_k) / 2; if (gsl_cdf_negative_binomial_P(k, 1 - loss_, n) > 1 - p_) { max_k = k; } else { min_k = k; } } return max_k + n; #endif return send_n_approx_norm(n); } int LossSender::send_n_approx_pd(int n) { #if TON_HAVE_GSL for (int k = 0;; k++) { if (gsl_cdf_poisson_P(k, (n + k) * loss_) > 1 - p_) { return k + n; } } #endif return send_n_approx_norm(n); } bool LossSender::has_good_approx() { #if TON_HAVE_GSL return true; #else return false; #endif } void LossSender::step() { n_++; v_.push_back(0); v_[n_] = v_[n_ - 1]; for (int j = n_; j >= 0; j--) { v_[j + 1] += v_[j] * loss_; v_[j] *= (1 - loss_); } while (res_i_ < n_ && v_[res_i_] < 1 - p_) { res_i_++; } auto left_ = n_ - res_i_; if ((int)res_.size() == left_) { res_.push_back(n_); } } } // namespace rldp2 } // namespace ton