From e52a1b356c26a75fc2833279eab7836061f31997 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Wed, 28 Feb 2024 00:12:34 +0530 Subject: [PATCH 1/3] Implement `math.factorial(n)` to calculate very large factorials with speed and accuracy --- src/runtime/math.py | 190 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 29 deletions(-) diff --git a/src/runtime/math.py b/src/runtime/math.py index 8655201892..7f383ecfa2 100644 --- a/src/runtime/math.py +++ b/src/runtime/math.py @@ -15,36 +15,169 @@ def modf(x: f64) -> tuple[f64, f64]: """ return (x - f64(int(x)), float(int(x))) -@overload -def factorial(x: i32) -> i32: - """ - Computes the factorial of `x`. - """ - result: i32 - result = 0 - if x < 0: - return result - result = 1 - i: i32 - for i in range(1, x+1): - result *= i - return result +def factorial(n: i32) -> i64: + """Computes the factorial of `n`.""" + MAX_LOOKUP_VALUE: i32 = 20 + FACTORIAL_LOOKUP_TABLE: list[i64] = [ + i64(1), + i64(1), + i64(2), + i64(6), + i64(24), + i64(120), + i64(720), + i64(5040), + i64(40320), + i64(362880), + i64(3628800), + i64(39916800), + i64(479001600), + i64(6227020800), + i64(87178291200), + i64(1307674368000), + i64(20922789888000), + i64(355687428096000), + i64(6402373705728000), + i64(121645100408832000), + i64(2432902008176640000), + ] + if n < 0: + # Exceptions are not implemented currently + # raise ValueError("factorial() not defined for negative values") + assert 1 == 0, "factorial() not defined for negative values." + elif n < MAX_LOOKUP_VALUE: + return FACTORIAL_LOOKUP_TABLE[n] + else: + f: list[i32] = [0] * 4300 + f[0] = 0 + f[1] = 0 + f[2] = 0 + f[3] = 0 + f[4] = 4 + f[5] = 6 + f[6] = 6 + f[7] = 7 + f[8] = 1 + f[9] = 8 + f[10] = 0 + f[11] = 0 + f[12] = 2 + f[13] = 0 + f[14] = 9 + f[15] = 2 + f[16] = 3 + f[17] = 4 + f[18] = 2 + + f_size: i32 = 19 + + i: i32 = 21 + while i <= n: + index: i32 = 0 + carry: i32 = 0 + while index < f_size: + product: i32 = f[index] * i + carry + f[index] = product % 10 + + carry = product // 10 + index += 1 + + while carry > 0: + f[f_size] = carry % 10 + carry = carry // 10 + f_size += 1 + i += 1 + + result: str = "" + idx: i32 + for idx in range(f_size - 1, -1, -1): + result += str(f[idx]) + print(result) + return i64(0) + + +@overload +def factorial(n: i64) -> i64: + """Computes the factorial of `n`.""" + MAX_LOOKUP_VALUE: i64 = i64(20) + FACTORIAL_LOOKUP_TABLE: list[i64] = [ + i64(1), + i64(1), + i64(2), + i64(6), + i64(24), + i64(120), + i64(720), + i64(5040), + i64(40320), + i64(362880), + i64(3628800), + i64(39916800), + i64(479001600), + i64(6227020800), + i64(87178291200), + i64(1307674368000), + i64(20922789888000), + i64(355687428096000), + i64(6402373705728000), + i64(121645100408832000), + i64(2432902008176640000), + ] + if n < i64(0): + # Exceptions are not implemented currently + # raise ValueError("factorial() not defined for negative values") + assert 1 == 0, "factorial() not defined for negative values." + elif n < MAX_LOOKUP_VALUE: + return FACTORIAL_LOOKUP_TABLE[n] + else: + f: list[i32] = [0] * 4300 + f[0] = 0 + f[1] = 0 + f[2] = 0 + f[3] = 0 + f[4] = 4 + f[5] = 6 + f[6] = 6 + f[7] = 7 + f[8] = 1 + f[9] = 8 + f[10] = 0 + f[11] = 0 + f[12] = 2 + f[13] = 0 + f[14] = 9 + f[15] = 2 + f[16] = 3 + f[17] = 4 + f[18] = 2 + + f_size: i32 = 19 + + i: i32 = 21 + while i64(i) <= n: + index: i32 = 0 + carry: i32 = 0 + while index < f_size: + product: i32 = f[index] * i + carry + f[index] = product % 10 + + carry = product // 10 + index += 1 + + while carry > 0: + f[f_size] = carry % 10 + carry = carry // 10 + f_size += 1 + i += 1 + + result: str = "" + idx: i32 + for idx in range(f_size - 1, -1, -1): + result += str(f[idx]) + print(result) + return i64(0) -@overload -def factorial(x: i64) -> i64: - """ - Computes the factorial of `x`. - """ - result: i64 - result = i64(0) - if x < i64(0): - return result - result = i64(1) - i: i64 - for i in range(i64(1), x + i64(1)): - result *= i64(i) - return result @overload def floor(x: i32) -> i32: @@ -457,7 +590,6 @@ def ldexp(x: f64, i: i32) -> f64: return result - def mod(a: i32, b: i32) -> i32: """ Returns a%b From e7857d0a33be4e009562ed9f0bf4cd81bcd44fee Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Sat, 9 Mar 2024 20:55:32 +0530 Subject: [PATCH 2/3] Use `int` as return type --- src/runtime/math.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/runtime/math.py b/src/runtime/math.py index 7f383ecfa2..a630c72a1b 100644 --- a/src/runtime/math.py +++ b/src/runtime/math.py @@ -16,7 +16,7 @@ def modf(x: f64) -> tuple[f64, f64]: return (x - f64(int(x)), float(int(x))) -def factorial(n: i32) -> i64: +def factorial(n: i32) -> int: """Computes the factorial of `n`.""" MAX_LOOKUP_VALUE: i32 = 20 FACTORIAL_LOOKUP_TABLE: list[i64] = [ @@ -93,12 +93,11 @@ def factorial(n: i32) -> i64: idx: i32 for idx in range(f_size - 1, -1, -1): result += str(f[idx]) - print(result) - return i64(0) + return int(result) @overload -def factorial(n: i64) -> i64: +def factorial(n: i64) -> int: """Computes the factorial of `n`.""" MAX_LOOKUP_VALUE: i64 = i64(20) FACTORIAL_LOOKUP_TABLE: list[i64] = [ @@ -175,8 +174,7 @@ def factorial(n: i64) -> i64: idx: i32 for idx in range(f_size - 1, -1, -1): result += str(f[idx]) - print(result) - return i64(0) + return int(result) @overload From 14783559b1995735bbc1becc5f66c86f7f9a77d6 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar <151380951+kmr-srbh@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:56:45 +0530 Subject: [PATCH 3/3] Revert setting return type to `int` --- src/runtime/math.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/math.py b/src/runtime/math.py index a630c72a1b..4a3c457392 100644 --- a/src/runtime/math.py +++ b/src/runtime/math.py @@ -16,7 +16,7 @@ def modf(x: f64) -> tuple[f64, f64]: return (x - f64(int(x)), float(int(x))) -def factorial(n: i32) -> int: +def factorial(n: i32) -> i64: """Computes the factorial of `n`.""" MAX_LOOKUP_VALUE: i32 = 20 FACTORIAL_LOOKUP_TABLE: list[i64] = [ @@ -93,11 +93,11 @@ def factorial(n: i32) -> int: idx: i32 for idx in range(f_size - 1, -1, -1): result += str(f[idx]) - return int(result) + return i64(0) @overload -def factorial(n: i64) -> int: +def factorial(n: i64) -> i64: """Computes the factorial of `n`.""" MAX_LOOKUP_VALUE: i64 = i64(20) FACTORIAL_LOOKUP_TABLE: list[i64] = [ @@ -174,7 +174,7 @@ def factorial(n: i64) -> int: idx: i32 for idx in range(f_size - 1, -1, -1): result += str(f[idx]) - return int(result) + return i64(0) @overload