diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java index 47addc84a..76b817ac3 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java @@ -82,9 +82,11 @@ import org.apache.geaflow.dsl.udf.table.date.WeekDay; import org.apache.geaflow.dsl.udf.table.date.WeekOfYear; import org.apache.geaflow.dsl.udf.table.date.Year; +import org.apache.geaflow.dsl.udf.table.math.Cbrt; import org.apache.geaflow.dsl.udf.table.math.E; import org.apache.geaflow.dsl.udf.table.math.Log2; import org.apache.geaflow.dsl.udf.table.math.Round; +import org.apache.geaflow.dsl.udf.table.math.Trunc; import org.apache.geaflow.dsl.udf.table.other.Direction; import org.apache.geaflow.dsl.udf.table.other.EdgeSrcId; import org.apache.geaflow.dsl.udf.table.other.EdgeTargetId; @@ -169,9 +171,11 @@ public class BuildInSqlFunctionTable extends ListSqlOperatorTable { .add(GeaFlowFunction.of(ArrayUnion.class)) // udf.table.math + .add(GeaFlowFunction.of(Cbrt.class)) .add(GeaFlowFunction.of(E.class)) .add(GeaFlowFunction.of(Log2.class)) .add(GeaFlowFunction.of(Round.class)) + .add(GeaFlowFunction.of(Trunc.class)) // udf.table.string .add(GeaFlowFunction.of(Ascii2String.class)) diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java new file mode 100644 index 000000000..42f4bc09a --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java @@ -0,0 +1,48 @@ +/* + * 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.geaflow.dsl.udf.table.math; + +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "cbrt", description = "Returns the cube root of a double value.") +public class Cbrt extends UDF { + + public Double eval(Double n) { + if (n == null) { + return null; + } + return Math.cbrt(n); + } + + public Double eval(Long n) { + if (n == null) { + return null; + } + return Math.cbrt(n.doubleValue()); + } + + public Double eval(Integer n) { + if (n == null) { + return null; + } + return Math.cbrt(n.doubleValue()); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java new file mode 100644 index 000000000..e4783e86e --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java @@ -0,0 +1,65 @@ +/* + * 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.geaflow.dsl.udf.table.math; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "trunc", description = "Truncates x to d decimal places without rounding.") +public class Trunc extends UDF { + + private Double truncate(Double n, int scale) { + if (Double.isNaN(n) || Double.isInfinite(n)) { + return n; + } + return BigDecimal.valueOf(n).setScale(scale, RoundingMode.DOWN).doubleValue(); + } + + public Double eval(Double n) { + if (n == null) { + return null; + } + return truncate(n, 0); + } + + public Long eval(Long n) { + return n; + } + + public Integer eval(Integer n) { + return n; + } + + public Double eval(Double n, Integer scale) { + if (n == null || scale == null) { + return null; + } + return truncate(n, scale); + } + + public Double eval(Double n, Long scale) { + if (n == null || scale == null) { + return null; + } + return truncate(n, scale.intValue()); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/UDFMathTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/UDFMathTest.java new file mode 100644 index 000000000..c6b12a0fc --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/UDFMathTest.java @@ -0,0 +1,91 @@ +/* + * 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.geaflow.dsl.udf.math; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import org.apache.geaflow.dsl.udf.table.math.Cbrt; +import org.apache.geaflow.dsl.udf.table.math.Trunc; +import org.testng.annotations.Test; + +public class UDFMathTest { + + @Test + public void testCbrt() { + Cbrt cbrt = new Cbrt(); + + assertEquals(cbrt.eval(27.0), 3.0, 1e-10); + assertEquals(cbrt.eval(8.0), 2.0, 1e-10); + assertEquals(cbrt.eval(0.0), 0.0, 1e-10); + assertEquals(cbrt.eval(-27.0), -3.0, 1e-10); + assertEquals(cbrt.eval(1.0), 1.0, 1e-10); + + assertEquals(cbrt.eval(27L), 3.0, 1e-10); + assertEquals(cbrt.eval(0L), 0.0, 1e-10); + assertEquals(cbrt.eval(-8L), -2.0, 1e-10); + + assertEquals(cbrt.eval(8), 2.0, 1e-10); + assertEquals(cbrt.eval(0), 0.0, 1e-10); + assertEquals(cbrt.eval(-27), -3.0, 1e-10); + + assertNull(cbrt.eval((Double) null)); + assertNull(cbrt.eval((Long) null)); + assertNull(cbrt.eval((Integer) null)); + } + + @Test + public void testTrunc() { + Trunc trunc = new Trunc(); + + // truncate to integer (no second arg) + assertEquals(trunc.eval(3.9), 3.0, 1e-10); + assertEquals(trunc.eval(-3.9), -3.0, 1e-10); + assertEquals(trunc.eval(0.0), 0.0, 1e-10); + assertEquals(trunc.eval(1.0), 1.0, 1e-10); + + // truncate vs round: 3.567 truncated to 2 decimal places = 3.56, not 3.57 + assertEquals(trunc.eval(3.567, 2), 3.56, 1e-10); + assertEquals(trunc.eval(-3.567, 2), -3.56, 1e-10); + assertEquals(trunc.eval(3.567, 0), 3.0, 1e-10); + assertEquals(trunc.eval(3.567, 1), 3.5, 1e-10); + + // with Long scale + assertEquals(trunc.eval(3.567, 2L), 3.56, 1e-10); + assertEquals(trunc.eval(-3.567, 2L), -3.56, 1e-10); + + // integer and long inputs pass through unchanged + assertEquals((long) trunc.eval(5L), 5L); + assertEquals((int) trunc.eval(5), 5); + assertNull(trunc.eval((Long) null)); + assertNull(trunc.eval((Integer) null)); + + // NaN and Infinity pass through + assertEquals(trunc.eval(Double.NaN), Double.NaN); + assertEquals(trunc.eval(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY); + assertEquals(trunc.eval(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY); + + assertNull(trunc.eval((Double) null)); + assertNull(trunc.eval(3.5, (Integer) null)); + assertNull(trunc.eval(3.5, (Long) null)); + assertNull(trunc.eval(null, 2)); + assertNull(trunc.eval(null, 2L)); + } +}