-rw-r--r-- 3102 cryptattacktester-20230614/queue_prob.cpp raw
#include <cassert>
#include <vector>
#include "queue_prob.h"
using namespace std;
// generator runs G times
// each time producing an event with probability p
// which is then inserted into a queue
// output: chances of 0, 1, 2, ..., Q-1 entries in the queue
// i.e., coefficients of x^0,...,x^(Q-1) in (1-p+px)^G
vector<bigfloat> queue_distribution(bigint G,bigfloat p,bigint Q)
{
assert(Q >= 0);
assert(G >= 0);
vector<bigfloat> phi(Q,0);
if (Q >= 1) phi.at(0) = 1;
if (G == 0) return phi;
bigfloat p1m = 1-p;
// XXX: can be faster to use the bigfloat version below
vector<bigfloat> base(Q,0);
if (Q >= 1) base.at(0) = p1m;
if (Q >= 2) base.at(1) = p;
for (bigint pos = nbits(G)-1;pos >= 0;--pos) {
vector<bigfloat> newphi(Q);
for (bigint e = 0;e < Q;++e) newphi.at(e) = 0;
for (bigint e = 0;e < Q;++e)
for (bigint d = 0;d+e < Q;++d)
newphi.at(d+e) += phi.at(d)*phi.at(e);
if (G.bit(pos)) {
for (bigint e = 0;e < Q;++e) phi.at(e) = 0;
for (bigint e = 0;e < Q;++e)
for (bigint d = 0;d+e < Q;++d)
phi.at(d+e) += newphi.at(d)*base.at(e);
} else {
for (bigint e = 0;e < Q;++e) phi.at(e) = newphi.at(e);
}
}
return phi;
}
vector<bigfloat> queue_distribution(bigfloat G,bigfloat p,bigint Q)
{
assert(Q >= 0);
vector<bigfloat> phi(Q,0);
if (Q >= 1) phi.at(0) = 1;
if (G.definitely_zero()) return phi;
bigfloat p1m = 1-p;
vector<bigfloat> ppow;
ppow.push_back(1);
for (bigint e = 0;e < Q;++e) ppow.push_back(ppow.at(e)*p);
vector<bigfloat> p1mpow;
p1mpow.push_back(1);
for (bigint e = 0;e < Q;++e) p1mpow.push_back(p1mpow.at(e)*p1m);
bigfloat p1mpowGQ1 = pow(p1m,G-(Q-1));
for (bigint e = 0;e < Q;++e) {
// phi_e = binomial(G,e) p^e (1-p)^(G-e)
phi.at(e) = binomial_bigfloat(G,e)*ppow.at(e)*p1mpow.at(Q-1-e)*p1mpowGQ1;
}
return phi;
}
// generator runs G times
// each time producing an event with probability p
// which is then inserted into a queue
// but queue is limited to length Q
// output: average number of events in queue
bigfloat queue_average(bigint G,bigfloat p,bigint Q)
{
if (G <= 0) return 0;
if (G <= Q) return p*bigfloat(G);
// define phi = (1-p+px)^G
// then phi_e = chance of exactly e events being produced
// so on average queue keeps X events
// where X = sum_{e} min{e,Q} phi_e
//
// have sum_e Q phi_e = Q phi(1) = Q
// so sum_{e} (Q-min{e,Q}) phi_e = Q-X
// and this sum has the advantage of being supported on e<Q
auto phi = queue_distribution(G,p,Q);
bigfloat result = 0;
for (bigint e = 0;e < Q;++e)
result += (Q-e)*phi.at(e);
result = Q-result;
result = bigfloat_guarantee_nonnegative(result);
return result;
}
bigfloat queue_average(bigfloat G,bigfloat p,bigint Q)
{
if (!G.definitely_positive()) return 0;
if (!bigfloat(Q).definitely_less(G)) return p*G;
auto phi = queue_distribution(G,p,Q);
bigfloat result = 0;
for (bigint e = 0;e < Q;++e)
result += (Q-e)*phi.at(e);
result = Q-result;
result = bigfloat_guarantee_nonnegative(result);
return result;
}