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