diff --git a/be/src/vec/functions/math.cpp b/be/src/vec/functions/math.cpp index e37b618525cb26..58db6a442f9a2b 100644 --- a/be/src/vec/functions/math.cpp +++ b/be/src/vec/functions/math.cpp @@ -89,6 +89,24 @@ struct AtanhName { using FunctionAtanh = FunctionMathUnaryAlwayNullable>; +struct CotName { + static constexpr auto name = "cot"; + static constexpr bool is_invalid_input(Float64 x) { + constexpr double epsilon = 1e-10; + double remainder = std::fmod(std::abs(x), M_PI); + + return std::abs(x) < epsilon || std::abs(remainder) < epsilon || + std::abs(remainder - M_PI) < epsilon; + } +}; + +static inline double cot_impl(double x) { + return 1 / std::tan(x); +} + +using FunctionCot = + FunctionMathUnaryAlwayNullable>; + template struct Atan2Impl { using A = typename PrimitiveTypeTraits::ColumnItemType; @@ -540,6 +558,7 @@ void register_function_math(SimpleFunctionFactory& factory) { factory.register_function(); factory.register_function(); factory.register_function(); + factory.register_function(); factory.register_function(); factory.register_alias("ln", "dlog1"); factory.register_function(); diff --git a/be/test/vec/function/function_math_test.cpp b/be/test/vec/function/function_math_test.cpp index 0e7fb5088cda4b..8948b9189bf585 100644 --- a/be/test/vec/function/function_math_test.cpp +++ b/be/test/vec/function/function_math_test.cpp @@ -128,6 +128,18 @@ TEST(MathFunctionTest, cos_test) { static_cast(check_function(func_name, input_types, data_set)); } +TEST(MathFunctionTest, cot_test) { + std::string func_name = "cot"; + + InputTypeSet input_types = {PrimitiveType::TYPE_DOUBLE}; + + DataSet data_set = {{{-1.0}, -0.6420926159343306}, {{0.5}, 1.830487721712452}, + {{1.0}, 0.6420926159343306}, {{0.0}, Null()}, + {{M_PI / 4}, 1.0000000000000002}, {{M_PI / 2}, 6.123233995736766e-17}}; + + static_cast(check_function(func_name, input_types, data_set)); +} + TEST(MathFunctionTest, sin_test) { std::string func_name = "sin"; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java index eb053c99531849..f85a079fc4dcbc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java @@ -132,6 +132,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Cos; import org.apache.doris.nereids.trees.expressions.functions.scalar.Cosh; import org.apache.doris.nereids.trees.expressions.functions.scalar.CosineDistance; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Cot; import org.apache.doris.nereids.trees.expressions.functions.scalar.CountEqual; import org.apache.doris.nereids.trees.expressions.functions.scalar.CountSubstring; import org.apache.doris.nereids.trees.expressions.functions.scalar.Crc32; @@ -621,6 +622,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(Cos.class, "cos"), scalar(Cosh.class, "cosh"), scalar(CosineDistance.class, "cosine_distance"), + scalar(Cot.class, "cot"), scalar(CountEqual.class, "countequal"), scalar(CountSubstring.class, "count_substrings"), scalar(CreateMap.class, "map"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java index 77e9c0007c1fdb..f96ae025effc5c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java @@ -917,6 +917,25 @@ public static Expression cos(DoubleLiteral first) { return checkOutputBoundary(new DoubleLiteral(Math.cos(first.getValue()))); } + /** + * cot + */ + @ExecFunction(name = "cot") + public static Expression cot(DoubleLiteral first) { + if (inputOutOfBound(first, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, false, false)) { + return new NullLiteral(DoubleType.INSTANCE); + } else { + final double epsilon = 1e-10; + double value = first.getValue(); + double remainder = Math.abs(value) % Math.PI; + + if (Math.abs(value) < epsilon || Math.abs(remainder) < epsilon || Math.abs(remainder - Math.PI) < epsilon) { + return new NullLiteral(DoubleType.INSTANCE); + } + return checkOutputBoundary(new DoubleLiteral(1.0 / Math.tan(first.getValue()))); + } + } + /** * tan */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Cot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Cot.java new file mode 100644 index 00000000000000..d4ad8b5a161c45 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Cot.java @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DoubleType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * Cot Scala Function + */ +public class Cot extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DoubleType.INSTANCE).args(DoubleType.INSTANCE) + ); + + /** + * constructor with 1 argument. + */ + public Cot(Expression arg) { + super("cot", arg); + } + + /** + * withChildren. + */ + @Override + public Cot withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new Cot(children.get(0)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitCot(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java index df18f9d7e62f9e..e904b3e18fb08a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java @@ -140,6 +140,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Cos; import org.apache.doris.nereids.trees.expressions.functions.scalar.Cosh; import org.apache.doris.nereids.trees.expressions.functions.scalar.CosineDistance; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Cot; import org.apache.doris.nereids.trees.expressions.functions.scalar.CountEqual; import org.apache.doris.nereids.trees.expressions.functions.scalar.CountSubstring; import org.apache.doris.nereids.trees.expressions.functions.scalar.Crc32; @@ -938,6 +939,10 @@ default R visitConcat(Concat concat, C context) { return visitScalarFunction(concat, context); } + default R visitCot(Cot cot, C context) { + return visitScalarFunction(cot, context); + } + default R visitChar(Char charFunc, C context) { return visitScalarFunction(charFunc, context); } diff --git a/regression-test/data/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.out b/regression-test/data/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.out index ae2203c03d8fb0..49e4d6ffa8ff8a 100644 --- a/regression-test/data/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.out +++ b/regression-test/data/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.out @@ -71,6 +71,24 @@ 0.0 false 8 0.0 false 9 +-- !cot_1 -- +-0.6420926159343306 false + +-- !cot_2 -- +0.6420926159343306 false + +-- !cot_3 -- +\N true 0 +\N true 1 +\N true 2 +\N true 3 +\N true 4 +\N true 5 +\N true 6 +\N true 7 +\N true 8 +\N true 9 + -- !sqrt_1 -- \N true diff --git a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_numeric_arithmatic.groovy b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_numeric_arithmatic.groovy index b533880574c25d..0ee3655bdc4015 100644 --- a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_numeric_arithmatic.groovy +++ b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_numeric_arithmatic.groovy @@ -244,6 +244,17 @@ suite("fold_constant_numeric_arithmatic") { testFoldConst("SELECT COSH(709.782712893384)") // Near overflow boundary testFoldConst("SELECT COSH(-709.782712893384)") // Near negative overflow boundary +//Cot function cases + testFoldConst("SELECT COT(PI()) AS cot_case_1") //cot(π) = null + testFoldConst("SELECT COT(0) AS cot_case_2") //cot(0) = null + testFoldConst("SELECT COT(PI()/2) AS cot_case_3") //cot(π/2) + testFoldConst("SELECT COT(PI()/4)") + testFoldConst("SELECT COT(PI()/6)") + testFoldConst("SELECT COT(-PI())") // Negative PI + testFoldConst("SELECT COT(1E-308)") // Very small number + testFoldConst("SELECT COT(-1E-308)") // Very small negative number + testFoldConst("SELECT COT(NULL)") // NULL handling + //CountEqual function cases testFoldConst("SELECT COUNT(CASE WHEN 5 = 5 THEN 1 END) AS countequal_case_1") //1 (true) testFoldConst("SELECT COUNT(CASE WHEN 5 = 3 THEN 1 END) AS countequal_case_2") //0 (false) diff --git a/regression-test/suites/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.groovy b/regression-test/suites/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.groovy index 64b9ddc01ef795..5f55bb9f678f0b 100644 --- a/regression-test/suites/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.groovy +++ b/regression-test/suites/query_p0/sql_functions/math_functions/test_math_unary_always_nullable.groovy @@ -60,6 +60,16 @@ suite("test_math_unary_alway_nullable") { select atanh(0.0), atanh(0.0) is NULL, number from numbers("number"="10") """ + qt_cot_1 """ + select cot(-1.0), cot(-1.0) is null; + """ + qt_cot_2 """ + select cot(1.0), cot(1.0) is null; + """ + qt_cot_3 """ + select cot(0.0), cot(0.0) is NULL, number from numbers("number"="10") + """ + qt_sqrt_1 """ select sqrt(-1), sqrt(-1) is null; """