Skip to main content

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