Added math functions from API PR#351.

Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
new file mode 100644
index 0000000..965cce7
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     02/01/2022: Tomas Kraus
+//       - #N/A: Test extract() in CriteriaBuilder
+package org.eclipse.persistence.jpa.test.criteria;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.PersistenceException;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Root;
+
+import org.eclipse.persistence.jpa.test.criteria.model.NumberEntity;
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.eclipse.persistence.sessions.Session;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test sign() method in CriteriaBuilder.
+ * Added to JPA-API as PR #351
+ */
+@RunWith(EmfRunner.class)
+public class TestMathFunctions {
+
+    @Emf(createTables = DDLGen.DROP_CREATE,
+            classes = {
+                    NumberEntity.class
+            },
+            properties = {
+                    @Property(name = "eclipselink.cache.shared.default", value = "false"),
+                    // This property remaps String from VARCHAR->NVARCHAR(or db equivalent)
+                    @Property(name = "eclipselink.target-database-properties",
+                            value = "UseNationalCharacterVaryingTypeForString=true"),
+            })
+    private EntityManagerFactory emf;
+
+    private final NumberEntity[] NUMBER = {
+            new NumberEntity(0, 0L, 0D),
+            new NumberEntity(1, 1L, 1D),
+            new NumberEntity(2, -1L, -1D),
+            new NumberEntity(3, 42L, 42.42D),
+            new NumberEntity(4, -342L, -342.42D),
+            new NumberEntity(5, 4L, 4D),
+            new NumberEntity(6, -4L, -4D),
+            new NumberEntity(7, 4L, 14.23D),
+            new NumberEntity(8, 6L, 44.7542383252D),
+            new NumberEntity(9, 8L, -214.2457321233D)
+    };
+
+    @Before
+    public void setup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            for (NumberEntity e : NUMBER) {
+                em.persist(e);
+            }
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    @After
+    public void cleanup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            em.createQuery("DELETE FROM NumberEntity e").executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Verify result rounded to 10 decimal places as String operation.
+    // Precision of Java and relational DB floating point operation may not be the same.
+    private static void verifyRoundedDouble(final double expected, final double returned) {
+        // Round values down to 10 decimal places
+        DecimalFormat df = new DecimalFormat("#.##########");
+        df.setRoundingMode(RoundingMode.DOWN);
+        String expectedRounded = df.format(expected);
+        String returnedRounded = df.format(returned);
+        Assert.assertEquals(expectedRounded, returnedRounded);
+    }
+
+    // Call SELECT SIGN(n.longValue) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    private static Integer callSign(final EntityManager em, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Integer> cq = cb.createQuery(Integer.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.sign(number.get("longValue")));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call SIGN(n) on n=0.
+    @Test
+    public void testSignMethodWithZero() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Integer result = callSign(em, 0);
+            Assert.assertEquals(Integer.valueOf(0), result);
+        }
+    }
+
+    // Call SIGN(n) on n>0.
+    @Test
+    public void testSignMethodWithPositive() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Integer result = callSign(em, 3);
+            Assert.assertEquals(Integer.valueOf(1), result);
+        }
+    }
+
+    // Call SIGN(n) on n<0.
+    @Test
+    public void testSignMethodWithNegative() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Integer result = callSign(em, 4);
+            Assert.assertEquals(Integer.valueOf(-1), result);
+        }
+    }
+
+    // Call SELECT CEILING(n.doubleValue) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    private static Double callCeiling(final EntityManager em, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.ceiling(number.get("doubleValue")));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call CEILING(n) on n=0.
+    @Test
+    public void testCeilingMethodWithZero() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callCeiling(em, 0);
+            Assert.assertEquals(Double.valueOf(0), result);
+        }
+    }
+
+    // Call CEILING(n) on n>0.
+    @Test
+    public void testCeilingMethodWithPositive() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callCeiling(em, 3);
+            Assert.assertEquals(
+                    Double.valueOf(NUMBER[3].getLongValue().intValue()+1), result);
+        }
+    }
+
+    // Call CEILING(n) on n<0.
+    @Test
+    public void testCeilingMethodWithNegative() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callCeiling(em, 4);
+            Assert.assertEquals(
+                    Double.valueOf(NUMBER[4].getLongValue().intValue()), result);
+        }
+    }
+
+    // Call SELECT FLOOR(n.doubleValue) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    private static Double callFloor(final EntityManager em, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.floor(number.get("doubleValue")));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call FLOOR(n) on n=0.
+    @Test
+    public void testFloorMethodWithZero() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callFloor(em, 0);
+            Assert.assertEquals(Double.valueOf(0), result);
+        }
+    }
+
+    // Call FLOOR(n) on n>0.
+    @Test
+    public void testFloorMethodWithPositive() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callFloor(em, 3);
+            Assert.assertEquals(
+                    Double.valueOf(NUMBER[3].getLongValue().intValue()), result);
+        }
+    }
+
+    // Call FLOOR(n) on n<0.
+    @Test
+    public void testFloorMethodWithNegative() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callFloor(em, 4);
+            Assert.assertEquals(
+                    Double.valueOf(NUMBER[4].getLongValue().intValue()-1), result);
+        }
+    }
+
+    // Call SELECT EXP(n.doubleValue) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    private static Double callExp(final EntityManager em, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.exp(number.get("doubleValue")));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call EXP(0).
+    @Test
+    public void testExpMethodWithZero() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callExp(em, 0);
+            Assert.assertEquals(
+                    Double.valueOf(Math.exp(NUMBER[0].getDoubleValue())), result);
+        }
+    }
+
+    // Call EXP(1). Result is rounded down to 10 decimal places.
+    @Test
+    public void testExpMethodWithPlusOne() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            verifyRoundedDouble(Math.exp(NUMBER[1].getDoubleValue()), callExp(em, 1));
+        }
+    }
+
+    // Call EXP(-1). Result is rounded down to 10 decimal places.
+    @Test
+    public void testExpMethodWithMinusOne() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            verifyRoundedDouble(Math.exp(NUMBER[2].getDoubleValue()), callExp(em, 2));
+        }
+    }
+
+    // Call EXP(n) on n>0. Result is rounded down to 10 decimal places.
+    @Test
+    public void testExpMethodWithPositive() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            verifyRoundedDouble(Math.exp(NUMBER[5].getDoubleValue()), callExp(em, 5));
+        }
+    }
+
+    // Call EXP(n) on n<0. Result is rounded down to 10 decimal places.
+    @Test
+    public void testExpMethodWithNegative() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            verifyRoundedDouble(Math.exp(NUMBER[6].getDoubleValue()), callExp(em, 6));
+        }
+    }
+
+    // Call SELECT LN(n.doubleValue) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    private static Double callLn(final EntityManager em, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.ln(number.get("doubleValue")));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call LN(0). Domain of f(x): y = ln(x) is (0,infinity) so it shall throw an exception or return null.
+    @Test
+    public void testLnMethodWithZero() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callLn(em, 0);
+            Assert.assertNull(result);
+        } catch (PersistenceException pe) {
+            // Expected to be thrown
+        }
+    }
+
+    // Call LN(1). Result is rounded down to 10 decimal places.
+    @Test
+    public void testLnMethodWithPlusOne() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            verifyRoundedDouble(Math.log(NUMBER[1].getDoubleValue()), callLn(em, 1));
+        }
+    }
+
+    // Call LN(-1). Domain of f(x): y = ln(x) is (0,infinity) so it shall throw an exception or return null.
+    @Test
+    public void testLnMethodWithMinusOne() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callLn(em, 2);
+            Assert.assertNull(result);
+        } catch (PersistenceException pe) {
+            // Expected to be thrown
+        }
+    }
+
+    // Call LN(n) on n>0. Result is rounded down to 10 decimal places.
+    @Test
+    public void testLnMethodWithPositive() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            verifyRoundedDouble(Math.log(NUMBER[5].getDoubleValue()), callLn(em, 5));
+        }
+    }
+
+    // Call LN(n) on n<0. Domain of f(x): y = ln(x) is (0,infinity) so it shall throw an exception or return null.
+    @Test
+    public void testLnMethodWithNegative() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callLn(em, 6);
+            Assert.assertNull(result);
+        } catch (PersistenceException pe) {
+            // Expected to be thrown
+        }
+    }
+
+    // Call SELECT POWER(n.:field, exponent) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    // Matches Expression<Double> power(Expression<? extends Number> x, Number y) prototype
+    private static Double callPower(final EntityManager em, final int exponent, final int id, final String field) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.power(number.get(field), exponent));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call POWER(n.longValue, 2) on long n>0.
+    @Test
+    public void testPower2MethodWithZeroBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 2, 0, "longValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[0].getLongValue(), 2)), result);
+        }
+    }
+
+    // Call POWER(n.longValue, 2) on long n>0.
+    @Test
+    public void testPower2MethodWithPositiveLongBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 2, 3, "longValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[3].getLongValue(), 2)), result);
+        }
+    }
+
+    // Call POWER(n.doubleValue, 2) on double n>0.
+    @Test
+    public void testPower2MethodWithPositiveDoubleBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 2, 3, "doubleValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[3].getDoubleValue(), 2)), result);
+        }
+    }
+
+    // Call POWER(n.longValue, 2) on long n<0.
+    @Test
+    public void testPower2MethodWithNegativeLongBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 2, 4, "longValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[4].getLongValue(), 2)), result);
+        }
+    }
+
+    // Call POWER(n.doubleValue, 2) on double n<0.
+    @Test
+    public void testPower2MethodWithNegativeDoubleBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 2, 4, "doubleValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[4].getDoubleValue(), 2)), result);
+        }
+    }
+
+    // Call POWER(n.longValue, 3) on long n<0.
+    @Test
+    public void testPower3MethodWithNegativeLongBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 3, 4, "longValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[4].getLongValue(), 3)), result);
+        }
+    }
+
+    // Call POWER(n.doubleValue, 3) on double n<0.
+    @Test
+    public void testPower3MethodWithNegativeDoubleBase() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callPower(em, 3, 4, "doubleValue");
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[4].getDoubleValue(), 3)), result);
+        }
+    }
+
+    // Call SELECT POWER(n.doubleValue, n.longValue) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    // Matches Expression<Double> power(Expression<? extends Number> x, Expression<? extends Number> y) prototype
+    private static Double callExprPower(final EntityManager em, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.power(number.get("doubleValue"), number.get("longValue")));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call POWER(n.doubleValue, n.longValue) on id=7: [14.23D,4L] (result fits in double with no exponent).
+    @Test
+    public void testPowerMethodWithPositiveArgs() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callExprPower(em, 7);
+            Assert.assertEquals(
+                    Double.valueOf(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue())), result);
+        }
+    }
+
+    // Call SELECT ROUND(n.doubleValue, d) FROM NumberEntity n WHERE n.id = id
+    // using CriteriaQuery
+    // Matches Expression<Double> power(Expression<? extends Number> x, Number y) prototype
+    private static Double callRound(final EntityManager em, final int d, final int id) {
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+        Root<NumberEntity> number = cq.from(NumberEntity.class);
+        cq.select(cb.round(number.get("doubleValue"), d));
+        cq.where(cb.equal(number.get("id"), id));
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    // Call ROUND(n) on n>0.
+    @Test
+    public void testRoundMethodWithPositive() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isPostgreSQL());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callRound(em, 6,8);
+            Assert.assertEquals(Double.valueOf(44.754238D), result);
+        }
+    }
+
+    // Call ROUND(n) on n<0.
+    @Test
+    public void testRoundMethodWithNegative() {
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
+        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isPostgreSQL());
+        try (final EntityManager em = emf.createEntityManager()) {
+            Double result = callRound(em, 6, 9);
+            Assert.assertEquals(Double.valueOf(-214.245732D), result);
+        }
+    }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
new file mode 100644
index 0000000..61a6c46
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     02/01/2022: Tomas Kraus
+//       - #N/A: Test extract() in CriteriaBuilder
+package org.eclipse.persistence.jpa.test.criteria.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class NumberEntity {
+
+    @Id
+    private Integer id;
+
+    private Long longValue;
+
+    private Double doubleValue;
+
+    public NumberEntity() {
+
+    }
+
+    public NumberEntity(final Integer id, final Long longValue, final Double doubleValue) {
+        this.setId(id);
+        this.setLongValue(longValue);
+        this.setDoubleValue(doubleValue);
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(final Integer id) {
+        this.id = id;
+    }
+
+    public Long getLongValue() {
+        return longValue;
+    }
+
+    public void setLongValue(final Long longValue) {
+        this.longValue = longValue;
+    }
+
+    public Double getDoubleValue() {
+        return doubleValue;
+    }
+
+    public void setDoubleValue(final Double doubleValue) {
+        this.doubleValue = doubleValue;
+    }
+}
diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
index 7a1decb..42049e1 100644
--- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
+++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2020, 2021 IBM Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -19,6 +19,9 @@
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -1321,7 +1324,129 @@
         return new FunctionExpressionImpl(this.metamodel, ClassConstants.DOUBLE, ExpressionMath.sqrt(((InternalSelection)x).getCurrentNode()), buildList(x), "sqrt");
     }
 
-    // typecasts:
+    /**
+     * Create an expression that returns the sign of its argument, that is, {@code 1} if its argument is
+     * positive, {@code -1} if its argument is negative, or {@code 0} if its argument is exactly zero.
+     *
+     * @param x expression
+     * @return sign
+     */
+    @Override
+    public Expression<Integer> sign(Expression<? extends Number> x) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.INTEGER,
+                ExpressionMath.sign(((InternalSelection)x).getCurrentNode()), buildList(x), "sign");
+    }
+
+    /**
+     * Create an expression that returns the ceiling of its argument, that is, the smallest integer greater than
+     * or equal to its argument.
+     *
+     * @param x expression
+     * @return ceiling
+     */
+    @Override
+    public <N extends Number> Expression<N> ceiling(Expression<N> x) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.NUMBER,
+                ExpressionMath.ceil(((InternalSelection)x).getCurrentNode()), buildList(x), "ceiling");
+    }
+
+    /**
+     * Create an expression that returns the floor of its argument, that is, the largest integer smaller than
+     * or equal to its argument.
+     *
+     * @param x expression
+     * @return floor
+     */
+    @Override
+    public <N extends Number> Expression<N> floor(Expression<N> x) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.NUMBER,
+                ExpressionMath.floor(((InternalSelection)x).getCurrentNode()), buildList(x), "floor");
+    }
+
+    /**
+     * Create an expression that returns the exponential of its argument, that is, Euler's number <i>e</i>
+     * raised to the power of its argument.
+     *
+     * @param x expression
+     * @return exponential
+     */
+    @Override
+    public Expression<Double> exp(Expression<? extends Number> x) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.DOUBLE,
+                ExpressionMath.exp(((InternalSelection)x).getCurrentNode()), buildList(x), "exp");
+    }
+    /**
+     * Create an expression that returns the natural logarithm of its argument.
+     *
+     * @param x expression
+     * @return natural logarithm
+     */
+    @Override
+    public Expression<Double> ln(Expression<? extends Number> x) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.DOUBLE,
+                ExpressionMath.ln(((InternalSelection)x).getCurrentNode()), buildList(x), "ln");
+    }
+
+    /**
+     * Create an expression that returns the first argument raised to the power of its second argument.
+     *
+     * @param x base
+     * @param y exponent
+     * @return the base raised to the power of the exponent
+     */
+    @Override
+    public Expression<Double> power(Expression<? extends Number> x, Expression<? extends Number> y) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.DOUBLE,
+                ExpressionMath.power(((InternalSelection)x).getCurrentNode(),
+                ((InternalSelection)y).getCurrentNode()), buildList(x,y), "power");
+    }
+
+    /**
+     * Create an expression that returns the first argument raised to the power of its second argument.
+     *
+     * @param x base
+     * @param y exponent
+     * @return the base raised to the power of the exponent
+     */
+    @Override
+    public Expression<Double> power(Expression<? extends Number> x, Number y) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.DOUBLE,
+                ExpressionMath.power(((InternalSelection)x).getCurrentNode(), y),
+                buildList(x, internalLiteral(y)), "power");
+    }
+
+    /**
+     * Create an expression that returns the first argument rounded to the number of decimal places given by the
+     * second argument.
+     *
+     * @param x base
+     * @param n number of decimal places
+     * @return the rounded value
+     */
+    @Override
+    public <T extends Number> Expression<T> round(Expression<T> x, Integer n) {
+        return new FunctionExpressionImpl(this.metamodel, ClassConstants.NUMBER,
+                ExpressionMath.round(((InternalSelection)x).getCurrentNode(), n),
+                buildList(x, internalLiteral(n)), "round");
+    }
+
+    // This came with API modifications branch and shall be removed.
+    @Override
+    public Expression<LocalDate> localDate() {
+        return null;
+    }
+
+    @Override
+    public Expression<LocalDateTime> localDateTime() {
+        return null;
+    }
+
+    @Override
+    public Expression<LocalTime> localTime() {
+        return null;
+    }
+
+// typecasts:
     /**
      * Typecast.
      *