#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#define MAX_DICE 6
extern int errno;

struct __Perm {
	unsigned int *p;
	unsigned int members;
};

static uint32_t Permutations(uint32_t dice, uint32_t sides, struct __Perm *permutations){
	if(dice < 1)
		return 0;
	uint32_t num1;
	uint32_t num2;
	uint32_t num3;
	uint32_t num4;
	//uint32_t **ptr1 = NULL;
	//uint32_t *ptr1 = NULL;
	//uint32_t index = 0;
	//uint32_t cur_dice = 1;
	//uint32_t cur_dig = 0;

	//this will store the dice results, least significant digit first
	//uint32_t *dig_count = (uint32_t *)malloc( dice*sizeof(uint32_t) );
	//(void)memset((void *)dig_count, 1, dice*sizeof(uint32_t) );
	//sides^dice = permutations
	uint32_t perms = pow(sides, dice);
	//printf("perms: %u dice: %u\n", perms, dice);
	//sleep(2);
	//ptr1 = (*permutations);
// *(unsigned int *)

	permutations->p = malloc( sizeof(unsigned int *) * perms * dice );
	 //(*permutations) = malloc( sizeof(uint32_t *) * perms );
	//(*permutations) = malloc( perms*sizeof(uint32_t *) );
	//(*permutations) = (uint32_t **)malloc( 10000 );
	if( permutations->p == NULL) printf("Error: malloc(): %d\n", errno);
	//printf("here 1\n");

/*
	for(num1=0; num1<perms; num1++){
		//((uint32_t *)permutations[num1]) = malloc( dice*sizeof(uint32_t) );
		//ptr2 = ptr1[num1];
//(int*&) p = &n;
		permutations->p[num1] = malloc( sizeof(unsigned int) * dice );
		//((uint32_t **)(*permutations))[num1] = malloc( sizeof(uint32_t) * dice );
		//(*permutations)[num1] = malloc( dice*sizeof(uint32_t) );
		//(*permutations)[num1] = malloc( dice*sizeof(uint32_t) );
		if( permutations->p[num1] == NULL)
		//if( ((uint32_t *)permutations[num1]) == NULL)
			printf("Error: malloc(): %d\n", errno);
		//printf("%d\n", num1);
	}
*/
//	printf("here 2\n");
    //412/6 = 68 remainder 4...  so the base 6 number is XXX4
    //68/6  = 11 remainder 2...  so the base 6 number is XX24
    //11/6  =  1 remainder 5...  so the base 6 number is X524
    //1/6   =  0 remainder 1...  so the base 6 number is 1524
	//Just count and convert base 10 to base sides (6)
	//for(num1=0; num1<perms; num1++){
	//	for(num2=0; num2<dice; num2++){
	//		(*permutations)[num1][num2] = 1;
	//	}
	//}
	for(num1=0; num1<perms; num1++){
		num3 = num1;
		for(num2=0; num2<dice; num2++){
			//num4 = num3/sides;
			num4 = num3 % sides;
			num3 /= sides;
			permutations->p[num1*dice+num2] = num4+1;
			//((uint32_t *)(*permutations)[num1])[num2] = num4+1;


//(uint32_t *)( *permutations[num1])[num2] = num4+1;
			//(uint32_t *)(*(permutations)[num1])[num2] = num4+1;
			// *(uint32_t *)(*permutations + num1)[num2] = num4+1;
			//ptr1 = num4+1;
			// *(ptr1 + num2) = num4+1;
			// *(*(permutations + num1) + num2) = num4+1;

//((uint32_t *)(*permutations)[num1])[num2] = num4+1;
			//(*permutations)[num1][num2] = num4+1;
			//ptr1[num1][num2] = num4+1;;
			//(*permutations)[num1])[num2] = num4+1;
			//((uint32_t *)(*permutations)[num1])[num2] = num4+1;
			//(*permutations)[num1][num2] = num4+1;
		}
	}
	//printf("here 3\n");
	//free(dig_count);
	permutations->members = perms;
	return perms;
}


static uint32_t Unique(uint32_t dice, uint32_t sides, struct __Perm *permutations, struct __Perm *unique){
	if(sides < 2)
		return 1;
	uint32_t num1, num2, num3, num4;
	uint32_t factorial = 1;
	uint32_t unique_count  = 1;
	for(num1 = 1; num1<sides; num1++){
		factorial *= num1*1;
	}
	for(num1 = 1; num1<sides; num1++){
		unique_count *= (dice+num1);
	}
	unique_count /= factorial;

	//printf("fact: %u uc: %u\n", factorial, unique_count);
	//uint32_t unique = (dice+5)*(dice+4)*(dice+3)*(dice+2)*(dice+1) / factorial;

	unique->p = malloc( unique_count*sizeof(unsigned int *)*dice );

	/*
	(*unique) = malloc( unique_count*sizeof(void *) );
	if((*unique) == NULL) printf("Error: malloc(): %d\n", errno);
	for(num1 = 0; num1<unique_count; num1++){
		(*unique)[num1] = malloc(dice*sizeof(uint32_t) );
		if((*unique)[num1] == NULL) printf("Error: malloc(): %d\n", errno);
	}
	*/

	uint32_t perms = pow(sides, dice);
	uint32_t match_found = 0;
	uint32_t unique_members = 0;
	size_t dig_c_size = sides*sizeof(uint32_t);
	uint32_t *dig_count1;
	dig_count1 = malloc( dig_c_size );
	uint32_t *dig_count2;
	dig_count2 = malloc( dig_c_size );
	(void)memset((void *)dig_count1, '\0', dig_c_size );
	(void)memset((void *)dig_count2, '\0', dig_c_size );
	//place first combination in unique array
	for(num1 = 0; num1<dice; num1++){
//num1*dice+num2
		unique->p[num1] = permutations->p[num1];
		//((uint32_t *)(*unique)[0])[num1] = ((uint32_t *)(*permutations)[0])[num1];
	}
	unique_members++;

	for(num1 = 0; num1<perms; num1++){
		match_found = 0;
		(void)memset((void *)dig_count1, '\0', dig_c_size );
		for(num2=0; num2<dice; num2++){
			num3 = (uint32_t)(permutations->p[num1*dice+num2]);
			//num3 = (uint32_t)(permutations->p[num1][num2]);
			//num3 = ((uint32_t *)(*permutations)[num1])[num2];
			dig_count1[num3]++;
		}
		for(num2=0; num2<unique_members; num2++){
			(void)memset((void *)dig_count2, '\0', dig_c_size );
			for(num3=0; num3<dice; num3++){
				num4 = (uint32_t)unique->p[num2*dice+num3];
				//num4 = ((uint32_t *)(*unique)[num2])[num3];
				dig_count2[num4]++;
			}
			if(memcmp( (const void *)dig_count1, (const void *)dig_count2, dig_c_size) == 0){
				match_found = 1;
				break;
			}
		}
		if(match_found == 0){
			for(num2=0; num2<dice; num2++){
				unique->p[unique_members*dice+num2] = permutations->p[num1*dice+num2];
				//((uint32_t *)(*unique)[unique_members])[num2] = (uint32_t)(permutations->p[num1*dice+num2]);
				//((uint32_t *)(*unique)[unique_members])[num2] = ((uint32_t *)(*permutations)[num1])[num2];
			}
			unique_members++;
		}
	}

	//free((void *)dig_count1);
	//free((void *)dig_count2);
	unique->members = unique_count;
	return unique_count;
}


static uint32_t Pattern_Times(uint32_t *pattern, uint32_t pat_len, uint32_t dice, uint32_t sides, struct __Perm *unique){
	if(dice < pat_len)
		return 0;
	uint32_t num1, num2;
	uint32_t times = 0;
	uint32_t members = unique->members;
	size_t dig_c_size = sides*sizeof(uint32_t);
	uint32_t *dig_count1;
	uint32_t *dig_count2;
	dig_count1 = malloc( dig_c_size );
	dig_count2 = malloc( dig_c_size );
	(void)memset((void *)dig_count1, '\0', dig_c_size );
	(void)memset((void *)dig_count2, '\0', dig_c_size );

	for(num1=0; num1<pat_len; num1++){
		dig_count1[ pattern[num1] ]++;
	}
	for(num1=0; num1<members; num1++){
		(void)memset((void *)dig_count2, '\0', dig_c_size );
		for(num2=0; num2<dice; num2++){
			dig_count2[ unique->p[num1*dice+num2] ]++;
		}
		//if(memcmp( (const void *)dig_count1, (const void *)dig_count2, dig_c_size) == 0){
		if(dig_count2[1] != 0){
			if(dig_count1[1] <= dig_count2[1])
				times++;
		}
	}
	return times;
}

static uint32_t Three_Pair(uint32_t dice, uint32_t sides, struct __Perm *unique){
	if(dice < 6)
		return 0;
	uint32_t num1, num2;
	uint32_t times = 0;
	uint32_t members = unique->members;
	unsigned char two_c = 0;
	size_t dig_c_size = sides*sizeof(uint32_t);
	uint32_t *dig_count1;
	//uint32_t *dig_count2;
	dig_count1 = malloc( dig_c_size );
	//dig_count2 = malloc( dig_c_size );
	(void)memset((void *)dig_count1, '\0', dig_c_size );
	//(void)memset((void *)dig_count2, '\0', dig_c_size );

	//for(num1=1; num1<sides+1; num1++){
	//	for(num2=1; num2<4; num2++){
	//		
	//	}
	//}
	for(num1=0; num1<members; num1++){
		(void)memset((void *)dig_count1, '\0', dig_c_size );
		for(num2=0; num2<dice; num2++){
			dig_count1[ unique->p[num1*dice+num2] ]++;
		}
		two_c = 0;
		//if 3 are set to exactely 2
		for(num2=0; num2<sides; num2++){
			if(dig_count1[num2] >= 2){
				two_c++;
			}
		}
		if(two_c >= 3)
			times++;
	}
	return times;
}

int main(int argc, char **argv){
	uint32_t num1;
	struct __Perm permutations[MAX_DICE];
	struct __Perm unique[MAX_DICE];
	uint32_t unique_count[MAX_DICE];
	uint32_t sides = 6;
	uint32_t perms[MAX_DICE];
	uint32_t times = 0;
	uint32_t pattern[MAX_DICE];
	uint32_t pat_len = 0;

	printf("Dice:\n");
	for(num1=0; num1<MAX_DICE; num1++){
		printf("%u\n", num1+1);
		perms[num1] = Permutations(num1+1, sides, &permutations[num1]);
		printf("\tPermutations: %u\n", perms[num1]);

		unique_count[num1] = Unique(num1+1, sides, &permutations[num1], &unique[num1]);
		printf("\tCombinations: %u\n", unique_count[num1]);

		//for(num2=0; num2<perms; num2++){
		//	free(permutations[num2]);
		//}
		//free(permutations);
	}

	double prob = 0;
	for(num1=0; num1<MAX_DICE; num1++){
		pattern[num1] = 1;
	}
	//(void)memset((void *)pattern, 1, MAX_DICE*sizeof(uint32_t));
	//pattern = {1, 1, 1, 1, 1, 1};
	printf("Dice:\t\t 6\t\t 5\t\t 4\t\t 3\t\t 2\t\t 1\n");
	for(pat_len = MAX_DICE; pat_len>0; pat_len--){
		printf("pat_len %u:\t", pat_len);
		//for(num1=(MAX_DICE-1); num1>0; num1--){
		num1=MAX_DICE;
		while(num1 > 0){
			num1--;
			times = Pattern_Times(pattern, pat_len, num1+1, sides, &unique[num1]);
			prob = (double)times/unique[num1].members*100;
			if(prob < 10){
				printf(" %.04lf%%\t", prob );
			}else{
				printf("%.04lf%%\t", prob );
			}
		}
		printf("\n");
	}

	times = Three_Pair(6, sides, &unique[5]);
	prob = (double)times/unique[5].members*100;
	printf("3 pair: (%u)%.04lf%%\n", times, prob );

	for(num1=0; num1<MAX_DICE; num1++){
		//for(num2=0; num2<unique_count[num1]; num2++){
		//	free(unique[num1][num2]);
		//}
		free(unique[num1].p);
		//for(num2=0; num2<perms[num1]; num2++){
		//	free(permutations[num1].p);
		//}
		free(permutations[num1].p);
	}
	return 0;
}