import std.stdio: writeln;
import std.conv: text;
import std.random: uniform, Xorshift;
import std.algorithm: min, max;
import std.parallelism: task;
import core.thread: Thread;
import core.sync.mutex: Mutex;
import core.time: dur;
__gshared uint randomizeCount, equalizeCount;
final class Buckets {
alias TBucketValue = uint;
private final struct Bucket {
TBucketValue value;
Mutex mtx;
}
private Bucket[] buckets;
public this(in uint count) {
this.buckets.length = count;
foreach (ref b; buckets)
b = Bucket(uniform(0, 100), new Mutex());
}
public TBucketValue opIndex(in uint index) const pure nothrow {
return buckets[index].value;
}
public void transfer(in uint from, in uint to,
in TBucketValue amount) {
buckets[min(from, to)].mtx.lock();
buckets[max(from, to)].mtx.lock();
immutable realAmount = min(buckets[from].value, amount);
buckets[from].value -= realAmount;
buckets[to ].value += realAmount;
buckets[min(from, to)].mtx.unlock();
buckets[max(from, to)].mtx.unlock();
}
@property size_t length() const pure nothrow {
return this.buckets.length;
}
void toString(in void delegate(const(char)[]) sink) {
TBucketValue sum = 0;
sink("(");
foreach (ref b; buckets) {
b.mtx.lock();
sum += b.value;
sink(text(b.value));
sink(" ");
}
sink(") ");
sink(text(sum));
foreach (ref b; buckets)
b.mtx.unlock();
}
}
void randomize(Buckets data) {
immutable maxi = data.length - 1;
auto rng = Xorshift(1);
while (true) {
immutable i = uniform(0, maxi, rng);
immutable j = uniform(0, maxi, rng);
immutable amount = uniform(0, 20, rng);
data.transfer(i, j, amount);
randomizeCount++;
}
}
void equalize(Buckets data) {
immutable maxi = data.length - 1;
auto rng = Xorshift(1);
while (true) {
immutable i = uniform(0, maxi, rng);
immutable j = uniform(0, maxi, rng);
immutable a = data[i];
immutable b = data[j];
if (a > b)
data.transfer(i, j, (a - b) / 2);
else
data.transfer(j, i, (b - a) / 2);
equalizeCount++;
}
}
void display(Buckets data) {
while (true) {
writeln(randomizeCount, " ", equalizeCount, " ", data);
randomizeCount = 0;
equalizeCount = 0;
Thread.sleep(dur!"seconds"(3));
}
}
void main() {
auto data = new Buckets(20);
writeln("n. randomize, n. equalize, buckets, buckets sum:");
task!randomize(data).executeInNewThread();
task!equalize(data).executeInNewThread();
task!display(data).executeInNewThread();
}