#include <stdio.h>
#include <stdlib.h>
#define ADIOI_MIN(a, b) ((a) < (b) ? (a) : (b))

// ========== The new algorithm I propose ===============================================
long int algo1(long int CO, long int stripe_count, long int nprocs_for_coll) {
 long int CO_nodes, divisor, avail_cb_nodes;

    /* Calculate how many IO clients we need */
    /* To avoid extent lock conflicts,
     * avail_cb_nodes should either be a multiple of stripe_count,
			     or divide stripe_count exactly.
     * so that each OST is accessed by a maximum of CO constant clients. */

 if (nprocs_for_coll/stripe_count != 0) 
	/* avail_cb_nodes should be a multiple of stripe_count and the number of procs per OST
	   should be limited to the minimum between nprocs_for_coll/stripe_count and CO
	   e.g. if stripe_count=20, nprocs_for_coll=42 and CO=3 then avail_cb_nodes should be egal to 40 */
	avail_cb_nodes = stripe_count * ADIOI_MIN(nprocs_for_coll/stripe_count, CO);
 else {
	/* nprocs_for_coll is less than stripe_count */
	/* avail_cb_nodes should divide stripe_count */
	/* e.g. if stripe_count=60 and nprocs_for_coll=8 then avail_cb_nodes should be egal to 6 */
	/* This could be done with :
		while (stripe_count % avail_cb_nodes != 0) avail_cb_nodes--;
	   but this can be optimized for large values of nprocs_for_coll and stripe_count */
	divisor = 2;
	avail_cb_nodes = 1;
	/* try to divise */
	while (stripe_count >= divisor*divisor) {
		if ((stripe_count % divisor) == 0) {
			if (stripe_count/divisor <= nprocs_for_coll) {
				/* The value is found ! */
				avail_cb_nodes = stripe_count/divisor;
				break;
			}
			/* if divisor is less than nprocs_for_coll, divisor is a solution, but it is not sure that it is the best one */
			else if (divisor <= nprocs_for_coll) avail_cb_nodes = divisor;
		}
	divisor++;
	}
 }
 return avail_cb_nodes;
}

// ============================== With MPICH2 (With last patch from LiuYing) ====================
long int algo2(long int CO, long int stripe_count, long int nprocs_for_coll) {
 long int CO_nodes, divisor, avail_cb_nodes, CO_max;

 CO_max = (nprocs_for_coll - 1)/ stripe_count + 1;
 CO = ADIOI_MIN(CO_max, CO);

    /* Calculate how many IO clients we need */
    /* To avoid extent lock conflicts,
     * avail_cb_nodes should divide (stripe_count*CO) exactly,
     * so that each OST is accessed by only one or more constant clients. */

 CO_nodes = stripe_count * CO;
 avail_cb_nodes = ADIOI_MIN(nprocs_for_coll, CO_nodes);

 if (avail_cb_nodes <  CO_nodes) {
	do {
		/* find the divisor of CO_nodes */
		divisor = 1;
		do {
			divisor ++;
		} while (CO_nodes % divisor);
		/* if stripe_count*CO is a prime number, change nothing */
		CO_nodes = CO_nodes / divisor;
		if ((CO_nodes <= avail_cb_nodes) && (CO_nodes != 1)) {
			avail_cb_nodes = CO_nodes;
			break;
		}
	} while (CO_nodes != 1);
 }
 return avail_cb_nodes;
}

void compare_algorithmes(long int CO,  long int stripe_count, long int nprocs_for_coll) {
 long int avail_cb_nodes1, avail_cb_nodes2;

 avail_cb_nodes1 = algo1(CO, stripe_count, nprocs_for_coll);
 avail_cb_nodes2 = algo2(CO, stripe_count, nprocs_for_coll);

 printf("    %3ld            %3ld               %3ld          %3ld", stripe_count, nprocs_for_coll, CO, avail_cb_nodes1);
 if (avail_cb_nodes2 != avail_cb_nodes1) printf("            algorithme 2 gives %3ld\n", avail_cb_nodes2);
 else printf("\n");
}

int main(int argc, char **argv) {
 long int CO, stripe_count, nprocs_for_coll, avail_cb_nodes1, avail_cb_nodes2, prev_avail_cb_nodes=0;

 if ((argc < 3) || (argc > 4)) {
	printf("usage: cb_nodes stripe_count nprocs_for_coll [CO]\n");
	return -1;
 }
 stripe_count = strtol(argv[1],0,10);
 nprocs_for_coll = strtol(argv[2],0,10);
 if (argc == 4) CO = strtol(argv[3],0,10);
 else CO = 1;

 printf("stripe_count     nprocs_for_coll       CO     avail_cb_nodes       DIFFERENCES\n");
 if (stripe_count && nprocs_for_coll && CO) {
	compare_algorithmes(CO, stripe_count, nprocs_for_coll);
 }
 else {
	for (CO = 1; CO < 6; CO++) {
 		for (stripe_count = 1; stripe_count < 130; stripe_count++) {
			for (nprocs_for_coll = 2; nprocs_for_coll < CO*stripe_count+1; nprocs_for_coll++) {
				compare_algorithmes(CO, stripe_count, nprocs_for_coll);
			}
		}
	}
 }
}
