Project Euler Solutions
math.h
Go to the documentation of this file.
1 #ifndef EULER_MATH_H
2 #define EULER_MATH_H
3 
4 #ifdef DOXYGEN
5 namespace c::include::math {
6 #endif
7 
8 #include "./macros.h"
9 
10 #include <stdlib.h>
11 #include <stdint.h>
12 
13 uintmax_t factorial(unsigned char n);
14 inline uintmax_t factorial(unsigned char n) {
15  // note that this function only works for numbers smaller than MAX_FACTORIAL_64
16  if ((sizeof(uintmax_t) == 8 && n > MAX_FACTORIAL_64) || (sizeof(uintmax_t) == 16 && n > MAX_FACTORIAL_128))
17  return -1;
18  uintmax_t ret = 1;
19  for (unsigned char i = 2; i <= n; ++i) {
20  ret *= i;
21  }
22  return ret;
23 }
24 
25 uintmax_t n_choose_r(const unsigned int n, const unsigned int r) {
26  // function returns -1 if it overflows, -2 if it can't allocate memory, or 0 if n < r
27  if (n < r)
28  return 0;
29  if ((sizeof(uintmax_t) == 8 && n <= MAX_FACTORIAL_64) || (sizeof(uintmax_t) == 16 && n <= MAX_FACTORIAL_128)) {
30  // fast path if small enough
31  return factorial(n) / factorial(r) / factorial(n-r);
32  }
33  // slow path for larger numbers
34  #if CL_COMPILER
35  signed char *factors = (signed char *) malloc(sizeof(signed char) * (n + 1));
36  if (factors == NULL)
37  return -2;
38  #else
39  signed char factors[n+1];
40  #endif
41  // collect factors of final number
42  // negative factor values indicate need to divide
43  // note that the highest absolute value in factors would be 127 if sizeof(uintmax_t) == 16
44  for (unsigned int i = r + 1; i <= n; i++)
45  factors[i] = 1;
46  for (unsigned int i = 2; i <= r; i++)
47  factors[i] = 0; // we can set directly to 0 because r <= n, so they would cancel completely
48  for (unsigned int i = 2; i <= n - r; i++)
49  factors[i]--; // we have to decrement because we don't know if (n - r) <= r
50  // this loop reduces to prime factors only, which helps cancel some
51  for (unsigned int i = n; i > 1; i--) {
52  for (unsigned int j = 2; j < i; j++) {
53  if (i % j == 0) {
54  factors[j] += factors[i];
55  factors[i / j] += factors[i];
56  factors[i] = 0;
57  break;
58  }
59  }
60  }
61  uintmax_t answer = 1;
62  unsigned int div_idx = 2;
63  for (unsigned int mul_idx = 2; mul_idx <= n; mul_idx++) {
64  while (factors[mul_idx] > 0) {
65  uintmax_t prev = answer; // store the previous value to check for overflow
66  answer *= mul_idx;
67  for (; answer < prev && div_idx <= n; div_idx++) {
68  // if there was an overflow, and there are still divisor candidates, attempt to fix it
69  while (factors[div_idx] < 0) {
70  prev /= div_idx;
71  factors[div_idx]++;
72  }
73  answer = prev * mul_idx;
74  }
75  if (answer < prev) {
76  // if you couldn't fix it, return the overflow error code
77  #if CL_COMPILER
78  free(factors);
79  #endif
80  return -1;
81  }
82  factors[mul_idx]--;
83  }
84  }
85  for (; div_idx <= n; div_idx++) {
86  while (factors[div_idx] < 0) {
87  answer /= div_idx;
88  factors[div_idx]++;
89  }
90  }
91  #if CL_COMPILER
92  free(factors);
93  #endif
94  return answer;
95 }
96 
97 #ifdef DOXYGEN
98 };
99 #endif
100 
101 #endif
#define MAX_FACTORIAL_128
Definition: math.h:5
uintmax_t n_choose_r(const unsigned int n, const unsigned int r)
Definition: math.h:25
uintmax_t factorial(unsigned char n)
Definition: math.h:14
#define MAX_FACTORIAL_64