#include "Random.hh"
#include "kofn.hh"
static void
_kOfN_largeK (int k, int n, int* values)
{
assert (k > 0);
assert (k <= n);
int j = 0;
for (int i = 0; i < n; i++) {
double prob = (double) (k - j) / (n - i);
assert (prob <= 1);
double x = Random::rand.fp ();
if (x < prob) {
values[j++] = i;
}
}
assert (j == k);
}
static void
_kOfN_smallK (int k, int n, int* values)
{
assert (k > 0);
assert (k <= n);
if (k == 1) {
values[0] = Random::rand.i32 (0, n - 1);
return;
}
int leftN = n / 2;
int rightN = n - leftN;
int leftK = 0;
int rightK = 0;
for (int i = 0; i < k; i++) {
int x = Random::rand.i32 (0, n - i - 1);
if (x < leftN - leftK) {
leftK++;
} else {
rightK++;
}
}
if (leftK > 0) { _kOfN_smallK (leftK, leftN, values); }
if (rightK > 0) { _kOfN_smallK (rightK, rightN, values + leftK); }
for (int i = leftK; i < k; i++) {
values[i] += leftN;
}
}
void
kOfN (int k, int n, int* values)
{
assert (k >= 0);
assert (n >= 0);
if (k == 0) { return; }
static double log2 = log (2);
double klogk = k * log (k) / log2;
if (klogk < n / 2) {
_kOfN_smallK (k, n, values);
} else {
_kOfN_largeK (k, n, values);
}
}