datafusion_functions/math/
log.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Math function: `log()`.
19
20use std::any::Any;
21use std::sync::Arc;
22
23use super::power::PowerFunc;
24
25use crate::utils::{calculate_binary_math, decimal128_to_i128};
26use arrow::array::{Array, ArrayRef};
27use arrow::datatypes::{
28    DataType, Decimal128Type, Decimal256Type, Float32Type, Float64Type, Int32Type,
29    Int64Type, DECIMAL128_MAX_PRECISION, DECIMAL256_MAX_PRECISION,
30};
31use arrow::error::ArrowError;
32use arrow_buffer::i256;
33use datafusion_common::{
34    exec_err, internal_err, plan_datafusion_err, plan_err, Result, ScalarValue,
35};
36use datafusion_expr::expr::ScalarFunction;
37use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
38use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
39use datafusion_expr::{
40    lit, ColumnarValue, Documentation, Expr, ScalarFunctionArgs, ScalarUDF,
41    TypeSignature::*,
42};
43use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
44use datafusion_macros::user_doc;
45
46#[user_doc(
47    doc_section(label = "Math Functions"),
48    description = "Returns the base-x logarithm of a number. Can either provide a specified base, or if omitted then takes the base-10 of a number.",
49    syntax_example = r#"log(base, numeric_expression)
50log(numeric_expression)"#,
51    sql_example = r#"```sql
52> SELECT log(10);
53+---------+
54| log(10) |
55+---------+
56| 1.0     |
57+---------+
58```"#,
59    standard_argument(name = "base", prefix = "Base numeric"),
60    standard_argument(name = "numeric_expression", prefix = "Numeric")
61)]
62#[derive(Debug, PartialEq, Eq, Hash)]
63pub struct LogFunc {
64    signature: Signature,
65}
66
67impl Default for LogFunc {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73impl LogFunc {
74    pub fn new() -> Self {
75        Self {
76            signature: Signature::one_of(
77                vec![
78                    Numeric(1),
79                    Numeric(2),
80                    Exact(vec![DataType::Float32, DataType::Float32]),
81                    Exact(vec![DataType::Float64, DataType::Float64]),
82                    Exact(vec![
83                        DataType::Int64,
84                        DataType::Decimal128(DECIMAL128_MAX_PRECISION, 0),
85                    ]),
86                    Exact(vec![
87                        DataType::Float32,
88                        DataType::Decimal128(DECIMAL128_MAX_PRECISION, 0),
89                    ]),
90                    Exact(vec![
91                        DataType::Float64,
92                        DataType::Decimal128(DECIMAL128_MAX_PRECISION, 0),
93                    ]),
94                    Exact(vec![
95                        DataType::Int64,
96                        DataType::Decimal256(DECIMAL256_MAX_PRECISION, 0),
97                    ]),
98                    Exact(vec![
99                        DataType::Float32,
100                        DataType::Decimal256(DECIMAL256_MAX_PRECISION, 0),
101                    ]),
102                    Exact(vec![
103                        DataType::Float64,
104                        DataType::Decimal256(DECIMAL256_MAX_PRECISION, 0),
105                    ]),
106                ],
107                Volatility::Immutable,
108            ),
109        }
110    }
111}
112
113/// Binary function to calculate an integer logarithm of Decimal128 `value` using `base` base
114/// Returns error if base is invalid
115fn log_decimal128(value: i128, scale: i8, base: f64) -> Result<f64, ArrowError> {
116    if !base.is_finite() || base.trunc() != base {
117        return Err(ArrowError::ComputeError(format!(
118            "Log cannot use non-integer base: {base}"
119        )));
120    }
121    if (base as u32) < 2 {
122        return Err(ArrowError::ComputeError(format!(
123            "Log base must be greater than 1: {base}"
124        )));
125    }
126
127    let unscaled_value = decimal128_to_i128(value, scale)?;
128    if unscaled_value > 0 {
129        let log_value: u32 = unscaled_value.ilog(base as i128);
130        Ok(log_value as f64)
131    } else {
132        // Reflect f64::log behaviour
133        Ok(f64::NAN)
134    }
135}
136
137/// Binary function to calculate an integer logarithm of Decimal128 `value` using `base` base
138/// Returns error if base is invalid or if value is out of bounds of Decimal128
139fn log_decimal256(value: i256, scale: i8, base: f64) -> Result<f64, ArrowError> {
140    match value.to_i128() {
141        Some(value) => log_decimal128(value, scale, base),
142        None => Err(ArrowError::NotYetImplemented(format!(
143            "Log of Decimal256 larger than Decimal128 is not yet supported: {value}"
144        ))),
145    }
146}
147
148impl ScalarUDFImpl for LogFunc {
149    fn as_any(&self) -> &dyn Any {
150        self
151    }
152    fn name(&self) -> &str {
153        "log"
154    }
155
156    fn signature(&self) -> &Signature {
157        &self.signature
158    }
159
160    fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
161        // Check last argument (value)
162        match &arg_types.last().ok_or(plan_datafusion_err!("No args"))? {
163            DataType::Float32 => Ok(DataType::Float32),
164            _ => Ok(DataType::Float64),
165        }
166    }
167
168    fn output_ordering(&self, input: &[ExprProperties]) -> Result<SortProperties> {
169        let (base_sort_properties, num_sort_properties) = if input.len() == 1 {
170            // log(x) defaults to log(10, x)
171            (SortProperties::Singleton, input[0].sort_properties)
172        } else {
173            (input[0].sort_properties, input[1].sort_properties)
174        };
175        match (num_sort_properties, base_sort_properties) {
176            (first @ SortProperties::Ordered(num), SortProperties::Ordered(base))
177                if num.descending != base.descending
178                    && num.nulls_first == base.nulls_first =>
179            {
180                Ok(first)
181            }
182            (
183                first @ (SortProperties::Ordered(_) | SortProperties::Singleton),
184                SortProperties::Singleton,
185            ) => Ok(first),
186            (SortProperties::Singleton, second @ SortProperties::Ordered(_)) => {
187                Ok(-second)
188            }
189            _ => Ok(SortProperties::Unordered),
190        }
191    }
192
193    // Support overloaded log(base, x) and log(x) which defaults to log(10, x)
194    fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
195        let args = ColumnarValue::values_to_arrays(&args.args)?;
196
197        let (base, value) = if args.len() == 2 {
198            // note in f64::log params order is different than in sql. e.g in sql log(base, x) == f64::log(x, base)
199            (ColumnarValue::Array(Arc::clone(&args[0])), &args[1])
200        } else {
201            // log(num) - assume base is 10
202            let ret_type = if args[0].data_type().is_null() {
203                &DataType::Float64
204            } else {
205                args[0].data_type()
206            };
207            (
208                ColumnarValue::Array(
209                    ScalarValue::new_ten(ret_type)?.to_array_of_size(args[0].len())?,
210                ),
211                &args[0],
212            )
213        };
214
215        // All log functors have format 'log(value, base)'
216        // Therefore, for `calculate_binary_math` the first type means a type of main array
217        // The second type is the type of the base array (even if derived from main)
218        let arr: ArrayRef = match value.data_type() {
219            DataType::Float32 => calculate_binary_math::<
220                Float32Type,
221                Float32Type,
222                Float32Type,
223                _,
224            >(value, &base, |x, b| Ok(f32::log(x, b)))?,
225            DataType::Float64 => calculate_binary_math::<
226                Float64Type,
227                Float64Type,
228                Float64Type,
229                _,
230            >(value, &base, |x, b| Ok(f64::log(x, b)))?,
231            DataType::Int32 => {
232                calculate_binary_math::<Int32Type, Float64Type, Float64Type, _>(
233                    value,
234                    &base,
235                    |x, b| Ok(f64::log(x as f64, b)),
236                )?
237            }
238            DataType::Int64 => {
239                calculate_binary_math::<Int64Type, Float64Type, Float64Type, _>(
240                    value,
241                    &base,
242                    |x, b| Ok(f64::log(x as f64, b)),
243                )?
244            }
245            DataType::Decimal128(_precision, scale) => {
246                calculate_binary_math::<Decimal128Type, Float64Type, Float64Type, _>(
247                    value,
248                    &base,
249                    |x, b| log_decimal128(x, *scale, b),
250                )?
251            }
252            DataType::Decimal256(_precision, scale) => {
253                calculate_binary_math::<Decimal256Type, Float64Type, Float64Type, _>(
254                    value,
255                    &base,
256                    |x, b| log_decimal256(x, *scale, b),
257                )?
258            }
259            other => {
260                return exec_err!("Unsupported data type {other:?} for function log")
261            }
262        };
263
264        Ok(ColumnarValue::Array(arr))
265    }
266
267    fn documentation(&self) -> Option<&Documentation> {
268        self.doc()
269    }
270
271    /// Simplify the `log` function by the relevant rules:
272    /// 1. Log(a, 1) ===> 0
273    /// 2. Log(a, Power(a, b)) ===> b
274    /// 3. Log(a, a) ===> 1
275    fn simplify(
276        &self,
277        mut args: Vec<Expr>,
278        info: &dyn SimplifyInfo,
279    ) -> Result<ExprSimplifyResult> {
280        // Args are either
281        // log(number)
282        // log(base, number)
283        let num_args = args.len();
284        if num_args > 2 {
285            return plan_err!("Expected log to have 1 or 2 arguments, got {num_args}");
286        }
287        let number = args.pop().ok_or_else(|| {
288            plan_datafusion_err!("Expected log to have 1 or 2 arguments, got 0")
289        })?;
290        let number_datatype = info.get_data_type(&number)?;
291        // default to base 10
292        let base = if let Some(base) = args.pop() {
293            base
294        } else {
295            lit(ScalarValue::new_ten(&number_datatype)?)
296        };
297
298        match number {
299            Expr::Literal(value, _)
300                if value == ScalarValue::new_one(&number_datatype)? =>
301            {
302                Ok(ExprSimplifyResult::Simplified(lit(ScalarValue::new_zero(
303                    &info.get_data_type(&base)?,
304                )?)))
305            }
306            Expr::ScalarFunction(ScalarFunction { func, mut args })
307                if is_pow(&func) && args.len() == 2 && base == args[0] =>
308            {
309                let b = args.pop().unwrap(); // length checked above
310                Ok(ExprSimplifyResult::Simplified(b))
311            }
312            number => {
313                if number == base {
314                    Ok(ExprSimplifyResult::Simplified(lit(ScalarValue::new_one(
315                        &number_datatype,
316                    )?)))
317                } else {
318                    let args = match num_args {
319                        1 => vec![number],
320                        2 => vec![base, number],
321                        _ => {
322                            return internal_err!(
323                                "Unexpected number of arguments in log::simplify"
324                            )
325                        }
326                    };
327                    Ok(ExprSimplifyResult::Original(args))
328                }
329            }
330        }
331    }
332}
333
334/// Returns true if the function is `PowerFunc`
335fn is_pow(func: &ScalarUDF) -> bool {
336    func.inner().as_any().downcast_ref::<PowerFunc>().is_some()
337}
338
339#[cfg(test)]
340mod tests {
341    use std::collections::HashMap;
342
343    use super::*;
344
345    use arrow::array::{
346        Date32Array, Decimal128Array, Decimal256Array, Float32Array, Float64Array,
347    };
348    use arrow::compute::SortOptions;
349    use arrow::datatypes::{Field, DECIMAL256_MAX_PRECISION};
350    use datafusion_common::cast::{as_float32_array, as_float64_array};
351    use datafusion_common::config::ConfigOptions;
352    use datafusion_common::DFSchema;
353    use datafusion_expr::execution_props::ExecutionProps;
354    use datafusion_expr::simplify::SimplifyContext;
355
356    #[test]
357    fn test_log_invalid_base_type() {
358        let arg_fields = vec![
359            Field::new("b", DataType::Date32, false).into(),
360            Field::new("n", DataType::Float64, false).into(),
361        ];
362        let args = ScalarFunctionArgs {
363            args: vec![
364                ColumnarValue::Array(Arc::new(Date32Array::from(vec![5, 10, 15, 20]))), // base
365                ColumnarValue::Array(Arc::new(Float64Array::from(vec![
366                    10.0, 100.0, 1000.0, 10000.0,
367                ]))), // num
368            ],
369            arg_fields,
370            number_rows: 4,
371            return_field: Field::new("f", DataType::Float64, true).into(),
372            config_options: Arc::new(ConfigOptions::default()),
373            lambdas: None,
374        };
375        let result = LogFunc::new().invoke_with_args(args);
376        assert!(result.is_err());
377        assert_eq!(
378            result.unwrap_err().to_string().lines().next().unwrap(),
379            "Arrow error: Cast error: Casting from Date32 to Float64 not supported"
380        );
381    }
382
383    #[test]
384    fn test_log_invalid_value() {
385        let arg_field = Field::new("a", DataType::Date32, false).into();
386        let args = ScalarFunctionArgs {
387            args: vec![
388                ColumnarValue::Array(Arc::new(Date32Array::from(vec![10]))), // num
389            ],
390            arg_fields: vec![arg_field],
391            number_rows: 1,
392            return_field: Field::new("f", DataType::Float64, true).into(),
393            config_options: Arc::new(ConfigOptions::default()),
394            lambdas: None,
395        };
396
397        let result = LogFunc::new().invoke_with_args(args);
398        result.expect_err("expected error");
399    }
400
401    #[test]
402    fn test_log_scalar_f32_unary() {
403        let arg_field = Field::new("a", DataType::Float32, false).into();
404        let args = ScalarFunctionArgs {
405            args: vec![
406                ColumnarValue::Scalar(ScalarValue::Float32(Some(10.0))), // num
407            ],
408            arg_fields: vec![arg_field],
409            number_rows: 1,
410            return_field: Field::new("f", DataType::Float32, true).into(),
411            config_options: Arc::new(ConfigOptions::default()),
412            lambdas: None,
413        };
414        let result = LogFunc::new()
415            .invoke_with_args(args)
416            .expect("failed to initialize function log");
417
418        match result {
419            ColumnarValue::Array(arr) => {
420                let floats = as_float32_array(&arr)
421                    .expect("failed to convert result to a Float32Array");
422
423                assert_eq!(floats.len(), 1);
424                assert!((floats.value(0) - 1.0).abs() < 1e-10);
425            }
426            ColumnarValue::Scalar(_) => {
427                panic!("Expected an array value")
428            }
429        }
430    }
431
432    #[test]
433    fn test_log_scalar_f64_unary() {
434        let arg_field = Field::new("a", DataType::Float64, false).into();
435        let args = ScalarFunctionArgs {
436            args: vec![
437                ColumnarValue::Scalar(ScalarValue::Float64(Some(10.0))), // num
438            ],
439            arg_fields: vec![arg_field],
440            number_rows: 1,
441            return_field: Field::new("f", DataType::Float64, true).into(),
442            config_options: Arc::new(ConfigOptions::default()),
443            lambdas: None,
444        };
445        let result = LogFunc::new()
446            .invoke_with_args(args)
447            .expect("failed to initialize function log");
448
449        match result {
450            ColumnarValue::Array(arr) => {
451                let floats = as_float64_array(&arr)
452                    .expect("failed to convert result to a Float64Array");
453
454                assert_eq!(floats.len(), 1);
455                assert!((floats.value(0) - 1.0).abs() < 1e-10);
456            }
457            ColumnarValue::Scalar(_) => {
458                panic!("Expected an array value")
459            }
460        }
461    }
462
463    #[test]
464    fn test_log_scalar_f32() {
465        let arg_fields = vec![
466            Field::new("a", DataType::Float32, false).into(),
467            Field::new("a", DataType::Float32, false).into(),
468        ];
469        let args = ScalarFunctionArgs {
470            args: vec![
471                ColumnarValue::Scalar(ScalarValue::Float32(Some(2.0))), // base
472                ColumnarValue::Scalar(ScalarValue::Float32(Some(32.0))), // num
473            ],
474            arg_fields,
475            number_rows: 1,
476            return_field: Field::new("f", DataType::Float32, true).into(),
477            config_options: Arc::new(ConfigOptions::default()),
478            lambdas: None,
479        };
480        let result = LogFunc::new()
481            .invoke_with_args(args)
482            .expect("failed to initialize function log");
483
484        match result {
485            ColumnarValue::Array(arr) => {
486                let floats = as_float32_array(&arr)
487                    .expect("failed to convert result to a Float32Array");
488
489                assert_eq!(floats.len(), 1);
490                assert!((floats.value(0) - 5.0).abs() < 1e-10);
491            }
492            ColumnarValue::Scalar(_) => {
493                panic!("Expected an array value")
494            }
495        }
496    }
497
498    #[test]
499    fn test_log_scalar_f64() {
500        let arg_fields = vec![
501            Field::new("a", DataType::Float64, false).into(),
502            Field::new("a", DataType::Float64, false).into(),
503        ];
504        let args = ScalarFunctionArgs {
505            args: vec![
506                ColumnarValue::Scalar(ScalarValue::Float64(Some(2.0))), // base
507                ColumnarValue::Scalar(ScalarValue::Float64(Some(64.0))), // num
508            ],
509            arg_fields,
510            number_rows: 1,
511            return_field: Field::new("f", DataType::Float64, true).into(),
512            config_options: Arc::new(ConfigOptions::default()),
513            lambdas: None,
514        };
515        let result = LogFunc::new()
516            .invoke_with_args(args)
517            .expect("failed to initialize function log");
518
519        match result {
520            ColumnarValue::Array(arr) => {
521                let floats = as_float64_array(&arr)
522                    .expect("failed to convert result to a Float64Array");
523
524                assert_eq!(floats.len(), 1);
525                assert!((floats.value(0) - 6.0).abs() < 1e-10);
526            }
527            ColumnarValue::Scalar(_) => {
528                panic!("Expected an array value")
529            }
530        }
531    }
532
533    #[test]
534    fn test_log_f64_unary() {
535        let arg_field = Field::new("a", DataType::Float64, false).into();
536        let args = ScalarFunctionArgs {
537            args: vec![
538                ColumnarValue::Array(Arc::new(Float64Array::from(vec![
539                    10.0, 100.0, 1000.0, 10000.0,
540                ]))), // num
541            ],
542            arg_fields: vec![arg_field],
543            number_rows: 4,
544            return_field: Field::new("f", DataType::Float64, true).into(),
545            config_options: Arc::new(ConfigOptions::default()),
546            lambdas: None,
547        };
548        let result = LogFunc::new()
549            .invoke_with_args(args)
550            .expect("failed to initialize function log");
551
552        match result {
553            ColumnarValue::Array(arr) => {
554                let floats = as_float64_array(&arr)
555                    .expect("failed to convert result to a Float64Array");
556
557                assert_eq!(floats.len(), 4);
558                assert!((floats.value(0) - 1.0).abs() < 1e-10);
559                assert!((floats.value(1) - 2.0).abs() < 1e-10);
560                assert!((floats.value(2) - 3.0).abs() < 1e-10);
561                assert!((floats.value(3) - 4.0).abs() < 1e-10);
562            }
563            ColumnarValue::Scalar(_) => {
564                panic!("Expected an array value")
565            }
566        }
567    }
568
569    #[test]
570    fn test_log_f32_unary() {
571        let arg_field = Field::new("a", DataType::Float32, false).into();
572        let args = ScalarFunctionArgs {
573            args: vec![
574                ColumnarValue::Array(Arc::new(Float32Array::from(vec![
575                    10.0, 100.0, 1000.0, 10000.0,
576                ]))), // num
577            ],
578            arg_fields: vec![arg_field],
579            number_rows: 4,
580            return_field: Field::new("f", DataType::Float32, true).into(),
581            config_options: Arc::new(ConfigOptions::default()),
582            lambdas: None,
583        };
584        let result = LogFunc::new()
585            .invoke_with_args(args)
586            .expect("failed to initialize function log");
587
588        match result {
589            ColumnarValue::Array(arr) => {
590                let floats = as_float32_array(&arr)
591                    .expect("failed to convert result to a Float64Array");
592
593                assert_eq!(floats.len(), 4);
594                assert!((floats.value(0) - 1.0).abs() < 1e-10);
595                assert!((floats.value(1) - 2.0).abs() < 1e-10);
596                assert!((floats.value(2) - 3.0).abs() < 1e-10);
597                assert!((floats.value(3) - 4.0).abs() < 1e-10);
598            }
599            ColumnarValue::Scalar(_) => {
600                panic!("Expected an array value")
601            }
602        }
603    }
604
605    #[test]
606    fn test_log_f64() {
607        let arg_fields = vec![
608            Field::new("a", DataType::Float64, false).into(),
609            Field::new("a", DataType::Float64, false).into(),
610        ];
611        let args = ScalarFunctionArgs {
612            args: vec![
613                ColumnarValue::Array(Arc::new(Float64Array::from(vec![
614                    2.0, 2.0, 3.0, 5.0, 5.0,
615                ]))), // base
616                ColumnarValue::Array(Arc::new(Float64Array::from(vec![
617                    8.0, 4.0, 81.0, 625.0, -123.0,
618                ]))), // num
619            ],
620            arg_fields,
621            number_rows: 5,
622            return_field: Field::new("f", DataType::Float64, true).into(),
623            config_options: Arc::new(ConfigOptions::default()),
624            lambdas: None,
625        };
626        let result = LogFunc::new()
627            .invoke_with_args(args)
628            .expect("failed to initialize function log");
629
630        match result {
631            ColumnarValue::Array(arr) => {
632                let floats = as_float64_array(&arr)
633                    .expect("failed to convert result to a Float64Array");
634
635                assert_eq!(floats.len(), 5);
636                assert!((floats.value(0) - 3.0).abs() < 1e-10);
637                assert!((floats.value(1) - 2.0).abs() < 1e-10);
638                assert!((floats.value(2) - 4.0).abs() < 1e-10);
639                assert!((floats.value(3) - 4.0).abs() < 1e-10);
640                assert!(floats.value(4).is_nan());
641            }
642            ColumnarValue::Scalar(_) => {
643                panic!("Expected an array value")
644            }
645        }
646    }
647
648    #[test]
649    fn test_log_f32() {
650        let arg_fields = vec![
651            Field::new("a", DataType::Float32, false).into(),
652            Field::new("a", DataType::Float32, false).into(),
653        ];
654        let args = ScalarFunctionArgs {
655            args: vec![
656                ColumnarValue::Array(Arc::new(Float32Array::from(vec![
657                    2.0, 2.0, 3.0, 5.0,
658                ]))), // base
659                ColumnarValue::Array(Arc::new(Float32Array::from(vec![
660                    8.0, 4.0, 81.0, 625.0,
661                ]))), // num
662            ],
663            arg_fields,
664            number_rows: 4,
665            return_field: Field::new("f", DataType::Float32, true).into(),
666            config_options: Arc::new(ConfigOptions::default()),
667            lambdas: None,
668        };
669        let result = LogFunc::new()
670            .invoke_with_args(args)
671            .expect("failed to initialize function log");
672
673        match result {
674            ColumnarValue::Array(arr) => {
675                let floats = as_float32_array(&arr)
676                    .expect("failed to convert result to a Float32Array");
677
678                assert_eq!(floats.len(), 4);
679                assert!((floats.value(0) - 3.0).abs() < f32::EPSILON);
680                assert!((floats.value(1) - 2.0).abs() < f32::EPSILON);
681                assert!((floats.value(2) - 4.0).abs() < f32::EPSILON);
682                assert!((floats.value(3) - 4.0).abs() < f32::EPSILON);
683            }
684            ColumnarValue::Scalar(_) => {
685                panic!("Expected an array value")
686            }
687        }
688    }
689    #[test]
690    // Test log() simplification errors
691    fn test_log_simplify_errors() {
692        let props = ExecutionProps::new();
693        let schema =
694            Arc::new(DFSchema::new_with_metadata(vec![], HashMap::new()).unwrap());
695        let context = SimplifyContext::new(&props).with_schema(schema);
696        // Expect 0 args to error
697        let _ = LogFunc::new().simplify(vec![], &context).unwrap_err();
698        // Expect 3 args to error
699        let _ = LogFunc::new()
700            .simplify(vec![lit(1), lit(2), lit(3)], &context)
701            .unwrap_err();
702    }
703
704    #[test]
705    // Test that non-simplifiable log() expressions are unchanged after simplification
706    fn test_log_simplify_original() {
707        let props = ExecutionProps::new();
708        let schema =
709            Arc::new(DFSchema::new_with_metadata(vec![], HashMap::new()).unwrap());
710        let context = SimplifyContext::new(&props).with_schema(schema);
711        // One argument with no simplifications
712        let result = LogFunc::new().simplify(vec![lit(2)], &context).unwrap();
713        let ExprSimplifyResult::Original(args) = result else {
714            panic!("Expected ExprSimplifyResult::Original")
715        };
716        assert_eq!(args.len(), 1);
717        assert_eq!(args[0], lit(2));
718        // Two arguments with no simplifications
719        let result = LogFunc::new()
720            .simplify(vec![lit(2), lit(3)], &context)
721            .unwrap();
722        let ExprSimplifyResult::Original(args) = result else {
723            panic!("Expected ExprSimplifyResult::Original")
724        };
725        assert_eq!(args.len(), 2);
726        assert_eq!(args[0], lit(2));
727        assert_eq!(args[1], lit(3));
728    }
729
730    #[test]
731    fn test_log_output_ordering() {
732        // [Unordered, Ascending, Descending, Literal]
733        let orders = [
734            ExprProperties::new_unknown(),
735            ExprProperties::new_unknown().with_order(SortProperties::Ordered(
736                SortOptions {
737                    descending: false,
738                    nulls_first: true,
739                },
740            )),
741            ExprProperties::new_unknown().with_order(SortProperties::Ordered(
742                SortOptions {
743                    descending: true,
744                    nulls_first: true,
745                },
746            )),
747            ExprProperties::new_unknown().with_order(SortProperties::Singleton),
748        ];
749
750        let log = LogFunc::new();
751
752        // Test log(num)
753        for order in orders.iter().cloned() {
754            let result = log.output_ordering(std::slice::from_ref(&order)).unwrap();
755            assert_eq!(result, order.sort_properties);
756        }
757
758        // Test log(base, num), where `nulls_first` is the same
759        let mut results = Vec::with_capacity(orders.len() * orders.len());
760        for base_order in orders.iter() {
761            for num_order in orders.iter().cloned() {
762                let result = log
763                    .output_ordering(&[base_order.clone(), num_order])
764                    .unwrap();
765                results.push(result);
766            }
767        }
768        let expected = [
769            // base: Unordered
770            SortProperties::Unordered,
771            SortProperties::Unordered,
772            SortProperties::Unordered,
773            SortProperties::Unordered,
774            // base: Ascending, num: Unordered
775            SortProperties::Unordered,
776            // base: Ascending, num: Ascending
777            SortProperties::Unordered,
778            // base: Ascending, num: Descending
779            SortProperties::Ordered(SortOptions {
780                descending: true,
781                nulls_first: true,
782            }),
783            // base: Ascending, num: Literal
784            SortProperties::Ordered(SortOptions {
785                descending: true,
786                nulls_first: true,
787            }),
788            // base: Descending, num: Unordered
789            SortProperties::Unordered,
790            // base: Descending, num: Ascending
791            SortProperties::Ordered(SortOptions {
792                descending: false,
793                nulls_first: true,
794            }),
795            // base: Descending, num: Descending
796            SortProperties::Unordered,
797            // base: Descending, num: Literal
798            SortProperties::Ordered(SortOptions {
799                descending: false,
800                nulls_first: true,
801            }),
802            // base: Literal, num: Unordered
803            SortProperties::Unordered,
804            // base: Literal, num: Ascending
805            SortProperties::Ordered(SortOptions {
806                descending: false,
807                nulls_first: true,
808            }),
809            // base: Literal, num: Descending
810            SortProperties::Ordered(SortOptions {
811                descending: true,
812                nulls_first: true,
813            }),
814            // base: Literal, num: Literal
815            SortProperties::Singleton,
816        ];
817        assert_eq!(results, expected);
818
819        // Test with different `nulls_first`
820        let base_order = ExprProperties::new_unknown().with_order(
821            SortProperties::Ordered(SortOptions {
822                descending: true,
823                nulls_first: true,
824            }),
825        );
826        let num_order = ExprProperties::new_unknown().with_order(
827            SortProperties::Ordered(SortOptions {
828                descending: false,
829                nulls_first: false,
830            }),
831        );
832        assert_eq!(
833            log.output_ordering(&[base_order, num_order]).unwrap(),
834            SortProperties::Unordered
835        );
836    }
837
838    #[test]
839    fn test_log_scalar_decimal128_unary() {
840        let arg_field = Field::new("a", DataType::Decimal128(38, 0), false).into();
841        let args = ScalarFunctionArgs {
842            args: vec![
843                ColumnarValue::Scalar(ScalarValue::Decimal128(Some(10), 38, 0)), // num
844            ],
845            arg_fields: vec![arg_field],
846            number_rows: 1,
847            return_field: Field::new("f", DataType::Decimal128(38, 0), true).into(),
848            config_options: Arc::new(ConfigOptions::default()),
849            lambdas: None,
850        };
851        let result = LogFunc::new()
852            .invoke_with_args(args)
853            .expect("failed to initialize function log");
854
855        match result {
856            ColumnarValue::Array(arr) => {
857                let floats = as_float64_array(&arr)
858                    .expect("failed to convert result to a Decimal128Array");
859                assert_eq!(floats.len(), 1);
860                assert!((floats.value(0) - 1.0).abs() < 1e-10);
861            }
862            ColumnarValue::Scalar(_) => {
863                panic!("Expected an array value")
864            }
865        }
866    }
867
868    #[test]
869    fn test_log_scalar_decimal128() {
870        let arg_fields = vec![
871            Field::new("b", DataType::Float64, false).into(),
872            Field::new("x", DataType::Decimal128(38, 0), false).into(),
873        ];
874        let args = ScalarFunctionArgs {
875            args: vec![
876                ColumnarValue::Scalar(ScalarValue::Float64(Some(2.0))), // base
877                ColumnarValue::Scalar(ScalarValue::Decimal128(Some(64), 38, 0)), // num
878            ],
879            arg_fields,
880            number_rows: 1,
881            return_field: Field::new("f", DataType::Float64, true).into(),
882            config_options: Arc::new(ConfigOptions::default()),
883            lambdas: None,
884        };
885        let result = LogFunc::new()
886            .invoke_with_args(args)
887            .expect("failed to initialize function log");
888
889        match result {
890            ColumnarValue::Array(arr) => {
891                let floats = as_float64_array(&arr)
892                    .expect("failed to convert result to a Float64Array");
893
894                assert_eq!(floats.len(), 1);
895                assert!((floats.value(0) - 6.0).abs() < 1e-10);
896            }
897            ColumnarValue::Scalar(_) => {
898                panic!("Expected an array value")
899            }
900        }
901    }
902
903    #[test]
904    fn test_log_decimal128_unary() {
905        let arg_field = Field::new("a", DataType::Decimal128(38, 0), false).into();
906        let args = ScalarFunctionArgs {
907            args: vec![
908                ColumnarValue::Array(Arc::new(
909                    Decimal128Array::from(vec![10, 100, 1000, 10000, 12600, -123])
910                        .with_precision_and_scale(38, 0)
911                        .unwrap(),
912                )), // num
913            ],
914            arg_fields: vec![arg_field],
915            number_rows: 6,
916            return_field: Field::new("f", DataType::Float64, true).into(),
917            config_options: Arc::new(ConfigOptions::default()),
918            lambdas: None,
919        };
920        let result = LogFunc::new()
921            .invoke_with_args(args)
922            .expect("failed to initialize function log");
923
924        match result {
925            ColumnarValue::Array(arr) => {
926                let floats = as_float64_array(&arr)
927                    .expect("failed to convert result to a Float64Array");
928
929                assert_eq!(floats.len(), 6);
930                assert!((floats.value(0) - 1.0).abs() < 1e-10);
931                assert!((floats.value(1) - 2.0).abs() < 1e-10);
932                assert!((floats.value(2) - 3.0).abs() < 1e-10);
933                assert!((floats.value(3) - 4.0).abs() < 1e-10);
934                assert!((floats.value(4) - 4.0).abs() < 1e-10); // Integer rounding
935                assert!(floats.value(5).is_nan());
936            }
937            ColumnarValue::Scalar(_) => {
938                panic!("Expected an array value")
939            }
940        }
941    }
942
943    #[test]
944    fn test_log_decimal128_base_decimal() {
945        // Base stays 2 despite scaling
946        for base in [
947            ScalarValue::Decimal128(Some(i128::from(2)), 38, 0),
948            ScalarValue::Decimal128(Some(i128::from(2000)), 38, 3),
949        ] {
950            let arg_fields = vec![
951                Field::new("b", DataType::Decimal128(38, 0), false).into(),
952                Field::new("x", DataType::Decimal128(38, 0), false).into(),
953            ];
954            let args = ScalarFunctionArgs {
955                args: vec![
956                    ColumnarValue::Scalar(base), // base
957                    ColumnarValue::Scalar(ScalarValue::Decimal128(Some(64), 38, 0)), // num
958                ],
959                arg_fields,
960                number_rows: 1,
961                return_field: Field::new("f", DataType::Float64, true).into(),
962                config_options: Arc::new(ConfigOptions::default()),
963                lambdas: None,
964            };
965            let result = LogFunc::new()
966                .invoke_with_args(args)
967                .expect("failed to initialize function log");
968
969            match result {
970                ColumnarValue::Array(arr) => {
971                    let floats = as_float64_array(&arr)
972                        .expect("failed to convert result to a Float64Array");
973
974                    assert_eq!(floats.len(), 1);
975                    assert!((floats.value(0) - 6.0).abs() < 1e-10);
976                }
977                ColumnarValue::Scalar(_) => {
978                    panic!("Expected an array value")
979                }
980            }
981        }
982    }
983
984    #[test]
985    fn test_log_decimal128_value_scale() {
986        // Value stays 1000 despite scaling
987        for value in [
988            ScalarValue::Decimal128(Some(i128::from(1000)), 38, 0),
989            ScalarValue::Decimal128(Some(i128::from(10000)), 38, 1),
990            ScalarValue::Decimal128(Some(i128::from(1000000)), 38, 3),
991        ] {
992            let arg_fields = vec![
993                Field::new("b", DataType::Decimal128(38, 0), false).into(),
994                Field::new("x", DataType::Decimal128(38, 0), false).into(),
995            ];
996            let args = ScalarFunctionArgs {
997                args: vec![
998                    ColumnarValue::Scalar(value), // base
999                ],
1000                arg_fields,
1001                number_rows: 1,
1002                return_field: Field::new("f", DataType::Float64, true).into(),
1003                config_options: Arc::new(ConfigOptions::default()),
1004                lambdas: None,
1005            };
1006            let result = LogFunc::new()
1007                .invoke_with_args(args)
1008                .expect("failed to initialize function log");
1009
1010            match result {
1011                ColumnarValue::Array(arr) => {
1012                    let floats = as_float64_array(&arr)
1013                        .expect("failed to convert result to a Float64Array");
1014
1015                    assert_eq!(floats.len(), 1);
1016                    assert!((floats.value(0) - 3.0).abs() < 1e-10);
1017                }
1018                ColumnarValue::Scalar(_) => {
1019                    panic!("Expected an array value")
1020                }
1021            }
1022        }
1023    }
1024
1025    #[test]
1026    fn test_log_decimal256_unary() {
1027        let arg_field = Field::new(
1028            "a",
1029            DataType::Decimal256(DECIMAL256_MAX_PRECISION, 0),
1030            false,
1031        )
1032        .into();
1033        let args = ScalarFunctionArgs {
1034            args: vec![
1035                ColumnarValue::Array(Arc::new(
1036                    Decimal256Array::from(vec![
1037                        Some(i256::from(10)),
1038                        Some(i256::from(100)),
1039                        Some(i256::from(1000)),
1040                        Some(i256::from(10000)),
1041                        Some(i256::from(12600)),
1042                        // Slightly lower than i128 max - can calculate
1043                        Some(i256::from_i128(i128::MAX) - i256::from(1000)),
1044                        // Give NaN for incorrect inputs, as in f64::log
1045                        Some(i256::from(-123)),
1046                    ])
1047                    .with_precision_and_scale(DECIMAL256_MAX_PRECISION, 0)
1048                    .unwrap(),
1049                )), // num
1050            ],
1051            arg_fields: vec![arg_field],
1052            number_rows: 7,
1053            return_field: Field::new("f", DataType::Float64, true).into(),
1054            config_options: Arc::new(ConfigOptions::default()),
1055            lambdas: None,
1056        };
1057        let result = LogFunc::new()
1058            .invoke_with_args(args)
1059            .expect("failed to initialize function log");
1060
1061        match result {
1062            ColumnarValue::Array(arr) => {
1063                let floats = as_float64_array(&arr)
1064                    .expect("failed to convert result to a Float64Array");
1065
1066                assert_eq!(floats.len(), 7);
1067                eprintln!("floats {:?}", &floats);
1068                assert!((floats.value(0) - 1.0).abs() < 1e-10);
1069                assert!((floats.value(1) - 2.0).abs() < 1e-10);
1070                assert!((floats.value(2) - 3.0).abs() < 1e-10);
1071                assert!((floats.value(3) - 4.0).abs() < 1e-10);
1072                assert!((floats.value(4) - 4.0).abs() < 1e-10); // Integer rounding for float log
1073                assert!((floats.value(5) - 38.0).abs() < 1e-10);
1074                assert!(floats.value(6).is_nan());
1075            }
1076            ColumnarValue::Scalar(_) => {
1077                panic!("Expected an array value")
1078            }
1079        }
1080    }
1081
1082    #[test]
1083    fn test_log_decimal128_wrong_base() {
1084        let arg_fields = vec![
1085            Field::new("b", DataType::Float64, false).into(),
1086            Field::new("x", DataType::Decimal128(38, 0), false).into(),
1087        ];
1088        let args = ScalarFunctionArgs {
1089            args: vec![
1090                ColumnarValue::Scalar(ScalarValue::Float64(Some(-2.0))), // base
1091                ColumnarValue::Scalar(ScalarValue::Decimal128(Some(64), 38, 0)), // num
1092            ],
1093            arg_fields,
1094            number_rows: 1,
1095            return_field: Field::new("f", DataType::Float64, true).into(),
1096            config_options: Arc::new(ConfigOptions::default()),
1097            lambdas: None,
1098        };
1099        let result = LogFunc::new().invoke_with_args(args);
1100        assert!(result.is_err());
1101        assert_eq!(
1102            "Arrow error: Compute error: Log base must be greater than 1: -2",
1103            result.unwrap_err().to_string().lines().next().unwrap()
1104        );
1105    }
1106
1107    #[test]
1108    fn test_log_decimal256_error() {
1109        let arg_field = Field::new("a", DataType::Decimal256(38, 0), false).into();
1110        let args = ScalarFunctionArgs {
1111            args: vec![
1112                ColumnarValue::Array(Arc::new(Decimal256Array::from(vec![
1113                    // Slightly larger than i128
1114                    Some(i256::from_i128(i128::MAX) + i256::from(1000)),
1115                ]))), // num
1116            ],
1117            arg_fields: vec![arg_field],
1118            number_rows: 1,
1119            return_field: Field::new("f", DataType::Float64, true).into(),
1120            config_options: Arc::new(ConfigOptions::default()),
1121            lambdas: None,
1122        };
1123        let result = LogFunc::new().invoke_with_args(args);
1124        assert!(result.is_err());
1125        assert_eq!(result.unwrap_err().to_string().lines().next().unwrap(),
1126            "Arrow error: Not yet implemented: Log of Decimal256 larger than Decimal128 is not yet supported: 170141183460469231731687303715884106727"
1127        );
1128    }
1129}