## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/functions/src/math/monotonicity.rs

    Line
    Count
    Source
    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
    use std::sync::LazyLock;
    19
    20
    use datafusion_common::{exec_err, Result, ScalarValue};
    21
    use datafusion_doc::scalar_doc_sections::DOC_SECTION_MATH;
    22
    use datafusion_expr::interval_arithmetic::Interval;
    23
    use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
    24
    use datafusion_expr::Documentation;
    25
    26
    /// Non-increasing on the interval \[−1, 1\], undefined otherwise.
    27
    0
    pub fn acos_order(input: &[ExprProperties]) -> Result<SortProperties> {
    28
    0
        let arg = &input[0];
    29
    0
        let range = &arg.range;
    30
    31
    0
        let valid_domain =
    32
    0
            Interval::make_symmetric_unit_interval(&range.lower().data_type())?;
    33
    34
    0
        if valid_domain.contains(range)? == Interval::TRUE {
    35
    0
            Ok(-arg.sort_properties)
    36
        } else {
    37
    0
            exec_err!("Input range of ACOS contains out-of-domain values")
    38
        }
    39
    0
    }
    40
    41
    0
    static DOCUMENTATION_ACOS: LazyLock<Documentation> = LazyLock::new(|| {
    42
    0
        Documentation::builder(
    43
            DOC_SECTION_MATH,
    44
            "Returns the arc cosine or inverse cosine of a number.",
    45
            "acos(numeric_expression)",
    46
        )
    47
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    48
    0
        .with_sql_example(
    49
            r#"```sql
    50
    > SELECT acos(1);
    51
    +----------+
    52
    | acos(1)  |
    53
    +----------+
    54
    | 0.0      |
    55
    +----------+
    56
    ```"#,
    57
        )
    58
    0
        .build()
    59
    0
    });
    60
    61
    0
    pub fn get_acos_doc() -> &'static Documentation {
    62
    0
        &DOCUMENTATION_ACOS
    63
    0
    }
    64
    65
    /// Non-decreasing for x ≥ 1, undefined otherwise.
    66
    0
    pub fn acosh_order(input: &[ExprProperties]) -> Result<SortProperties> {
    67
    0
        let arg = &input[0];
    68
    0
        let range = &arg.range;
    69
    70
    0
        let valid_domain = Interval::try_new(
    71
    0
            ScalarValue::new_one(&range.lower().data_type())?,
    72
    0
            ScalarValue::try_from(&range.upper().data_type())?,
    73
    0
        )?;
    74
    75
    0
        if valid_domain.contains(range)? == Interval::TRUE {
    76
    0
            Ok(arg.sort_properties)
    77
        } else {
    78
    0
            exec_err!("Input range of ACOSH contains out-of-domain values")
    79
        }
    80
    0
    }
    81
    82
    static DOCUMENTATION_ACOSH: LazyLock<Documentation> =
    83
    0
        LazyLock::new(|| {
    84
    0
            Documentation::builder(
    85
            DOC_SECTION_MATH,
    86
            "Returns the area hyperbolic cosine or inverse hyperbolic cosine of a number.",
    87
            "acosh(numeric_expression)",
    88
        )
    89
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    90
    0
        .with_sql_example(r#"```sql
    91
    > SELECT acosh(2);
    92
    +------------+
    93
    | acosh(2)   |
    94
    +------------+
    95
    | 1.31696    |
    96
    +------------+
    97
    ```"#)
    98
    0
        .build()
    99
    0
        });
    100
    101
    0
    pub fn get_acosh_doc() -> &'static Documentation {
    102
    0
        &DOCUMENTATION_ACOSH
    103
    0
    }
    104
    105
    /// Non-decreasing on the interval \[−1, 1\], undefined otherwise.
    106
    0
    pub fn asin_order(input: &[ExprProperties]) -> Result<SortProperties> {
    107
    0
        let arg = &input[0];
    108
    0
        let range = &arg.range;
    109
    110
    0
        let valid_domain =
    111
    0
            Interval::make_symmetric_unit_interval(&range.lower().data_type())?;
    112
    113
    0
        if valid_domain.contains(range)? == Interval::TRUE {
    114
    0
            Ok(arg.sort_properties)
    115
        } else {
    116
    0
            exec_err!("Input range of ASIN contains out-of-domain values")
    117
        }
    118
    0
    }
    119
    120
    0
    static DOCUMENTATION_ASIN: LazyLock<Documentation> = LazyLock::new(|| {
    121
    0
        Documentation::builder(
    122
            DOC_SECTION_MATH,
    123
            "Returns the arc sine or inverse sine of a number.",
    124
            "asin(numeric_expression)",
    125
        )
    126
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    127
    0
        .with_sql_example(
    128
            r#"```sql
    129
    > SELECT asin(0.5);
    130
    +------------+
    131
    | asin(0.5)  |
    132
    +------------+
    133
    | 0.5235988  |
    134
    +------------+
    135
    ```"#,
    136
        )
    137
    0
        .build()
    138
    0
    });
    139
    140
    0
    pub fn get_asin_doc() -> &'static Documentation {
    141
    0
        &DOCUMENTATION_ASIN
    142
    0
    }
    143
    144
    /// Non-decreasing for all real numbers.
    145
    0
    pub fn asinh_order(input: &[ExprProperties]) -> Result<SortProperties> {
    146
    0
        Ok(input[0].sort_properties)
    147
    0
    }
    148
    149
    0
    static DOCUMENTATION_ASINH: LazyLock<Documentation> = LazyLock::new(|| {
    150
    0
        Documentation::builder(
    151
            DOC_SECTION_MATH,
    152
            "Returns the area hyperbolic sine or inverse hyperbolic sine of a number.",
    153
            "asinh(numeric_expression)",
    154
        )
    155
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    156
    0
        .with_sql_example(
    157
            r#" ```sql
    158
    > SELECT asinh(1);
    159
    +------------+
    160
    | asinh(1)   |
    161
    +------------+
    162
    | 0.8813736  |
    163
    +------------+
    164
    ```"#,
    165
        )
    166
    0
        .build()
    167
    0
    });
    168
    169
    0
    pub fn get_asinh_doc() -> &'static Documentation {
    170
    0
        &DOCUMENTATION_ASINH
    171
    0
    }
    172
    173
    /// Non-decreasing for all real numbers.
    174
    0
    pub fn atan_order(input: &[ExprProperties]) -> Result<SortProperties> {
    175
    0
        Ok(input[0].sort_properties)
    176
    0
    }
    177
    178
    0
    static DOCUMENTATION_ATAN: LazyLock<Documentation> = LazyLock::new(|| {
    179
    0
        Documentation::builder(
    180
            DOC_SECTION_MATH,
    181
            "Returns the arc tangent or inverse tangent of a number.",
    182
            "atan(numeric_expression)",
    183
        )
    184
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    185
    0
        .with_sql_example(
    186
            r#"```sql
    187
        > SELECT atan(1);
    188
    +-----------+
    189
    | atan(1)   |
    190
    +-----------+
    191
    | 0.7853982 |
    192
    +-----------+
    193
    ```"#,
    194
        )
    195
    0
        .build()
    196
    0
    });
    197
    198
    0
    pub fn get_atan_doc() -> &'static Documentation {
    199
    0
        &DOCUMENTATION_ATAN
    200
    0
    }
    201
    202
    /// Non-decreasing on the interval \[−1, 1\], undefined otherwise.
    203
    0
    pub fn atanh_order(input: &[ExprProperties]) -> Result<SortProperties> {
    204
    0
        let arg = &input[0];
    205
    0
        let range = &arg.range;
    206
    207
    0
        let valid_domain =
    208
    0
            Interval::make_symmetric_unit_interval(&range.lower().data_type())?;
    209
    210
    0
        if valid_domain.contains(range)? == Interval::TRUE {
    211
    0
            Ok(arg.sort_properties)
    212
        } else {
    213
    0
            exec_err!("Input range of ATANH contains out-of-domain values")
    214
        }
    215
    0
    }
    216
    217
    static DOCUMENTATION_ATANH: LazyLock<Documentation> =
    218
    0
        LazyLock::new(|| {
    219
    0
            Documentation::builder(
    220
            DOC_SECTION_MATH,
    221
            "Returns the area hyperbolic tangent or inverse hyperbolic tangent of a number.",
    222
            "atanh(numeric_expression)",
    223
        )
    224
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    225
    0
        .with_sql_example(r#"```sql
    226
        > SELECT atanh(0.5);
    227
    +-------------+
    228
    | atanh(0.5)  |
    229
    +-------------+
    230
    | 0.5493061   |
    231
    +-------------+
    232
    ```"#)
    233
    0
        .build()
    234
    0
        });
    235
    236
    0
    pub fn get_atanh_doc() -> &'static Documentation {
    237
    0
        &DOCUMENTATION_ATANH
    238
    0
    }
    239
    240
    /// Order depends on the quadrant.
    241
    // TODO: Implement ordering rule of the ATAN2 function.
    242
    0
    pub fn atan2_order(_input: &[ExprProperties]) -> Result<SortProperties> {
    243
    0
        Ok(SortProperties::Unordered)
    244
    0
    }
    245
    246
    static DOCUMENTATION_ATANH2: LazyLock<Documentation> =
    247
    0
        LazyLock::new(|| {
    248
    0
            Documentation::builder(
    249
            DOC_SECTION_MATH,
    250
            "Returns the arc tangent or inverse tangent of `expression_y / expression_x`.",
    251
            "atan2(expression_y, expression_x)",
    252
        )
    253
    0
        .with_argument(
    254
            "expression_y",
    255
            r#"First numeric expression to operate on.
    256
    Can be a constant, column, or function, and any combination of arithmetic operators."#,
    257
        )
    258
    0
        .with_argument(
    259
            "expression_x",
    260
            r#"Second numeric expression to operate on.
    261
    Can be a constant, column, or function, and any combination of arithmetic operators."#,
    262
        )
    263
    0
        .with_sql_example(r#"```sql
    264
    > SELECT atan2(1, 1);
    265
    +------------+
    266
    | atan2(1,1) |
    267
    +------------+
    268
    | 0.7853982  |
    269
    +------------+
    270
    ```"#)
    271
    0
        .build()
    272
    0
        });
    273
    274
    0
    pub fn get_atan2_doc() -> &'static Documentation {
    275
    0
        &DOCUMENTATION_ATANH2
    276
    0
    }
    277
    278
    /// Non-decreasing for all real numbers.
    279
    0
    pub fn cbrt_order(input: &[ExprProperties]) -> Result<SortProperties> {
    280
    0
        Ok(input[0].sort_properties)
    281
    0
    }
    282
    283
    0
    static DOCUMENTATION_CBRT: LazyLock<Documentation> = LazyLock::new(|| {
    284
    0
        Documentation::builder(
    285
            DOC_SECTION_MATH,
    286
            "Returns the cube root of a number.",
    287
            "cbrt(numeric_expression)",
    288
        )
    289
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    290
    0
        .with_sql_example(
    291
            r#"```sql
    292
    > SELECT cbrt(27);
    293
    +-----------+
    294
    | cbrt(27)  |
    295
    +-----------+
    296
    | 3.0       |
    297
    +-----------+
    298
    ```"#,
    299
        )
    300
    0
        .build()
    301
    0
    });
    302
    303
    0
    pub fn get_cbrt_doc() -> &'static Documentation {
    304
    0
        &DOCUMENTATION_CBRT
    305
    0
    }
    306
    307
    /// Non-decreasing for all real numbers.
    308
    0
    pub fn ceil_order(input: &[ExprProperties]) -> Result<SortProperties> {
    309
    0
        Ok(input[0].sort_properties)
    310
    0
    }
    311
    312
    0
    static DOCUMENTATION_CEIL: LazyLock<Documentation> = LazyLock::new(|| {
    313
    0
        Documentation::builder(
    314
            DOC_SECTION_MATH,
    315
            "Returns the nearest integer greater than or equal to a number.",
    316
            "ceil(numeric_expression)",
    317
        )
    318
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    319
    0
        .with_sql_example(
    320
            r#"```sql
    321
        > SELECT ceil(3.14);
    322
    +------------+
    323
    | ceil(3.14) |
    324
    +------------+
    325
    | 4.0        |
    326
    +------------+
    327
    ```"#,
    328
        )
    329
    0
        .build()
    330
    0
    });
    331
    332
    0
    pub fn get_ceil_doc() -> &'static Documentation {
    333
    0
        &DOCUMENTATION_CEIL
    334
    0
    }
    335
    336
    /// Non-increasing on \[0, π\] and then non-decreasing on \[π, 2π\].
    337
    /// This pattern repeats periodically with a period of 2π.
    338
    // TODO: Implement ordering rule of the ATAN2 function.
    339
    0
    pub fn cos_order(_input: &[ExprProperties]) -> Result<SortProperties> {
    340
    0
        Ok(SortProperties::Unordered)
    341
    0
    }
    342
    343
    0
    static DOCUMENTATION_COS: LazyLock<Documentation> = LazyLock::new(|| {
    344
    0
        Documentation::builder(
    345
            DOC_SECTION_MATH,
    346
            "Returns the cosine of a number.",
    347
            "cos(numeric_expression)",
    348
        )
    349
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    350
    0
        .with_sql_example(
    351
            r#"```sql
    352
    > SELECT cos(0);
    353
    +--------+
    354
    | cos(0) |
    355
    +--------+
    356
    | 1.0    |
    357
    +--------+
    358
    ```"#,
    359
        )
    360
    0
        .build()
    361
    0
    });
    362
    363
    0
    pub fn get_cos_doc() -> &'static Documentation {
    364
    0
        &DOCUMENTATION_COS
    365
    0
    }
    366
    367
    /// Non-decreasing for x ≥ 0 and symmetrically non-increasing for x ≤ 0.
    368
    0
    pub fn cosh_order(input: &[ExprProperties]) -> Result<SortProperties> {
    369
    0
        let arg = &input[0];
    370
    0
        let range = &arg.range;
    371
    372
    0
        let zero_point = Interval::make_zero(&range.lower().data_type())?;
    373
    374
    0
        if range.gt_eq(&zero_point)? == Interval::TRUE {
    375
    0
            Ok(arg.sort_properties)
    376
    0
        } else if range.lt_eq(&zero_point)? == Interval::TRUE {
    377
    0
            Ok(-arg.sort_properties)
    378
        } else {
    379
    0
            Ok(SortProperties::Unordered)
    380
        }
    381
    0
    }
    382
    383
    0
    static DOCUMENTATION_COSH: LazyLock<Documentation> = LazyLock::new(|| {
    384
    0
        Documentation::builder(
    385
            DOC_SECTION_MATH,
    386
            "Returns the hyperbolic cosine of a number.",
    387
            "cosh(numeric_expression)",
    388
        )
    389
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    390
    0
        .with_sql_example(
    391
            r#"```sql
    392
    > SELECT cosh(1);
    393
    +-----------+
    394
    | cosh(1)   |
    395
    +-----------+
    396
    | 1.5430806 |
    397
    +-----------+
    398
    ```"#,
    399
        )
    400
    0
        .build()
    401
    0
    });
    402
    403
    0
    pub fn get_cosh_doc() -> &'static Documentation {
    404
    0
        &DOCUMENTATION_COSH
    405
    0
    }
    406
    407
    /// Non-decreasing function that converts radians to degrees.
    408
    0
    pub fn degrees_order(input: &[ExprProperties]) -> Result<SortProperties> {
    409
    0
        Ok(input[0].sort_properties)
    410
    0
    }
    411
    412
    0
    static DOCUMENTATION_DEGREES: LazyLock<Documentation> = LazyLock::new(|| {
    413
    0
        Documentation::builder(
    414
            DOC_SECTION_MATH,
    415
            "Converts radians to degrees.",
    416
            "degrees(numeric_expression)",
    417
        )
    418
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    419
    0
        .with_sql_example(
    420
            r#"```sql
    421
        > SELECT degrees(pi());
    422
    +------------+
    423
    | degrees(0) |
    424
    +------------+
    425
    | 180.0      |
    426
    +------------+
    427
    ```"#,
    428
        )
    429
    0
        .build()
    430
    0
    });
    431
    432
    0
    pub fn get_degrees_doc() -> &'static Documentation {
    433
    0
        &DOCUMENTATION_DEGREES
    434
    0
    }
    435
    436
    /// Non-decreasing for all real numbers.
    437
    0
    pub fn exp_order(input: &[ExprProperties]) -> Result<SortProperties> {
    438
    0
        Ok(input[0].sort_properties)
    439
    0
    }
    440
    441
    0
    static DOCUMENTATION_EXP: LazyLock<Documentation> = LazyLock::new(|| {
    442
    0
        Documentation::builder(
    443
            DOC_SECTION_MATH,
    444
            "Returns the base-e exponential of a number.",
    445
            "exp(numeric_expression)",
    446
        )
    447
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    448
    0
        .with_sql_example(
    449
            r#"```sql
    450
    > SELECT exp(1);
    451
    +---------+
    452
    | exp(1)  |
    453
    +---------+
    454
    | 2.71828 |
    455
    +---------+
    456
    ```"#,
    457
        )
    458
    0
        .build()
    459
    0
    });
    460
    461
    0
    pub fn get_exp_doc() -> &'static Documentation {
    462
    0
        &DOCUMENTATION_EXP
    463
    0
    }
    464
    465
    /// Non-decreasing for all real numbers.
    466
    0
    pub fn floor_order(input: &[ExprProperties]) -> Result<SortProperties> {
    467
    0
        Ok(input[0].sort_properties)
    468
    0
    }
    469
    470
    0
    static DOCUMENTATION_FLOOR: LazyLock<Documentation> = LazyLock::new(|| {
    471
    0
        Documentation::builder(
    472
            DOC_SECTION_MATH,
    473
            "Returns the nearest integer less than or equal to a number.",
    474
            "floor(numeric_expression)",
    475
        )
    476
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    477
    0
        .with_sql_example(
    478
            r#"```sql
    479
    > SELECT floor(3.14);
    480
    +-------------+
    481
    | floor(3.14) |
    482
    +-------------+
    483
    | 3.0         |
    484
    +-------------+
    485
    ```"#,
    486
        )
    487
    0
        .build()
    488
    0
    });
    489
    490
    0
    pub fn get_floor_doc() -> &'static Documentation {
    491
    0
        &DOCUMENTATION_FLOOR
    492
    0
    }
    493
    494
    /// Non-decreasing for x ≥ 0, undefined otherwise.
    495
    0
    pub fn ln_order(input: &[ExprProperties]) -> Result<SortProperties> {
    496
    0
        let arg = &input[0];
    497
    0
        let range = &arg.range;
    498
    499
    0
        let zero_point = Interval::make_zero(&range.lower().data_type())?;
    500
    501
    0
        if range.gt_eq(&zero_point)? == Interval::TRUE {
    502
    0
            Ok(arg.sort_properties)
    503
        } else {
    504
    0
            exec_err!("Input range of LN contains out-of-domain values")
    505
        }
    506
    0
    }
    507
    508
    0
    static DOCUMENTATION_LN: LazyLock<Documentation> = LazyLock::new(|| {
    509
    0
        Documentation::builder(
    510
            DOC_SECTION_MATH,
    511
            "Returns the natural logarithm of a number.",
    512
            "ln(numeric_expression)",
    513
        )
    514
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    515
    0
        .with_sql_example(
    516
            r#"```sql
    517
    > SELECT ln(2.71828);
    518
    +-------------+
    519
    | ln(2.71828) |
    520
    +-------------+
    521
    | 1.0         |
    522
    +-------------+
    523
    ```"#,
    524
        )
    525
    0
        .build()
    526
    0
    });
    527
    528
    0
    pub fn get_ln_doc() -> &'static Documentation {
    529
    0
        &DOCUMENTATION_LN
    530
    0
    }
    531
    532
    /// Non-decreasing for x ≥ 0, undefined otherwise.
    533
    0
    pub fn log2_order(input: &[ExprProperties]) -> Result<SortProperties> {
    534
    0
        let arg = &input[0];
    535
    0
        let range = &arg.range;
    536
    537
    0
        let zero_point = Interval::make_zero(&range.lower().data_type())?;
    538
    539
    0
        if range.gt_eq(&zero_point)? == Interval::TRUE {
    540
    0
            Ok(arg.sort_properties)
    541
        } else {
    542
    0
            exec_err!("Input range of LOG2 contains out-of-domain values")
    543
        }
    544
    0
    }
    545
    546
    0
    static DOCUMENTATION_LOG2: LazyLock<Documentation> = LazyLock::new(|| {
    547
    0
        Documentation::builder(
    548
            DOC_SECTION_MATH,
    549
            "Returns the base-2 logarithm of a number.",
    550
            "log2(numeric_expression)",
    551
        )
    552
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    553
    0
        .with_sql_example(
    554
            r#"```sql
    555
    > SELECT log2(8);
    556
    +-----------+
    557
    | log2(8)   |
    558
    +-----------+
    559
    | 3.0       |
    560
    +-----------+
    561
    ```"#,
    562
        )
    563
    0
        .build()
    564
    0
    });
    565
    566
    0
    pub fn get_log2_doc() -> &'static Documentation {
    567
    0
        &DOCUMENTATION_LOG2
    568
    0
    }
    569
    570
    /// Non-decreasing for x ≥ 0, undefined otherwise.
    571
    0
    pub fn log10_order(input: &[ExprProperties]) -> Result<SortProperties> {
    572
    0
        let arg = &input[0];
    573
    0
        let range = &arg.range;
    574
    575
    0
        let zero_point = Interval::make_zero(&range.lower().data_type())?;
    576
    577
    0
        if range.gt_eq(&zero_point)? == Interval::TRUE {
    578
    0
            Ok(arg.sort_properties)
    579
        } else {
    580
    0
            exec_err!("Input range of LOG10 contains out-of-domain values")
    581
        }
    582
    0
    }
    583
    584
    0
    static DOCUMENTATION_LOG10: LazyLock<Documentation> = LazyLock::new(|| {
    585
    0
        Documentation::builder(
    586
            DOC_SECTION_MATH,
    587
            "Returns the base-10 logarithm of a number.",
    588
            "log10(numeric_expression)",
    589
        )
    590
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    591
    0
        .with_sql_example(
    592
            r#"```sql
    593
    > SELECT log10(100);
    594
    +-------------+
    595
    | log10(100)  |
    596
    +-------------+
    597
    | 2.0         |
    598
    +-------------+
    599
    ```"#,
    600
        )
    601
    0
        .build()
    602
    0
    });
    603
    604
    0
    pub fn get_log10_doc() -> &'static Documentation {
    605
    0
        &DOCUMENTATION_LOG10
    606
    0
    }
    607
    608
    /// Non-decreasing for all real numbers x.
    609
    0
    pub fn radians_order(input: &[ExprProperties]) -> Result<SortProperties> {
    610
    0
        Ok(input[0].sort_properties)
    611
    0
    }
    612
    613
    0
    static DOCUMENTATION_RADIANS: LazyLock<Documentation> = LazyLock::new(|| {
    614
    0
        Documentation::builder(
    615
            DOC_SECTION_MATH,
    616
            "Converts degrees to radians.",
    617
            "radians(numeric_expression)",
    618
        )
    619
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    620
    0
        .with_sql_example(
    621
            r#"```sql
    622
    > SELECT radians(180);
    623
    +----------------+
    624
    | radians(180)   |
    625
    +----------------+
    626
    | 3.14159265359  |
    627
    +----------------+
    628
    ```"#,
    629
        )
    630
    0
        .build()
    631
    0
    });
    632
    633
    0
    pub fn get_radians_doc() -> &'static Documentation {
    634
    0
        &DOCUMENTATION_RADIANS
    635
    0
    }
    636
    637
    /// Non-decreasing on \[0, π\] and then non-increasing on \[π, 2π\].
    638
    /// This pattern repeats periodically with a period of 2π.
    639
    // TODO: Implement ordering rule of the SIN function.
    640
    0
    pub fn sin_order(_input: &[ExprProperties]) -> Result<SortProperties> {
    641
    0
        Ok(SortProperties::Unordered)
    642
    0
    }
    643
    644
    0
    static DOCUMENTATION_SIN: LazyLock<Documentation> = LazyLock::new(|| {
    645
    0
        Documentation::builder(
    646
            DOC_SECTION_MATH,
    647
            "Returns the sine of a number.",
    648
            "sin(numeric_expression)",
    649
        )
    650
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    651
    0
        .with_sql_example(
    652
            r#"```sql
    653
    > SELECT sin(0);
    654
    +----------+
    655
    | sin(0)   |
    656
    +----------+
    657
    | 0.0      |
    658
    +----------+
    659
    ```"#,
    660
        )
    661
    0
        .build()
    662
    0
    });
    663
    664
    0
    pub fn get_sin_doc() -> &'static Documentation {
    665
    0
        &DOCUMENTATION_SIN
    666
    0
    }
    667
    668
    /// Non-decreasing for all real numbers.
    669
    0
    pub fn sinh_order(input: &[ExprProperties]) -> Result<SortProperties> {
    670
    0
        Ok(input[0].sort_properties)
    671
    0
    }
    672
    673
    0
    static DOCUMENTATION_SINH: LazyLock<Documentation> = LazyLock::new(|| {
    674
    0
        Documentation::builder(
    675
            DOC_SECTION_MATH,
    676
            "Returns the hyperbolic sine of a number.",
    677
            "sinh(numeric_expression)",
    678
        )
    679
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    680
    0
        .with_sql_example(
    681
            r#"```sql
    682
    > SELECT sinh(1);
    683
    +-----------+
    684
    | sinh(1)   |
    685
    +-----------+
    686
    | 1.1752012 |
    687
    +-----------+
    688
    ```"#,
    689
        )
    690
    0
        .build()
    691
    0
    });
    692
    693
    0
    pub fn get_sinh_doc() -> &'static Documentation {
    694
    0
        &DOCUMENTATION_SINH
    695
    0
    }
    696
    697
    /// Non-decreasing for x ≥ 0, undefined otherwise.
    698
    0
    pub fn sqrt_order(input: &[ExprProperties]) -> Result<SortProperties> {
    699
    0
        let arg = &input[0];
    700
    0
        let range = &arg.range;
    701
    702
    0
        let zero_point = Interval::make_zero(&range.lower().data_type())?;
    703
    704
    0
        if range.gt_eq(&zero_point)? == Interval::TRUE {
    705
    0
            Ok(arg.sort_properties)
    706
        } else {
    707
    0
            exec_err!("Input range of SQRT contains out-of-domain values")
    708
        }
    709
    0
    }
    710
    711
    0
    static DOCUMENTATION_SQRT: LazyLock<Documentation> = LazyLock::new(|| {
    712
    0
        Documentation::builder(
    713
            DOC_SECTION_MATH,
    714
            "Returns the square root of a number.",
    715
            "sqrt(numeric_expression)",
    716
        )
    717
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    718
    0
        .build()
    719
    0
    });
    720
    721
    0
    pub fn get_sqrt_doc() -> &'static Documentation {
    722
    0
        &DOCUMENTATION_SQRT
    723
    0
    }
    724
    725
    /// Non-decreasing between vertical asymptotes at x = k * π ± π / 2 for any
    726
    /// integer k.
    727
    // TODO: Implement ordering rule of the TAN function.
    728
    0
    pub fn tan_order(_input: &[ExprProperties]) -> Result<SortProperties> {
    729
    0
        Ok(SortProperties::Unordered)
    730
    0
    }
    731
    732
    0
    static DOCUMENTATION_TAN: LazyLock<Documentation> = LazyLock::new(|| {
    733
    0
        Documentation::builder(
    734
            DOC_SECTION_MATH,
    735
            "Returns the tangent of a number.",
    736
            "tan(numeric_expression)",
    737
        )
    738
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    739
    0
        .with_sql_example(
    740
            r#"```sql
    741
    > SELECT tan(pi()/4);
    742
    +--------------+
    743
    | tan(PI()/4)  |
    744
    +--------------+
    745
    | 1.0          |
    746
    +--------------+
    747
    ```"#,
    748
        )
    749
    0
        .build()
    750
    0
    });
    751
    752
    0
    pub fn get_tan_doc() -> &'static Documentation {
    753
    0
        &DOCUMENTATION_TAN
    754
    0
    }
    755
    756
    /// Non-decreasing for all real numbers.
    757
    0
    pub fn tanh_order(input: &[ExprProperties]) -> Result<SortProperties> {
    758
    0
        Ok(input[0].sort_properties)
    759
    0
    }
    760
    761
    0
    static DOCUMENTATION_TANH: LazyLock<Documentation> = LazyLock::new(|| {
    762
    0
        Documentation::builder(
    763
            DOC_SECTION_MATH,
    764
            "Returns the hyperbolic tangent of a number.",
    765
            "tanh(numeric_expression)",
    766
        )
    767
    0
        .with_standard_argument("numeric_expression", Some("Numeric"))
    768
    0
        .with_sql_example(
    769
            r#"```sql
    770
      > SELECT tanh(20);
    771
      +----------+
    772
      | tanh(20) |
    773
      +----------+
    774
      | 1.0      |
    775
      +----------+
    776
      ```"#,
    777
        )
    778
    0
        .build()
    779
    0
    });
    780
    781
    0
    pub fn get_tanh_doc() -> &'static Documentation {
    782
    0
        &DOCUMENTATION_TANH
    783
    0
    }
    784
    785
    #[cfg(test)]
    786
    mod tests {
    787
        use arrow::compute::SortOptions;
    788
        use datafusion_common::Result;
    789
    790
        use super::*;
    791
    792
        #[derive(Debug)]
    793
        struct MonotonicityTestCase {
    794
            name: &'static str,
    795
            func: fn(&[ExprProperties]) -> Result<SortProperties>,
    796
            lower: f64,
    797
            upper: f64,
    798
            input_sort: SortProperties,
    799
            expected: Result<SortProperties>,
    800
        }
    801
    802
        #[test]
    803
        fn test_monotonicity_table() {
    804
            fn create_ep(lower: f64, upper: f64, sp: SortProperties) -> ExprProperties {
    805
                ExprProperties {
    806
                    range: Interval::try_new(
    807
                        ScalarValue::from(lower),
    808
                        ScalarValue::from(upper),
    809
                    )
    810
                    .unwrap(),
    811
                    sort_properties: sp,
    812
                    preserves_lex_ordering: false,
    813
                }
    814
            }
    815
    816
            let test_cases = vec![
    817
                MonotonicityTestCase {
    818
                    name: "acos_order within domain",
    819
                    func: acos_order,
    820
                    lower: -0.5,
    821
                    upper: 0.5,
    822
                    input_sort: SortProperties::Ordered(SortOptions {
    823
                        descending: false,
    824
                        nulls_first: false,
    825
                    }),
    826
                    expected: Ok(SortProperties::Ordered(SortOptions {
    827
                        descending: true,
    828
                        nulls_first: false,
    829
                    })),
    830
                },
    831
                MonotonicityTestCase {
    832
                    name: "acos_order out of domain",
    833
                    func: acos_order,
    834
                    lower: -2.0,
    835
                    upper: 1.0,
    836
                    input_sort: SortProperties::Ordered(SortOptions {
    837
                        descending: false,
    838
                        nulls_first: false,
    839
                    }),
    840
                    expected: exec_err!("Input range of ACOS contains out-of-domain values"),
    841
                },
    842
                MonotonicityTestCase {
    843
                    name: "acosh_order within domain",
    844
                    func: acosh_order,
    845
                    lower: 2.0,
    846
                    upper: 100.0,
    847
                    input_sort: SortProperties::Ordered(SortOptions {
    848
                        descending: false,
    849
                        nulls_first: true,
    850
                    }),
    851
                    expected: Ok(SortProperties::Ordered(SortOptions {
    852
                        descending: false,
    853
                        nulls_first: true,
    854
                    })),
    855
                },
    856
                MonotonicityTestCase {
    857
                    name: "acosh_order out of domain",
    858
                    func: acosh_order,
    859
                    lower: 0.5,
    860
                    upper: 1.0,
    861
                    input_sort: SortProperties::Ordered(SortOptions {
    862
                        descending: true,
    863
                        nulls_first: false,
    864
                    }),
    865
                    expected: exec_err!("Input range of ACOSH contains out-of-domain values"),
    866
                },
    867
                MonotonicityTestCase {
    868
                    name: "asin_order within domain",
    869
                    func: asin_order,
    870
                    lower: -0.5,
    871
                    upper: 0.5,
    872
                    input_sort: SortProperties::Ordered(SortOptions {
    873
                        descending: false,
    874
                        nulls_first: false,
    875
                    }),
    876
                    expected: Ok(SortProperties::Ordered(SortOptions {
    877
                        descending: false,
    878
                        nulls_first: false,
    879
                    })),
    880
                },
    881
                MonotonicityTestCase {
    882
                    name: "asin_order out of domain",
    883
                    func: asin_order,
    884
                    lower: -2.0,
    885
                    upper: 1.0,
    886
                    input_sort: SortProperties::Ordered(SortOptions {
    887
                        descending: false,
    888
                        nulls_first: false,
    889
                    }),
    890
                    expected: exec_err!("Input range of ASIN contains out-of-domain values"),
    891
                },
    892
                MonotonicityTestCase {
    893
                    name: "asinh_order within domain",
    894
                    func: asinh_order,
    895
                    lower: -1.0,
    896
                    upper: 1.0,
    897
                    input_sort: SortProperties::Ordered(SortOptions {
    898
                        descending: false,
    899
                        nulls_first: false,
    900
                    }),
    901
                    expected: Ok(SortProperties::Ordered(SortOptions {
    902
                        descending: false,
    903
                        nulls_first: false,
    904
                    })),
    905
                },
    906
                MonotonicityTestCase {
    907
                    name: "asinh_order out of domain",
    908
                    func: asinh_order,
    909
                    lower: -2.0,
    910
                    upper: 1.0,
    911
                    input_sort: SortProperties::Ordered(SortOptions {
    912
                        descending: false,
    913
                        nulls_first: false,
    914
                    }),
    915
                    expected: Ok(SortProperties::Ordered(SortOptions {
    916
                        descending: false,
    917
                        nulls_first: false,
    918
                    })),
    919
                },
    920
                MonotonicityTestCase {
    921
                    name: "atan_order within domain",
    922
                    func: atan_order,
    923
                    lower: -1.0,
    924
                    upper: 1.0,
    925
                    input_sort: SortProperties::Ordered(SortOptions {
    926
                        descending: false,
    927
                        nulls_first: false,
    928
                    }),
    929
                    expected: Ok(SortProperties::Ordered(SortOptions {
    930
                        descending: false,
    931
                        nulls_first: false,
    932
                    })),
    933
                },
    934
                MonotonicityTestCase {
    935
                    name: "atan_order out of domain",
    936
                    func: atan_order,
    937
                    lower: -2.0,
    938
                    upper: 1.0,
    939
                    input_sort: SortProperties::Ordered(SortOptions {
    940
                        descending: false,
    941
                        nulls_first: false,
    942
                    }),
    943
                    expected: Ok(SortProperties::Ordered(SortOptions {
    944
                        descending: false,
    945
                        nulls_first: false,
    946
                    })),
    947
                },
    948
                MonotonicityTestCase {
    949
                    name: "atanh_order within domain",
    950
                    func: atanh_order,
    951
                    lower: -0.6,
    952
                    upper: 0.6,
    953
                    input_sort: SortProperties::Ordered(SortOptions {
    954
                        descending: false,
    955
                        nulls_first: false,
    956
                    }),
    957
                    expected: Ok(SortProperties::Ordered(SortOptions {
    958
                        descending: false,
    959
                        nulls_first: false,
    960
                    })),
    961
                },
    962
                MonotonicityTestCase {
    963
                    name: "atanh_order out of domain",
    964
                    func: atanh_order,
    965
                    lower: -2.0,
    966
                    upper: 1.0,
    967
                    input_sort: SortProperties::Ordered(SortOptions {
    968
                        descending: false,
    969
                        nulls_first: false,
    970
                    }),
    971
                    expected: exec_err!("Input range of ATANH contains out-of-domain values"),
    972
                },
    973
                MonotonicityTestCase {
    974
                    name: "cbrt_order within domain",
    975
                    func: cbrt_order,
    976
                    lower: -1.0,
    977
                    upper: 1.0,
    978
                    input_sort: SortProperties::Ordered(SortOptions {
    979
                        descending: false,
    980
                        nulls_first: false,
    981
                    }),
    982
                    expected: Ok(SortProperties::Ordered(SortOptions {
    983
                        descending: false,
    984
                        nulls_first: false,
    985
                    })),
    986
                },
    987
                MonotonicityTestCase {
    988
                    name: "cbrt_order out of domain",
    989
                    func: cbrt_order,
    990
                    lower: -2.0,
    991
                    upper: 1.0,
    992
                    input_sort: SortProperties::Ordered(SortOptions {
    993
                        descending: false,
    994
                        nulls_first: false,
    995
                    }),
    996
                    expected: Ok(SortProperties::Ordered(SortOptions {
    997
                        descending: false,
    998
                        nulls_first: false,
    999
                    })),
    1000
                },
    1001
                MonotonicityTestCase {
    1002
                    name: "ceil_order within domain",
    1003
                    func: ceil_order,
    1004
                    lower: -1.0,
    1005
                    upper: 1.0,
    1006
                    input_sort: SortProperties::Ordered(SortOptions {
    1007
                        descending: false,
    1008
                        nulls_first: false,
    1009
                    }),
    1010
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1011
                        descending: false,
    1012
                        nulls_first: false,
    1013
                    })),
    1014
                },
    1015
                MonotonicityTestCase {
    1016
                    name: "ceil_order out of domain",
    1017
                    func: ceil_order,
    1018
                    lower: -2.0,
    1019
                    upper: 1.0,
    1020
                    input_sort: SortProperties::Ordered(SortOptions {
    1021
                        descending: false,
    1022
                        nulls_first: false,
    1023
                    }),
    1024
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1025
                        descending: false,
    1026
                        nulls_first: false,
    1027
                    })),
    1028
                },
    1029
                MonotonicityTestCase {
    1030
                    name: "cos_order within domain",
    1031
                    func: cos_order,
    1032
                    lower: 0.0,
    1033
                    upper: 2.0 * std::f64::consts::PI,
    1034
                    input_sort: SortProperties::Ordered(SortOptions {
    1035
                        descending: false,
    1036
                        nulls_first: false,
    1037
                    }),
    1038
                    expected: Ok(SortProperties::Unordered),
    1039
                },
    1040
                MonotonicityTestCase {
    1041
                    name: "cos_order out of domain",
    1042
                    func: cos_order,
    1043
                    lower: -2.0,
    1044
                    upper: 1.0,
    1045
                    input_sort: SortProperties::Ordered(SortOptions {
    1046
                        descending: false,
    1047
                        nulls_first: false,
    1048
                    }),
    1049
                    expected: Ok(SortProperties::Unordered),
    1050
                },
    1051
                MonotonicityTestCase {
    1052
                    name: "cosh_order within domain positive",
    1053
                    func: cosh_order,
    1054
                    lower: 5.0,
    1055
                    upper: 100.0,
    1056
                    input_sort: SortProperties::Ordered(SortOptions {
    1057
                        descending: false,
    1058
                        nulls_first: false,
    1059
                    }),
    1060
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1061
                        descending: false,
    1062
                        nulls_first: false,
    1063
                    })),
    1064
                },
    1065
                MonotonicityTestCase {
    1066
                    name: "cosh_order within domain negative",
    1067
                    func: cosh_order,
    1068
                    lower: -100.0,
    1069
                    upper: -5.0,
    1070
                    input_sort: SortProperties::Ordered(SortOptions {
    1071
                        descending: false,
    1072
                        nulls_first: false,
    1073
                    }),
    1074
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1075
                        descending: true,
    1076
                        nulls_first: false,
    1077
                    })),
    1078
                },
    1079
                MonotonicityTestCase {
    1080
                    name: "cosh_order out of domain so unordered",
    1081
                    func: cosh_order,
    1082
                    lower: -1.0,
    1083
                    upper: 1.0,
    1084
                    input_sort: SortProperties::Ordered(SortOptions {
    1085
                        descending: false,
    1086
                        nulls_first: false,
    1087
                    }),
    1088
                    expected: Ok(SortProperties::Unordered),
    1089
                },
    1090
                MonotonicityTestCase {
    1091
                    name: "degrees_order",
    1092
                    func: degrees_order,
    1093
                    lower: -1.0,
    1094
                    upper: 1.0,
    1095
                    input_sort: SortProperties::Ordered(SortOptions {
    1096
                        descending: true,
    1097
                        nulls_first: true,
    1098
                    }),
    1099
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1100
                        descending: true,
    1101
                        nulls_first: true,
    1102
                    })),
    1103
                },
    1104
                MonotonicityTestCase {
    1105
                    name: "exp_order",
    1106
                    func: exp_order,
    1107
                    lower: -1000.0,
    1108
                    upper: 1000.0,
    1109
                    input_sort: SortProperties::Ordered(SortOptions {
    1110
                        descending: false,
    1111
                        nulls_first: false,
    1112
                    }),
    1113
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1114
                        descending: false,
    1115
                        nulls_first: false,
    1116
                    })),
    1117
                },
    1118
                MonotonicityTestCase {
    1119
                    name: "floor_order",
    1120
                    func: floor_order,
    1121
                    lower: -1.0,
    1122
                    upper: 1.0,
    1123
                    input_sort: SortProperties::Ordered(SortOptions {
    1124
                        descending: true,
    1125
                        nulls_first: true,
    1126
                    }),
    1127
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1128
                        descending: true,
    1129
                        nulls_first: true,
    1130
                    })),
    1131
                },
    1132
                MonotonicityTestCase {
    1133
                    name: "ln_order within domain",
    1134
                    func: ln_order,
    1135
                    lower: 1.0,
    1136
                    upper: 2.0,
    1137
                    input_sort: SortProperties::Ordered(SortOptions {
    1138
                        descending: false,
    1139
                        nulls_first: false,
    1140
                    }),
    1141
                    expected: Ok(SortProperties::Ordered(SortOptions {
    1142
                        descending: false,
    1143
                        nulls_first: false,
    1144
                    })),
    1145
                },
    1146
                MonotonicityTestCase {
    1147
                    name: "ln_order out of domain",
    1148
                    func: ln_order,
    1149
                    lower: -5.0,
    1150
                    upper: -4.0,
    1151
                    input_sort: SortProperties::Ordered(SortOptions {
    1152
                        descending: false,
    1153
                        nulls_first: false,
    1154
                    }),
    1155
                    expected: exec_err!("Input range of LN contains out-of-domain values"),
    1156
                },
    1157
            ];
    1158
    1159
            for tcase in test_cases {
    1160
                let input = vec![create_ep(tcase.lower, tcase.upper, tcase.input_sort)];
    1161
                let actual = (tcase.func)(&input);
    1162
                match (&actual, &tcase.expected) {
    1163
                    (Ok(a), Ok(e)) => assert_eq!(
    1164
                        a, e,
    1165
                        "Test '{}' failed: got {:?}, expected {:?}",
    1166
                        tcase.name, a, e
    1167
                    ),
    1168
                    (Err(e1), Err(e2)) => {
    1169
                        assert_eq!(
    1170
                            e1.strip_backtrace().to_string(),
    1171
                            e2.strip_backtrace().to_string(),
    1172
                            "Test '{}' failed: got {:?}, expected {:?}",
    1173
                            tcase.name,
    1174
                            e1,
    1175
                            e2
    1176
                        )
    1177
                    } // Both are errors, so it's fine
    1178
                    _ => panic!(
    1179
                        "Test '{}' failed: got {:?}, expected {:?}",
    1180
                        tcase.name, actual, tcase.expected
    1181
                    ),
    1182
                }
    1183
            }
    1184
        }
    1185
    }