datafusion_functions/math/
factorial.rs1use arrow::array::{ArrayRef, AsArray, Int64Array};
19use std::any::Any;
20use std::sync::Arc;
21
22use arrow::datatypes::DataType::Int64;
23use arrow::datatypes::{DataType, Int64Type};
24
25use crate::utils::make_scalar_function;
26use datafusion_common::{Result, exec_err};
27use datafusion_expr::{
28 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
29 Volatility,
30};
31use datafusion_macros::user_doc;
32
33#[user_doc(
34 doc_section(label = "Math Functions"),
35 description = "Factorial. Returns 1 if value is less than 2.",
36 syntax_example = "factorial(numeric_expression)",
37 sql_example = r#"```sql
38> SELECT factorial(5);
39+---------------+
40| factorial(5) |
41+---------------+
42| 120 |
43+---------------+
44```"#,
45 standard_argument(name = "numeric_expression", prefix = "Numeric")
46)]
47#[derive(Debug, PartialEq, Eq, Hash)]
48pub struct FactorialFunc {
49 signature: Signature,
50}
51
52impl Default for FactorialFunc {
53 fn default() -> Self {
54 FactorialFunc::new()
55 }
56}
57
58impl FactorialFunc {
59 pub fn new() -> Self {
60 Self {
61 signature: Signature::uniform(1, vec![Int64], Volatility::Immutable),
62 }
63 }
64}
65
66impl ScalarUDFImpl for FactorialFunc {
67 fn as_any(&self) -> &dyn Any {
68 self
69 }
70
71 fn name(&self) -> &str {
72 "factorial"
73 }
74
75 fn signature(&self) -> &Signature {
76 &self.signature
77 }
78
79 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
80 Ok(Int64)
81 }
82
83 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
84 make_scalar_function(factorial, vec![])(&args.args)
85 }
86
87 fn documentation(&self) -> Option<&Documentation> {
88 self.doc()
89 }
90}
91
92const FACTORIALS: [i64; 21] = [
93 1,
94 1,
95 2,
96 6,
97 24,
98 120,
99 720,
100 5040,
101 40320,
102 362880,
103 3628800,
104 39916800,
105 479001600,
106 6227020800,
107 87178291200,
108 1307674368000,
109 20922789888000,
110 355687428096000,
111 6402373705728000,
112 121645100408832000,
113 2432902008176640000,
114]; fn factorial(args: &[ArrayRef]) -> Result<ArrayRef> {
118 match args[0].data_type() {
119 Int64 => {
120 let result: Int64Array =
121 args[0].as_primitive::<Int64Type>().try_unary(|a| {
122 if a < 0 {
123 Ok(1)
124 } else if a < FACTORIALS.len() as i64 {
125 Ok(FACTORIALS[a as usize])
126 } else {
127 exec_err!("Overflow happened on FACTORIAL({a})")
128 }
129 })?;
130 Ok(Arc::new(result) as ArrayRef)
131 }
132 other => exec_err!("Unsupported data type {other:?} for function factorial."),
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139 use datafusion_common::cast::as_int64_array;
140
141 #[test]
142 fn test_factorial_i64() {
143 let args: Vec<ArrayRef> = vec![
144 Arc::new(Int64Array::from(vec![0, 1, 2, 4, 20, -1])), ];
146
147 let result = factorial(&args).expect("failed to initialize function factorial");
148 let ints =
149 as_int64_array(&result).expect("failed to initialize function factorial");
150
151 let expected = Int64Array::from(vec![1, 1, 2, 24, 2432902008176640000, 1]);
152
153 assert_eq!(ints, &expected);
154 }
155
156 #[test]
157 fn test_overflow() {
158 let args: Vec<ArrayRef> = vec![
159 Arc::new(Int64Array::from(vec![21])), ];
161
162 let result = factorial(&args);
163 assert!(result.is_err());
164 }
165}