/*
** Rolf Riesen, February 2007 and February 2008
**
** The idea for this problem is from Michael J. Quinn's book.
**
** Solution to homework problem 2 of the third homework.
** We need to find how many ID numbers are available given the following
** constraints:
**     - ID numbers are seven-digit combinations of the numerals 0--9
**     - Two consecutive digits may not be the same. Leading consecutive zeros are OK. ID 0000000 is OK by this rule.
**     - The sum of the digits may not be 7, 11, 13, 21, or 33
**     - The seven digits cannot all be even or odd. Again, ignore leading zeros.
**
** We do domain decomposition based on the input: Given P processors, we
** assign numbers 0..(9999999 / P - 1) to processor 0,
** (9999999 / P)...(2 * 9999999 / P - 1) to processor 1, and so on.
*/
#include <stdio.h>
#include <mpi.h>

#define MAXNUM	(9999999)
#define FALSE	(0)
#define TRUE	(1)


/*
** Local functions
*/
int digitsum(int n);
int consecutive(int n);
int evenodd(int n);



int
main(int argc, char *argv[])
{

int my_rank, nnodes;
int count, total_count;
int start, end;
int sum;
int i;
#if defined(DEBUG)
int debug= 1;
#else
int debug= 0;
#endif


    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &nnodes);

    /*
    ** Based on my rank, figure out the range I'm responsible for
    */
    start= my_rank * MAXNUM / nnodes;
    end= ((my_rank + 1) * MAXNUM) / nnodes - 1;

    if (my_rank == (nnodes - 1))   {
	/* The last node must take care of the very last number */
	end= end + 1;
    }
    printf("[%03d] I will work on numbers %7d through %7d\n", my_rank, start, end);

    count= 0;
    /*
    ** Start counting according to the rules for ID numbers
    */
    for (i= start; i <= end; i++)   {
	if (consecutive(i))   {
	    /* Not a valid ID number. Try the next one. */
	    if (debug)   {
		printf("[%3d] Rejecting ID %7d because (identical consecutive digits)\n", my_rank, i);
	    }
	    continue;
	}

	if (evenodd(i))   {
	    /* Not a valid ID number. Try the next one. */
	    if (debug)   {
		printf("[%3d] Rejecting ID %7d because (all digits even or odd)\n", my_rank, i);
	    }
	    continue;
	}

	sum= digitsum(i);
	if ((sum == 7) || (sum == 11) || (sum == 13) ||
            (sum == 21) || (sum == 33))   {
	    /* Not a valid ID number. Try the next one. */
	    if (debug)   {
		printf("[%3d] Rejecting ID %7d because (digit sum is 7, 11, 13, 21, or 33)\n", my_rank, i);
	    }
	    continue;
	}

	/* i is a valid ID number */
	count++;
    }

    if (debug)   {
	printf("[%03d] Found %7d valid ID numbers\n", my_rank, count);
    }

    /* Combine the counts on node 0 and print */
    MPI_Reduce(&count, &total_count, 1, MPI_INTEGER, MPI_SUM, 0, MPI_COMM_WORLD);

    if (my_rank == 0)   {
	printf("In the range from 0 to %d there are %d valid ID numbers\n",
	    MAXNUM, total_count);
    }


    MPI_Finalize();
    return 0;

}  /* end of main() */


/*
** Are there consecutive digits in n that are identical?
*/
int
consecutive(int n)
{
int cur_digit;
int last_digit;
int max_digit;
int i;


    if (n == 0)   {
        return FALSE;
    }

    max_digit= 0;
    cur_digit= n;
    while(cur_digit)   {
	cur_digit= cur_digit / 10;
	max_digit++;
    }

    last_digit= -1;
    for (i= 0; i < max_digit; i++)   {
	cur_digit= n % 10;
	if (cur_digit == last_digit)   {
	    return TRUE;
	}
	n = n / 10;
	last_digit= cur_digit;
    }

    return FALSE;

}  /* end of consecutive() */



/*
** Make sure not all digits are even or odd
*/
int
evenodd(int n)
{
int cur_digit;
int parity;
int max_digit;
int i;


    if (n == 0)   {
        return TRUE;
    }

    max_digit= 0;
    cur_digit= n;
    while(cur_digit)   {
	cur_digit= cur_digit / 10;
	max_digit++;
    }

    parity= (n % 10) % 2;
    for (i= 0; i < max_digit; i++)   {
	cur_digit= n % 10;
	if ((cur_digit % 2) != parity)   {
	    return FALSE;
	}
	n = n / 10;
    }

    return TRUE;

}  /* end of evenodd() */



/*
** Add up the individual digits of n
*/
int
digitsum(int n)
{

int sum;
int last_digit;


    sum= 0;
    while(n)   {
	last_digit= n % 10;
	sum= sum + last_digit;
	n = n / 10;
    }

    return sum;

}  /* end of digitsum() */
