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