math.f90
View source code here on GitHub!
- function math/factorial(n)
- Parameters:
n [integer]
- Return:
answer [integer(i18t)] :: n!
- function math/n_choose_r(n, r)
- Parameters:
n [integer]
r [integer]
- Return:
answer [integer(i18t)] :: nCr
- function math/n_choose_r_slow(n, r)
This is a helper function for numbers that would otherwise overflow using the naive calculation of
factorial(n) / factorial(r) / factorial(n - r)
. It's a slower path, but a much more reliable one because it actually factorizes the numbers and cancels out as many as possible before multiplying.- Parameters:
n [integer]
r [integer]
- Return:
answer [integer(i18t)] :: nCr
1module math
2 use constants
3 implicit none
4contains
5 pure integer(i18t) function factorial(n) result(answer)
6 integer, intent(in) :: n
7 integer :: i
8 answer = 1
9 do i = 2, n
10 answer = answer * i
11 end do
12 end function
13
14 integer(i18t) function n_choose_r(n, r) result(answer)
15 integer, intent(in) :: n, r
16 if (n <= MAX_FACTORIAL_64) then
17 answer = factorial(n) / factorial(r) / factorial(n-r) ! fast path if small enough
18 else
19 answer = n_choose_r_slow(n, r) ! slow path for larger numbers
20 end if
21 end function
22
23 integer(i18t) function n_choose_r_slow(n, r) result(answer)
24 integer, intent(in) :: n, r
25 integer(i2t), allocatable :: factors(:)
26 integer(i18t) :: tmp
27 integer :: i, j
28 allocate(factors(n))
29 if (.not. allocated(factors)) then
30 print *, "n_choose_r allocation failed. Exiting."
31 stop ERROR_ALLOCATE_FAILED
32 end if
33 factors = 0
34 ! collect factors of final number
35 do i = 2, n
36 factors(i) = 1
37 end do
38 ! negative factor values indicate need to divide
39 do i = 2, r
40 factors(i) = factors(i) - 1_i2t
41 end do
42 do i = 2, n - r
43 factors(i) = factors(i) - 1_i2t
44 end do
45 do i = n, 2, -1 ! this loop reduces to prime factors only
46 do j = 2, i - 1
47 if (mod(i, j) == 0) then
48 factors(j) = factors(j) + factors(i)
49 factors(i / j) = factors(i / j) + factors(i)
50 factors(i) = 0
51 exit
52 end if
53 end do
54 end do
55 i = 2
56 j = 2
57 answer = 1
58 do while (i <= n)
59 do while (factors(i) > 0)
60 tmp = answer
61 answer = answer * i
62 do while (answer < tmp .and. j <= n)
63 do while (factors(j) < 0)
64 tmp = tmp / j
65 factors(j) = factors(j) + 1_i2t
66 end do
67 j = j + 1_i2t
68 answer = tmp * i
69 end do
70 if (answer < tmp) then
71 answer = -1 ! this indicates an overflow
72 return
73 end if
74 factors(i) = factors(i) - 1_i2t
75 end do
76 i = i + 1_i2t
77 end do
78 do while (j <= n)
79 do while (factors(j) < 0)
80 answer = answer / j
81 factors(j) = factors(j) + 1_i2t
82 end do
83 j = j + 1_i2t
84 end do
85 deallocate(factors)
86 end function
87end module math