Skip to content

Commit de57a10

Browse files
committed
Improved math.factorial() function to work with very large values, with speed
1 parent 0def810 commit de57a10

File tree

1 file changed

+162
-29
lines changed

1 file changed

+162
-29
lines changed

src/runtime/math.py

+162-29
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,170 @@ def modf(x: f64) -> tuple[f64, f64]:
1515
"""
1616
return (x - f64(int(x)), float(int(x)))
1717

18-
@overload
19-
def factorial(x: i32) -> i32:
20-
"""
21-
Computes the factorial of `x`.
22-
"""
23-
24-
result: i32
25-
result = 0
26-
if x < 0:
27-
return result
28-
result = 1
29-
i: i32
30-
for i in range(1, x+1):
31-
result *= i
32-
return result
3318

3419
@overload
35-
def factorial(x: i64) -> i64:
36-
"""
37-
Computes the factorial of `x`.
38-
"""
39-
result: i64
40-
result = i64(0)
41-
if x < i64(0):
42-
return result
43-
result = i64(1)
44-
i: i64
45-
for i in range(i64(1), x + i64(1)):
46-
result *= i64(i)
47-
return result
20+
def factorial(n: i32) -> i64:
21+
"""Computes the factorial of `n`."""
22+
MAX_LOOKUP_VALUE: i32 = 20
23+
FACTORIAL_LOOKUP_TABLE: list[i64] = [
24+
i64(1),
25+
i64(1),
26+
i64(2),
27+
i64(6),
28+
i64(24),
29+
i64(120),
30+
i64(720),
31+
i64(5040),
32+
i64(40320),
33+
i64(362880),
34+
i64(3628800),
35+
i64(39916800),
36+
i64(479001600),
37+
i64(6227020800),
38+
i64(87178291200),
39+
i64(1307674368000),
40+
i64(20922789888000),
41+
i64(355687428096000),
42+
i64(6402373705728000),
43+
i64(121645100408832000),
44+
i64(2432902008176640000),
45+
]
46+
if n < 0:
47+
# Exceptions are not implemented currently
48+
# raise ValueError("factorial() not defined for negative values")
49+
assert 1 == 0, "factorial() not defined for negative values."
50+
elif n < MAX_LOOKUP_VALUE:
51+
return FACTORIAL_LOOKUP_TABLE[n]
52+
else:
53+
f: list[i32] = [0] * 4300
54+
f[0] = 0
55+
f[1] = 0
56+
f[2] = 0
57+
f[3] = 0
58+
f[4] = 4
59+
f[5] = 6
60+
f[6] = 6
61+
f[7] = 7
62+
f[8] = 1
63+
f[9] = 8
64+
f[10] = 0
65+
f[11] = 0
66+
f[12] = 2
67+
f[13] = 0
68+
f[14] = 9
69+
f[15] = 2
70+
f[16] = 3
71+
f[17] = 4
72+
f[18] = 2
73+
74+
f_size: i32 = 19
75+
76+
i: i32 = 21
77+
while i <= n:
78+
index: i32 = 0
79+
carry: i32 = 0
80+
while index < f_size:
81+
product: i32 = f[index] * i + carry
82+
f[index] = product % 10
83+
84+
carry = product // 10
85+
index += 1
86+
87+
while carry > 0:
88+
f[f_size] = carry % 10
89+
carry = carry // 10
90+
f_size += 1
91+
i += 1
92+
93+
result: str = ""
94+
idx: i32
95+
for idx in range(f_size - 1, -1, -1):
96+
result += str(f[idx])
97+
print(result)
98+
return i64(0)
99+
100+
101+
@overload
102+
def factorial(n: i64) -> i64:
103+
"""Computes the factorial of `n`."""
104+
MAX_LOOKUP_VALUE: i64 = i64(20)
105+
FACTORIAL_LOOKUP_TABLE: list[i64] = [
106+
i64(1),
107+
i64(1),
108+
i64(2),
109+
i64(6),
110+
i64(24),
111+
i64(120),
112+
i64(720),
113+
i64(5040),
114+
i64(40320),
115+
i64(362880),
116+
i64(3628800),
117+
i64(39916800),
118+
i64(479001600),
119+
i64(6227020800),
120+
i64(87178291200),
121+
i64(1307674368000),
122+
i64(20922789888000),
123+
i64(355687428096000),
124+
i64(6402373705728000),
125+
i64(121645100408832000),
126+
i64(2432902008176640000),
127+
]
128+
if n < i64(0):
129+
# Exceptions are not implemented currently
130+
# raise ValueError("factorial() not defined for negative values")
131+
assert 1 == 0, "factorial() not defined for negative values."
132+
elif n < MAX_LOOKUP_VALUE:
133+
return FACTORIAL_LOOKUP_TABLE[n]
134+
else:
135+
f: list[i32] = [0] * 4300
136+
f[0] = 0
137+
f[1] = 0
138+
f[2] = 0
139+
f[3] = 0
140+
f[4] = 4
141+
f[5] = 6
142+
f[6] = 6
143+
f[7] = 7
144+
f[8] = 1
145+
f[9] = 8
146+
f[10] = 0
147+
f[11] = 0
148+
f[12] = 2
149+
f[13] = 0
150+
f[14] = 9
151+
f[15] = 2
152+
f[16] = 3
153+
f[17] = 4
154+
f[18] = 2
155+
156+
f_size: i32 = 19
157+
158+
i: i32 = 21
159+
while i64(i) <= n:
160+
index: i32 = 0
161+
carry: i32 = 0
162+
while index < f_size:
163+
product: i32 = f[index] * i + carry
164+
f[index] = product % 10
165+
166+
carry = product // 10
167+
index += 1
168+
169+
while carry > 0:
170+
f[f_size] = carry % 10
171+
carry = carry // 10
172+
f_size += 1
173+
i += 1
174+
175+
result: str = ""
176+
idx: i32
177+
for idx in range(f_size - 1, -1, -1):
178+
result += str(f[idx])
179+
print(result)
180+
return i64(0)
181+
48182

49183
@overload
50184
def floor(x: i32) -> i32:
@@ -457,7 +591,6 @@ def ldexp(x: f64, i: i32) -> f64:
457591
return result
458592

459593

460-
461594
def mod(a: i32, b: i32) -> i32:
462595
"""
463596
Returns a%b

0 commit comments

Comments
 (0)