#include <stdlib>
#include <fstream>
#include <kernel>
#include <thread>
#include <unistd.h>					// getpid

monitor Buffer {
    condition full, empty;
    int front, back, count;
    int elements[20];
};

void ?{}( Buffer & buffer ) {
    buffer.front = buffer.back = buffer.count = 0;
}

int query( Buffer & buffer ) { return buffer.count; }

void insert( Buffer & mutex buffer, int elem ) {
    if ( buffer.count == 20 ) wait( &buffer.empty );
    buffer.elements[buffer.back] = elem;
    buffer.back = ( buffer.back + 1 ) % 20;
    buffer.count += 1;
    signal( &buffer.full );
}
int remove( Buffer & mutex buffer ) {
    if ( buffer.count == 0 ) wait( &buffer.full );
    int elem = buffer.elements[buffer.front];
    buffer.front = ( buffer.front + 1 ) % 20;
    buffer.count -= 1;
    signal( &buffer.empty );
    return elem;
}

thread Producer {
    Buffer * buffer;
    unsigned int N;
};
void main( Producer & prod ) {
    for ( int i = 1; i <= prod.N; i += 1 ) {
	yield( (unsigned int)rand48() % 5 );
	insert( *prod.buffer, 1 );
    } // for
    insert( *prod.buffer, -1 );
}
void ?{}( Producer & prod, Buffer * buffer, unsigned int N ) {
    prod.buffer = buffer;
    prod.N = N;
}

thread Consumer {
    Buffer * buffer;
    int * sum;						// summation of producer values
};
void main( Consumer & cons ) {
    *cons.sum = 0;
    for ( ;; ) {
	yield( (unsigned int)rand48() % 5 );
	int item = remove( *cons.buffer );
      if ( item == -1 ) break;				// sentinel ?
    	*cons.sum += item;
    } // for
}
void ?{}( Consumer & cons, Buffer * buffer, int * sum ) {
    cons.buffer = buffer;
    cons.sum = sum;
}

int main() {
    Buffer buffer;
    enum { Prods = 5, Cons = 5 };
    Producer * prods[Prods];
    Consumer * cons[Cons];
    const int Sentinel = -1;
    int sums[Cons];
    int i;
    processor p;

    //rand48seed( getpid() );
    rand48seed( 1003 );

    for ( i = 0; i < Cons; i += 1 ) {			// create consumers
	cons[i] = new( &buffer, &sums[i] );
    } // for
    for ( i = 0; i < Prods; i += 1 ) {			// create producers
	prods[i] = new( &buffer, 100000u );
    } // for

    for ( i = 0; i < Prods; i += 1 ) {			// wait for producers to finish
	delete( prods[i] );
    } // for
    for ( i = 0; i < Cons; i += 1 ) {			// generate sentinal values to stop consumers
	insert( buffer, Sentinel );
    } // for
    int sum = 0;
    for ( i = 0; i < Cons; i += 1 ) {			// wait for consumers to finish
	delete( cons[i] );
	sum += sums[i];
    } // for
    sout | "total:" | sum | endl;
}
