partitions.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #define _GNU_SOURCE
  2. #define MAX_STR_LEN 2048
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <math.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <igraph/igraph.h>
  9. #define max(a,b) ({ \
  10. __typeof__ (a) _a = (a); \
  11. __typeof__ (b) _b = (b); \
  12. _a > _b ? _a : _b; \
  13. })
  14. void usage(const char *progname, FILE *os) {
  15. fprintf(os, "Usage: %s <TOTAL> <DENOMINATION> ...\n", progname);
  16. }
  17. int cmp_double(const void *a, const void *b) {
  18. return (*(double*)a > *(double*)b) - (*(double*)a < *(double*)b);
  19. }
  20. double parse_arg(const char *a) {
  21. char *check_ptr;
  22. errno = 0;
  23. double val = strtod(a, &check_ptr);
  24. int e = errno;
  25. if(e) {
  26. fprintf(stderr, "Error %s: %s\n", strerrorname_np(e), strerror(e));
  27. exit(1);
  28. }
  29. if(check_ptr == a) {
  30. fprintf(stderr, "Error: '%s' not parsed as numeric.\n", check_ptr);
  31. exit(1);
  32. }
  33. if(val < 0.0) {
  34. fprintf(stderr, "Error: Negative input detected\n");
  35. exit(1);
  36. }
  37. if(check_ptr[0] != '\0') {
  38. fprintf(stderr, "Warning: Discarding non-numeric parts '%s' of '%s'\n", check_ptr, a);
  39. }
  40. return val;
  41. }
  42. void partitions(char *prefix, const double total, const double *denominations, const int ndenoms) {
  43. if(strnlen(prefix, MAX_STR_LEN) >= MAX_STR_LEN-1) {
  44. printf("WARNING: string of results is too long, truncation may be occurring.\n");
  45. }
  46. if(cmp_double(&total, &denominations[0]) < 0) {
  47. // We assume the denominations were pre-processed into sorted & unique order, so if the total is smaller than the smallest, there is no more partitioning to do.
  48. printf("%s\n", prefix);
  49. memset(prefix, '\0', MAX_STR_LEN);
  50. return;
  51. }
  52. // There are two things to do: assign a multiple of the biggest denomination to the total space, and recursively calculate what smaller denominations can go in the remaining space.
  53. int biggest_denom = 0;
  54. while(cmp_double(&total, &denominations[biggest_denom]) > 0 && biggest_denom < ndenoms) {
  55. ++biggest_denom;
  56. }
  57. // The previous check always goes one step too far, so pull back.
  58. if(biggest_denom > 0) {
  59. --biggest_denom;
  60. }
  61. int times = (int)floor(total/denominations[biggest_denom]);
  62. //printf("\nProcessing total = %f, biggest denomination = %f, number of denominations = %d. Current prefix = '%s'\n", total, denominations[biggest_denom], ndenoms, prefix);
  63. size_t prefix_len = strnlen(prefix, MAX_STR_LEN);
  64. if(ndenoms == 1) {
  65. // If there is only one denomination available, greedily fill the available space
  66. for(int p = 0; p < times; ++p) {
  67. char buf[MAX_STR_LEN];
  68. memset(buf, '\0', MAX_STR_LEN);
  69. strfromd(buf, MAX_STR_LEN, "%f", denominations[biggest_denom]);
  70. if(prefix_len > 0 || p > 0) {
  71. strcat(prefix, " ");
  72. }
  73. strncat(prefix, buf, 32);
  74. }
  75. printf("%s\n", prefix);
  76. memset(prefix, '\0', MAX_STR_LEN);
  77. } else if(ndenoms > 1) {
  78. for(int p_outer = times; p_outer >= 0; --p_outer) {
  79. char buf_outer[MAX_STR_LEN];
  80. memset(buf_outer, '\0', MAX_STR_LEN);
  81. strncpy(buf_outer, prefix, MAX_STR_LEN);
  82. for(int p_inner = 0; p_inner < p_outer; ++p_inner) {
  83. char buf_inner[MAX_STR_LEN];
  84. memset(buf_inner, '\0', MAX_STR_LEN);
  85. strfromd(buf_inner, MAX_STR_LEN, "%f", denominations[biggest_denom]);
  86. if(prefix_len > 0 || p_inner > 0) {
  87. strcat(buf_outer, " ");
  88. }
  89. strncat(buf_outer, buf_inner, 32);
  90. }
  91. partitions(buf_outer, total - (double)p_outer*denominations[biggest_denom], denominations, ndenoms-1);
  92. }
  93. }
  94. }
  95. int main(int argc, char **argv) {
  96. if(argc < 3) {
  97. usage(argv[0], stderr);
  98. exit(1);
  99. }
  100. double total = parse_arg(argv[1]);
  101. double *denominations = (double *)malloc((argc-2)*sizeof(double));
  102. if(denominations == NULL) {
  103. fprintf(stderr, "Error allocating memory for denominations\n");
  104. exit(1);
  105. }
  106. unsigned int argnum = 2;
  107. while(argnum < argc) {
  108. denominations[argnum-2] = parse_arg(argv[argnum]);
  109. ++argnum;
  110. }
  111. int ndenoms = 1;
  112. if(argc > 3) {
  113. qsort(denominations, argc-2, sizeof(double), cmp_double);
  114. // overwrite dups
  115. int d = 1;
  116. while(d < argc-2) {
  117. while(denominations[d] == denominations[d-1] && ++d < argc-2);
  118. if(d >= argc-2) {
  119. break;
  120. }
  121. ++ndenoms;
  122. denominations[ndenoms-1] = denominations[d];
  123. ++d;
  124. }
  125. }
  126. char prefix[MAX_STR_LEN];
  127. memset(prefix, '\0', MAX_STR_LEN);
  128. partitions(prefix, total, denominations, ndenoms);
  129. if(denominations != NULL) {
  130. free(denominations);
  131. }
  132. return 0;
  133. }