You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2022/11/22 17:26:52 UTC

[cassandra] branch trunk updated: Add Mathematical Functions

This is an automated email from the ASF dual-hosted git repository.

blerer pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 88dc64d208 Add Mathematical Functions
88dc64d208 is described below

commit 88dc64d2086c9a91f00ee024b8ef13cb2c193ee6
Author: XV4DE <sc...@kehillahstudent.org>
AuthorDate: Sat May 14 12:28:40 2022 -0700

    Add Mathematical Functions
    
    Patch by Simon Chess; review by Benjamin Lerer Ekaterina Dimitrova for CASSANDRA-17221
    
    This patch add the abs, exp, log, log10, and round functions for the numeric types.
---
 .build/cassandra-deps-template.xml                 |   4 +
 .build/parent-pom-template.xml                     |   5 +
 CHANGES.txt                                        |   1 +
 NEWS.txt                                           |   1 +
 doc/modules/cassandra/pages/cql/functions.adoc     |  20 ++
 .../apache/cassandra/cql3/functions/MathFcts.java  | 134 +++++++++
 .../cassandra/cql3/functions/NativeFunctions.java  |   1 +
 .../org/apache/cassandra/db/marshal/ByteType.java  |  30 ++
 .../cassandra/db/marshal/CounterColumnType.java    |  30 ++
 .../apache/cassandra/db/marshal/DecimalType.java   |  59 ++++
 .../apache/cassandra/db/marshal/DoubleType.java    |  30 ++
 .../org/apache/cassandra/db/marshal/FloatType.java |  30 ++
 .../org/apache/cassandra/db/marshal/Int32Type.java |  30 ++
 .../apache/cassandra/db/marshal/IntegerType.java   |  44 +++
 .../org/apache/cassandra/db/marshal/LongType.java  |  30 ++
 .../apache/cassandra/db/marshal/NumberType.java    |  49 +++
 .../org/apache/cassandra/db/marshal/ShortType.java |  30 ++
 .../cassandra/cql3/functions/MathFctsTest.java     | 331 +++++++++++++++++++++
 18 files changed, 859 insertions(+)

diff --git a/.build/cassandra-deps-template.xml b/.build/cassandra-deps-template.xml
index 96c6c5379b..44f9f35ab3 100644
--- a/.build/cassandra-deps-template.xml
+++ b/.build/cassandra-deps-template.xml
@@ -340,6 +340,10 @@
       <groupId>com.github.seancfoley</groupId>
       <artifactId>ipaddress</artifactId>
     </dependency>
+    <dependency>
+      <groupId>ch.obermuhlner</groupId>
+      <artifactId>big-math</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.agrona</groupId>
       <artifactId>agrona</artifactId>
diff --git a/.build/parent-pom-template.xml b/.build/parent-pom-template.xml
index a76c46bc72..752645599d 100644
--- a/.build/parent-pom-template.xml
+++ b/.build/parent-pom-template.xml
@@ -1029,6 +1029,11 @@
         <artifactId>agrona</artifactId>
         <version>1.17.1</version>
       </dependency>
+      <dependency>
+        <groupId>ch.obermuhlner</groupId>
+        <artifactId>big-math</artifactId>
+        <version>2.3.0</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 </project>
diff --git a/CHANGES.txt b/CHANGES.txt
index 39363ac4c4..d64521fdea 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.2
+ * Add Mathematical functions (CASSANDRA-17221)
  * Make incremental backup configurable per table (CASSANDRA-15402)
  * Change shebangs of Python scripts to resolve Python 3 from env command (CASSANDRA-17832)
  * Add reasons to guardrail messages and consider guardrails in the error message for needed ALLOW FILTERING (CASSANDRA-17967)
diff --git a/NEWS.txt b/NEWS.txt
index bc94c640c0..2238c43f36 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -57,6 +57,7 @@ using the provided 'sstableupgrade' tool.
 
 New features
 ------------
+    - Added new Mathematical CQL functions: abs, exp, log, log10 and round.
     - Adds a trie-based memtable implementation, which improves memory use, garbage collection efficiency and lookup
       performance. The new memtable is implemented by the TrieMemtable class and can be selected using the memtable
       API, see src/java/org/apache/cassandra/db/memtable/Memtable_API.md.
diff --git a/doc/modules/cassandra/pages/cql/functions.adoc b/doc/modules/cassandra/pages/cql/functions.adoc
index 93439a322e..39fc9860a4 100644
--- a/doc/modules/cassandra/pages/cql/functions.adoc
+++ b/doc/modules/cassandra/pages/cql/functions.adoc
@@ -238,6 +238,26 @@ For every xref:cql/types.adoc#native-types[type] supported by CQL, the function
 Conversely, the function `blobAsType` takes a 64-bit `blob` argument and converts it to a `bigint` value. 
 For example, `bigintAsBlob(3)` returns `0x0000000000000003` and `blobAsBigint(0x0000000000000003)` returns `3`.
 
+==== Math Functions
+
+Cql provides the following math functions: `abs`, `exp`, `log`, `log10`, and `round`.
+The return type for these functions is always the same as the input type.
+
+[cols=",",options="header",]
+|===
+|Function name |Description
+
+|`abs` | Returns the absolute value of the input.
+
+|`exp` | Returns the number e to the power of the input.
+
+|`log` | Returns the natural log of the input.
+
+|`log10` | Returns the log base 10 of the input.
+
+|`round` | Rounds the input to the nearest whole number using rounding mode `HALF_UP`.
+|===
+
 [[user-defined-scalar-functions]]
 ==== User-defined functions
 
diff --git a/src/java/org/apache/cassandra/cql3/functions/MathFcts.java b/src/java/org/apache/cassandra/cql3/functions/MathFcts.java
new file mode 100644
index 0000000000..553ac40fe0
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/functions/MathFcts.java
@@ -0,0 +1,134 @@
+/*
+ * 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.cassandra.cql3.functions;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.transport.ProtocolVersion;
+
+public final class MathFcts
+{
+    public static void addFunctionsTo(NativeFunctions functions)
+    {
+        final List<NumberType<?>> numericTypes = ImmutableList.of(ByteType.instance,
+                                                                  ShortType.instance,
+                                                                  Int32Type.instance,
+                                                                  FloatType.instance,
+                                                                  LongType.instance,
+                                                                  DoubleType.instance,
+                                                                  IntegerType.instance,
+                                                                  DecimalType.instance,
+                                                                  CounterColumnType.instance);
+
+        numericTypes.stream()
+                    .map(t -> ImmutableList.of(absFct(t),
+                                               expFct(t),
+                                               logFct(t),
+                                               log10Fct(t),
+                                               roundFct(t)))
+                     .flatMap(Collection::stream)
+                     .forEach(f -> functions.add(f));
+    }
+
+    public static NativeFunction absFct(final NumberType<?> type)
+    {
+        return new NativeScalarFunction("abs", type, type)
+        {
+            @Override
+            public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
+            {
+                ByteBuffer bb = parameters.get(0);
+                if (bb == null)
+                    return null;
+                return type.abs(bb);
+            }
+        };
+    }
+
+    public static NativeFunction expFct(final NumberType<?> type)
+    {
+        return new NativeScalarFunction("exp", type, type)
+        {
+            @Override
+            public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
+            {
+                ByteBuffer bb = parameters.get(0);
+                if (bb == null)
+                    return null;
+                return type.exp(bb);
+            }
+        };
+    }
+
+    public static NativeFunction logFct(final NumberType<?> type)
+    {
+        return new NativeScalarFunction("log", type, type)
+        {
+            @Override
+            public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
+            {
+                ByteBuffer bb = parameters.get(0);
+                if (bb == null)
+                    return null;
+                return type.log(bb);
+
+            }
+        };
+    }
+
+    public static NativeFunction log10Fct(final NumberType<?> type)
+    {
+        return new NativeScalarFunction("log10", type, type)
+        {
+            @Override
+            public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
+            {
+                ByteBuffer bb = parameters.get(0);
+                if (bb == null)
+                    return null;
+                return type.log10(bb);
+
+            }
+        };
+    }
+
+    public static NativeFunction roundFct(final NumberType<?> type)
+    {
+        return new NativeScalarFunction("round", type, type)
+        {
+            @Override
+            public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
+            {
+                ByteBuffer bb = parameters.get(0);
+                if (bb == null)
+                    return null;
+                return type.round(bb);
+
+            }
+        };
+    }
+
+    private MathFcts()
+    {
+    }
+}
diff --git a/src/java/org/apache/cassandra/cql3/functions/NativeFunctions.java b/src/java/org/apache/cassandra/cql3/functions/NativeFunctions.java
index f89bba870d..6ea5e81ed4 100644
--- a/src/java/org/apache/cassandra/cql3/functions/NativeFunctions.java
+++ b/src/java/org/apache/cassandra/cql3/functions/NativeFunctions.java
@@ -41,6 +41,7 @@ public class NativeFunctions
             OperationFcts.addFunctionsTo(this);
             AggregateFcts.addFunctionsTo(this);
             BytesConversionFcts.addFunctionsTo(this);
+            MathFcts.addFunctionsTo(this);
         }
     };
 
diff --git a/src/java/org/apache/cassandra/db/marshal/ByteType.java b/src/java/org/apache/cassandra/db/marshal/ByteType.java
index a910fbba11..efb4c619b0 100644
--- a/src/java/org/apache/cassandra/db/marshal/ByteType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ByteType.java
@@ -153,4 +153,34 @@ public class ByteType extends NumberType<Byte>
     {
         return ByteBufferUtil.bytes((byte) -toByte(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((byte) Math.abs(toByte(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((byte) Math.exp(toByte(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((byte) Math.log(toByte(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((byte) Math.log10(toByte(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.clone(input);
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
index 0dae0927f4..aed80c93dc 100644
--- a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
+++ b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
@@ -128,4 +128,34 @@ public class CounterColumnType extends NumberType<Long>
     {
         return ByteBufferUtil.bytes(-toLong(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.abs(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((long) Math.exp(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((long) Math.log(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((long) Math.log10(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.clone(input);
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java
index 3e02dc9696..ba91b4799a 100644
--- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java
@@ -37,6 +37,8 @@ import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.bytecomparable.ByteComparable;
 import org.apache.cassandra.utils.bytecomparable.ByteSource;
 
+import ch.obermuhlner.math.big.BigDecimalMath;
+
 public class DecimalType extends NumberType<BigDecimal>
 {
     public static final DecimalType instance = new DecimalType();
@@ -390,4 +392,61 @@ public class DecimalType extends NumberType<BigDecimal>
     {
         return decompose(toBigDecimal(input).negate());
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return decompose(toBigDecimal(input).abs());
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return decompose(exp(toBigDecimal(input)));
+    }
+
+    protected BigDecimal exp(BigDecimal input)
+    {
+        int precision = input.precision();
+        precision = Math.max(MIN_SIGNIFICANT_DIGITS, precision);
+        precision = Math.min(MAX_PRECISION.getPrecision(), precision);
+        return BigDecimalMath.exp(input, new MathContext(precision, RoundingMode.HALF_EVEN));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return decompose(log(toBigDecimal(input)));
+    }
+
+    protected BigDecimal log(BigDecimal input)
+    {
+        if (input.compareTo(BigDecimal.ZERO) <= 0) throw new ArithmeticException("Natural log of number zero or less");
+        int precision = input.precision();
+        precision = Math.max(MIN_SIGNIFICANT_DIGITS, precision);
+        precision = Math.min(MAX_PRECISION.getPrecision(), precision);
+        return BigDecimalMath.log(input, new MathContext(precision, RoundingMode.HALF_EVEN));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return decompose(log10(toBigDecimal(input)));
+    }
+
+    protected BigDecimal log10(BigDecimal input)
+    {
+        if (input.compareTo(BigDecimal.ZERO) <= 0) throw new ArithmeticException("Log10 of number zero or less");
+        int precision = input.precision();
+        precision = Math.max(MIN_SIGNIFICANT_DIGITS, precision);
+        precision = Math.min(MAX_PRECISION.getPrecision(), precision);
+        return BigDecimalMath.log10(input, new MathContext(precision, RoundingMode.HALF_EVEN));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return DecimalType.instance.decompose(
+        toBigDecimal(input).setScale(0, RoundingMode.HALF_UP));
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java
index 56ae0131b3..c74e2e16fc 100644
--- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java
@@ -179,4 +179,34 @@ public class DoubleType extends NumberType<Double>
     {
         return ByteBufferUtil.bytes(-toDouble(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.abs(toDouble(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.exp(toDouble(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.log(toDouble(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.log10(toDouble(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((double) Math.round(toDouble(input)));
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java
index 2adb127d41..bd42cb50bf 100644
--- a/src/java/org/apache/cassandra/db/marshal/FloatType.java
+++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java
@@ -174,4 +174,34 @@ public class FloatType extends NumberType<Float>
     {
         return ByteBufferUtil.bytes(-toFloat(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.abs(toFloat(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((float) Math.exp(toFloat(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((float) Math.log(toFloat(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((float) Math.log10(toFloat(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((float) Math.round(toFloat(input)));
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java
index 6dee26e224..a0db33108f 100644
--- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java
+++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java
@@ -174,4 +174,34 @@ public class Int32Type extends NumberType<Integer>
     {
         return ByteBufferUtil.bytes(-toInt(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.abs(toInt(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((int) Math.exp(toInt(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((int) Math.log(toInt(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((int) Math.log10(toInt(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.clone(input);
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
index b52bda8900..b52f920961 100644
--- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java
+++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
@@ -559,4 +559,48 @@ public final class IntegerType extends NumberType<BigInteger>
     {
         return decompose(toBigInteger(input).negate());
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return decompose(toBigInteger(input).abs());
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        BigInteger bi = toBigInteger(input);
+        BigDecimal bd = new BigDecimal(bi);
+        BigDecimal result = DecimalType.instance.exp(bd);
+        BigInteger out = result.toBigInteger();
+        return IntegerType.instance.decompose(out);
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        BigInteger bi = toBigInteger(input);
+        if (bi.compareTo(BigInteger.ZERO) <= 0) throw new ArithmeticException("Natural log of number zero or less");
+        BigDecimal bd = new BigDecimal(bi);
+        BigDecimal result = DecimalType.instance.log(bd);
+        BigInteger out = result.toBigInteger();
+        return IntegerType.instance.decompose(out);
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        BigInteger bi = toBigInteger(input);
+        if (bi.compareTo(BigInteger.ZERO) <= 0) throw new ArithmeticException("Log10 of number zero or less");
+        BigDecimal bd = new BigDecimal(bi);
+        BigDecimal result = DecimalType.instance.log10(bd);
+        BigInteger out = result.toBigInteger();
+        return IntegerType.instance.decompose(out);
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.clone(input);
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java
index 6bf5e9e669..9c0a73eb7b 100644
--- a/src/java/org/apache/cassandra/db/marshal/LongType.java
+++ b/src/java/org/apache/cassandra/db/marshal/LongType.java
@@ -198,4 +198,34 @@ public class LongType extends NumberType<Long>
     {
         return ByteBufferUtil.bytes(-toLong(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes(Math.abs(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((long) Math.exp(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((long) Math.log(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((long) Math.log10(toLong(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.clone(input);
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/NumberType.java b/src/java/org/apache/cassandra/db/marshal/NumberType.java
index 9e7697fb2f..5b10720cc0 100644
--- a/src/java/org/apache/cassandra/db/marshal/NumberType.java
+++ b/src/java/org/apache/cassandra/db/marshal/NumberType.java
@@ -220,4 +220,53 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
      * @return the negated argument
      */
     public abstract ByteBuffer negate(ByteBuffer input);
+
+    /**
+     * Takes the absolute value of the argument.
+     *
+     * @param input the argument to take the absolute value of
+     * @return a ByteBuffer containing the absolute value of the argument. The type of the contents of the ByteBuffer
+     * will match that of the input.
+     */
+    public abstract ByteBuffer abs(ByteBuffer input);
+
+    /**
+     * Raises e to the power of the argument.
+     *
+     * @param input the argument to raise e to
+     * @return a ByteBuffer containg the value of e (the base of natural logarithms) raised to the power of the
+     * argument. The type of the contents of the ByteBuffer will be a double, unless the input is a DecimalType or
+     * IntegerType, in which the ByteBuffer will contain a DecimalType.
+     */
+    public abstract ByteBuffer exp(ByteBuffer input);
+
+    /**
+     * Takes the natural logrithm of the argument.
+     *
+     * @param input the argument to take the natural log (ln) of
+     * @return a ByteBuffer containg the log base e (ln) of the argument. The type of the contents of the ByteBuffer
+     * will be a double, unless the input is a DecimalType or IntegerType, in which the ByteBuffer will contain a
+     * DecimalType.
+     */
+    public abstract ByteBuffer log(ByteBuffer input);
+
+    /**
+     * Takes the log base 10 of the arguement.
+     *
+     * @param input the argument to take the log base ten of
+     * @return a ByteBuffer containg the log base 10 of the argument. The type of the contents of the ByteBuffer
+     * will be a double, unless the input is a DecimalType or IntegerType, in which the ByteBuffer will contain a
+     * DecimalType.
+     */
+    public abstract ByteBuffer log10(ByteBuffer input);
+
+    /**
+     * Rounds the argument to the nearest whole number.
+     *
+     * @param input the argument to round
+     * @return a ByteBuffer containg the rounded argument. If the input is an integral type, the ByteBuffer will contain
+     * a copy of the input. If the input is a float, the ByteBuffer will contain an int32. If the input is a double, the
+     * output will contain a long. If the input is a DecimalType, the output will contain an IntegerType.
+     */
+    public abstract ByteBuffer round(ByteBuffer input);
 }
diff --git a/src/java/org/apache/cassandra/db/marshal/ShortType.java b/src/java/org/apache/cassandra/db/marshal/ShortType.java
index 013fa95949..a1c0dc88e4 100644
--- a/src/java/org/apache/cassandra/db/marshal/ShortType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ShortType.java
@@ -150,4 +150,34 @@ public class ShortType extends NumberType<Short>
     {
         return ByteBufferUtil.bytes((short) -toShort(input));
     }
+
+    @Override
+    public ByteBuffer abs(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((short) Math.abs(toShort(input)));
+    }
+
+    @Override
+    public ByteBuffer exp(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((short) Math.exp(toShort(input)));
+    }
+
+    @Override
+    public ByteBuffer log(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((short) Math.log(toShort(input)));
+    }
+
+    @Override
+    public ByteBuffer log10(ByteBuffer input)
+    {
+        return ByteBufferUtil.bytes((short) Math.log10(toShort(input)));
+    }
+
+    @Override
+    public ByteBuffer round(ByteBuffer input)
+    {
+        return ByteBufferUtil.clone(input);
+    }
 }
diff --git a/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java
new file mode 100644
index 0000000000..daefc0aedf
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java
@@ -0,0 +1,331 @@
+/*
+ * 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.cassandra.cql3.functions;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.NumberType;
+import org.apache.cassandra.db.marshal.ShortType;
+import org.apache.cassandra.transport.ProtocolVersion;
+
+import static org.junit.Assert.assertEquals;
+
+public class MathFctsTest
+{
+    @Test
+    public void testAbs()
+    {
+        assertAbsEquals(1, Int32Type.instance, 1);
+        assertAbsEquals(0, Int32Type.instance, 0);
+        assertAbsEquals(3, Int32Type.instance, -3);
+
+        assertAbsEquals((byte) 1, ByteType.instance, (byte) 1);
+        assertAbsEquals((byte) 0, ByteType.instance, (byte) 0);
+        assertAbsEquals((byte) 3, ByteType.instance, (byte) -3);
+
+        assertAbsEquals((short) 1, ShortType.instance, (short) 1);
+        assertAbsEquals((short) 0, ShortType.instance, (short) 0);
+        assertAbsEquals((short) 3, ShortType.instance, (short) -3);
+
+        assertAbsEquals(1F, FloatType.instance, 1F);
+        assertAbsEquals(1.5F, FloatType.instance, 1.5F);
+        assertAbsEquals(0F, FloatType.instance, 0F);
+        assertAbsEquals(3F, FloatType.instance, -3F);
+        assertAbsEquals(3.7F, FloatType.instance, -3.7F);
+
+        assertAbsEquals(1L, LongType.instance, 1L);
+        assertAbsEquals(0L, LongType.instance, 0L);
+        assertAbsEquals(3L, LongType.instance, -3L);
+
+        assertAbsEquals(1L, CounterColumnType.instance, 1L);
+        assertAbsEquals(0L, CounterColumnType.instance, 0L);
+        assertAbsEquals(3L, CounterColumnType.instance, -3L);
+
+        assertAbsEquals(1.0, DoubleType.instance, 1.0);
+        assertAbsEquals(1.5, DoubleType.instance, 1.5);
+        assertAbsEquals(0.0, DoubleType.instance, 0.0);
+        assertAbsEquals(3.0, DoubleType.instance, -3.0);
+        assertAbsEquals(3.7, DoubleType.instance, -3.7);
+
+        assertAbsEquals(BigInteger.ONE, IntegerType.instance, BigInteger.ONE);
+        assertAbsEquals(BigInteger.ZERO, IntegerType.instance, BigInteger.ZERO);
+        assertAbsEquals(BigInteger.valueOf(3), IntegerType.instance, BigInteger.valueOf(-3));
+
+        assertAbsEquals(BigDecimal.ONE, DecimalType.instance, BigDecimal.ONE);
+        assertAbsEquals(BigDecimal.valueOf(1.5), DecimalType.instance, BigDecimal.valueOf(1.5));
+        assertAbsEquals(BigDecimal.ZERO, DecimalType.instance, BigDecimal.ZERO);
+        assertAbsEquals(BigDecimal.valueOf(3), DecimalType.instance, BigDecimal.valueOf(-3));
+        assertAbsEquals(BigDecimal.valueOf(3.7), DecimalType.instance, BigDecimal.valueOf(-3.7));
+    }
+
+    private <T extends Number> void assertAbsEquals(T expected, NumberType<T> inputType, T inputValue)
+    {
+        assertFctEquals(MathFcts.absFct(inputType), expected, inputType, inputValue);
+    }
+
+    @Test
+    public void testExp()
+    {
+        assertExpEquals((int) Math.pow(Math.E, 5), Int32Type.instance, 5);
+        assertExpEquals((int) Math.E, Int32Type.instance, 1);
+        assertExpEquals(1, Int32Type.instance, 0);
+        assertExpEquals((int) Math.pow(Math.E, -1), Int32Type.instance, -1);
+
+        assertExpEquals((byte) Math.pow(Math.E, 5), ByteType.instance, (byte) 5);
+        assertExpEquals((byte) Math.E, ByteType.instance, (byte) 1);
+        assertExpEquals((byte) 1, ByteType.instance, (byte) 0);
+        assertExpEquals((byte) Math.pow(Math.E, -1), ByteType.instance, (byte) -1);
+
+        assertExpEquals((short) Math.pow(Math.E, 5), ShortType.instance, (short) 5);
+        assertExpEquals((short) Math.E, ShortType.instance, (short) 1);
+        assertExpEquals((short) 1, ShortType.instance, (short) 0);
+        assertExpEquals((short) Math.pow(Math.E, -1), ShortType.instance, (short) -1);
+
+        assertExpEquals((float) Math.pow(Math.E, 5), FloatType.instance, 5F);
+        assertExpEquals((float) Math.E, FloatType.instance, 1F);
+        assertExpEquals(1F, FloatType.instance, 0F);
+        assertExpEquals((float) Math.pow(Math.E, -1), FloatType.instance, -1F);
+        assertExpEquals((float) Math.pow(Math.E, -2.5), FloatType.instance, -2.5F);
+
+        assertExpEquals((long) Math.pow(Math.E, 5), LongType.instance, 5L);
+        assertExpEquals((long) Math.E, LongType.instance, 1L);
+        assertExpEquals(1L, LongType.instance, 0L);
+        assertExpEquals((long) Math.pow(Math.E, -1), LongType.instance, -1L);
+
+        assertExpEquals((long) Math.pow(Math.E, 5), CounterColumnType.instance, 5L);
+        assertExpEquals((long) Math.E, CounterColumnType.instance, 1L);
+        assertExpEquals(1L, CounterColumnType.instance, 0L);
+        assertExpEquals((long) Math.pow(Math.E, -1), CounterColumnType.instance, -1L);
+
+        assertExpEquals(BigInteger.valueOf((long) Math.pow(Math.E, 5)), IntegerType.instance, BigInteger.valueOf(5));
+        assertExpEquals(BigInteger.valueOf((long) Math.E), IntegerType.instance, BigInteger.valueOf(1));
+        assertExpEquals(BigInteger.valueOf(1), IntegerType.instance, BigInteger.valueOf(0));
+        assertExpEquals(BigInteger.valueOf((long) Math.pow(Math.E, -1)), IntegerType.instance, BigInteger.valueOf(-1));
+
+        assertExpEquals(new BigDecimal("148.41315910257660342111558004055"), DecimalType.instance, BigDecimal.valueOf(5));
+        assertExpEquals(new BigDecimal("2.7182818284590452353602874713527"), DecimalType.instance, BigDecimal.valueOf(1));
+        assertExpEquals(new BigDecimal("1"), DecimalType.instance, BigDecimal.valueOf(0));
+        assertExpEquals(new BigDecimal("0.36787944117144232159552377016146"), DecimalType.instance, BigDecimal.valueOf(-1));
+    }
+
+    private <T extends Number> void assertExpEquals(T expected, NumberType<T> inputType, T inputValue)
+    {
+        assertFctEquals(MathFcts.expFct(inputType), expected, inputType, inputValue);
+    }
+
+
+
+    @Test
+    public void testLog()
+    {
+        assertLogEquals((int) Math.log(5), Int32Type.instance, 5);
+        assertLogEquals(0, Int32Type.instance, (int) Math.E);
+        assertLogEquals(0, Int32Type.instance, 1);
+        assertLogEquals((int) Math.log(-1), Int32Type.instance, -1);
+
+        assertLogEquals((byte) Math.log(5), ByteType.instance, (byte) 5);
+        assertLogEquals((byte) 0, ByteType.instance, (byte) Math.E);
+        assertLogEquals((byte) 0, ByteType.instance, (byte) 1);
+        assertLogEquals((byte) Math.log(-1), ByteType.instance, (byte) -1);
+
+        assertLogEquals((short) Math.log(5), ShortType.instance, (short) 5);
+        assertLogEquals((short) 0, ShortType.instance, (short) Math.E);
+        assertLogEquals((short) 0, ShortType.instance, (short) 1);
+        assertLogEquals((short) Math.log(-1), ShortType.instance, (short) -1);
+
+        assertLogEquals((float) Math.log(5.5F), FloatType.instance, 5.5F, 0.0000001);
+        assertLogEquals(1F, FloatType.instance, (float) Math.E, 0.0000001);
+        assertLogEquals(0F, FloatType.instance, 1F, 0.0000001);
+        assertLogEquals((float) Math.log(-1F), FloatType.instance, -1F, 0.0000001);
+
+
+        assertLogEquals((long) Math.log(5), LongType.instance, 5L);
+        assertLogEquals(0L, LongType.instance, (long) Math.E);
+        assertLogEquals(0L, LongType.instance, 1L);
+        assertLogEquals((long) Math.log(-1), LongType.instance, -1L);
+
+        assertLogEquals((long) Math.log(5), CounterColumnType.instance, 5L);
+        assertLogEquals(0L, CounterColumnType.instance, (long) Math.E);
+        assertLogEquals(0L, CounterColumnType.instance, 1L);
+        assertLogEquals((long) Math.log(-1), CounterColumnType.instance, -1L);
+
+        assertLogEquals(BigInteger.valueOf((long) Math.log(5)), IntegerType.instance, BigInteger.valueOf(5));
+        assertLogEquals(BigInteger.valueOf(0), IntegerType.instance, BigInteger.valueOf((long) Math.E));
+        assertLogEquals(BigInteger.valueOf(0), IntegerType.instance, BigInteger.valueOf(1));
+
+        assertLogEquals(new BigDecimal("1.6094379124341003746007593332262"), DecimalType.instance, BigDecimal.valueOf(5));
+        assertLogEquals(new BigDecimal("0"), DecimalType.instance, BigDecimal.valueOf(1));
+    }
+
+    private <T extends Number> void assertLogEquals(T expected, NumberType<T> inputType, T inputValue)
+    {
+        assertFctEquals(MathFcts.logFct(inputType), expected, inputType, inputValue);
+    }
+
+    private <T extends Number> void assertLogEquals(T expected, NumberType<T> inputType, T inputValue, double delta)
+    {
+        assertFctEquals(MathFcts.logFct(inputType), expected, inputType, inputValue, delta);
+    }
+    
+    @Test
+    public void testLog10()
+    {
+        assertLog10Equals((int) Math.log10(5), Int32Type.instance, 5);
+        assertLog10Equals(0, Int32Type.instance, (int) Math.E);
+        assertLog10Equals(0, Int32Type.instance, 1);
+        assertLog10Equals((int) Math.log10(-1), Int32Type.instance, -1);
+
+        assertLog10Equals((byte) Math.log10(5), ByteType.instance, (byte) 5);
+        assertLog10Equals((byte) 0, ByteType.instance, (byte) Math.E);
+        assertLog10Equals((byte) 0, ByteType.instance, (byte) 1);
+        assertLog10Equals((byte) Math.log10(-1), ByteType.instance, (byte) -1);
+
+        assertLog10Equals((short) Math.log10(5), ShortType.instance, (short) 5);
+        assertLog10Equals((short) 0, ShortType.instance, (short) Math.E);
+        assertLog10Equals((short) 0, ShortType.instance, (short) 1);
+        assertLog10Equals((short) Math.log10(-1), ShortType.instance, (short) -1);
+
+        assertLog10Equals((float) Math.log10(5.5F), FloatType.instance, 5.5F, 0.0000001);
+        assertLog10Equals(1F, FloatType.instance, 10F, 0.0000001);
+        assertLog10Equals(0F, FloatType.instance, 1F, 0.0000001);
+        assertLog10Equals((float) Math.log10(-1F), FloatType.instance, -1F, 0.0000001);
+
+
+        assertLog10Equals((long) Math.log10(5), LongType.instance, 5L);
+        assertLog10Equals(0L, LongType.instance, (long) Math.E);
+        assertLog10Equals(0L, LongType.instance, 1L);
+        assertLog10Equals((long) Math.log10(-1), LongType.instance, -1L);
+
+        assertLog10Equals((long) Math.log10(5), CounterColumnType.instance, 5L);
+        assertLog10Equals(0L, CounterColumnType.instance, (long) Math.E);
+        assertLog10Equals(0L, CounterColumnType.instance, 1L);
+        assertLog10Equals((long) Math.log10(-1), CounterColumnType.instance, -1L);
+
+        assertLog10Equals(BigInteger.valueOf((long) Math.log10(5)), IntegerType.instance, BigInteger.valueOf(5));
+        assertLog10Equals(BigInteger.valueOf(0), IntegerType.instance, BigInteger.valueOf((long) Math.E));
+        assertLog10Equals(BigInteger.valueOf(0), IntegerType.instance, BigInteger.valueOf(1));
+
+        assertLog10Equals(new BigDecimal("0.69897000433601880478626110527551"), DecimalType.instance, BigDecimal.valueOf(5));
+        assertLog10Equals(new BigDecimal("0"), DecimalType.instance, BigDecimal.valueOf(1));
+    }
+
+    private <T extends Number> void assertLog10Equals(T expected, NumberType<T> inputType, T inputValue)
+    {
+        assertFctEquals(MathFcts.log10Fct(inputType), expected, inputType, inputValue);
+    }
+
+    private <T extends Number> void assertLog10Equals(T expected, NumberType<T> inputType, T inputValue, double delta)
+    {
+        assertFctEquals(MathFcts.log10Fct(inputType), expected, inputType, inputValue, delta);
+    }
+
+    @Test
+    public void testRound()
+    {
+        assertRoundEquals(5, Int32Type.instance, 5);
+        assertRoundEquals(0, Int32Type.instance, 0);
+        assertRoundEquals(-1, Int32Type.instance, -1);
+
+        assertRoundEquals((byte) 5, ByteType.instance, (byte) 5);
+        assertRoundEquals((byte) 0, ByteType.instance, (byte) 0);
+        assertRoundEquals((byte) -1, ByteType.instance, (byte) -1);
+
+        assertRoundEquals((short) 5, ShortType.instance, (short) 5);
+        assertRoundEquals((short) 0, ShortType.instance, (short) 0);
+        assertRoundEquals((short) -1, ShortType.instance, (short) -1);
+
+        assertRoundEquals((float) Math.round(5.5F), FloatType.instance, 5.5F);
+        assertRoundEquals(1F, FloatType.instance, 1F);
+        assertRoundEquals(0F, FloatType.instance, 0F);
+        assertRoundEquals((float) Math.round(-1.5F), FloatType.instance, -1.5F);
+
+
+        assertRoundEquals(5L, LongType.instance, 5L);
+        assertRoundEquals(0L, LongType.instance, 0L);
+        assertRoundEquals(-1L, LongType.instance, -1L);
+
+        assertRoundEquals(5L, CounterColumnType.instance, 5L);
+        assertRoundEquals(0L, CounterColumnType.instance, 0L);
+        assertRoundEquals(-1L, CounterColumnType.instance, -1L);
+
+        assertRoundEquals(BigInteger.valueOf(5), IntegerType.instance, BigInteger.valueOf(5));
+        assertRoundEquals(BigInteger.valueOf(0), IntegerType.instance, BigInteger.valueOf(0));
+        assertRoundEquals(BigInteger.valueOf(-1), IntegerType.instance, BigInteger.valueOf(-1));
+
+        assertRoundEquals(new BigDecimal("6"), DecimalType.instance, BigDecimal.valueOf(5.5));
+        assertRoundEquals(new BigDecimal("1"), DecimalType.instance, BigDecimal.valueOf(1));
+        assertRoundEquals(new BigDecimal("0"), DecimalType.instance, BigDecimal.valueOf(0));
+        assertRoundEquals(new BigDecimal("-2"), DecimalType.instance, BigDecimal.valueOf(-1.5));
+    }
+
+    private <T extends Number> void assertRoundEquals(T expected, NumberType<T> inputType, T inputValue)
+    {
+        assertFctEquals(MathFcts.roundFct(inputType), expected, inputType, inputValue);
+    }
+
+    private static ByteBuffer executeFunction(Function function, ByteBuffer input)
+    {
+        List<ByteBuffer> params = Collections.singletonList(input);
+        return ((ScalarFunction) function).execute(ProtocolVersion.CURRENT, params);
+    }
+
+    private <T extends Number> void assertFctEquals(NativeFunction fct, T expected, NumberType<T> inputType, T inputValue)
+    {
+        ByteBuffer input = inputType.decompose(inputValue);
+        if (expected instanceof BigDecimal)
+        {
+            // This block is to deal with the edgecase where two BigDecimals' values are equal but not their scale.
+            BigDecimal bdExpected = (BigDecimal) expected;
+            BigDecimal bdInputValue = (BigDecimal) inputType.compose(executeFunction(fct, input));
+            assertEquals(bdExpected.compareTo(bdInputValue), 0);
+        }
+        else
+        {
+            assertEquals(expected, inputType.compose(executeFunction(fct, input)));
+        }
+    }
+
+    private <T extends Number> void assertFctEquals(NativeFunction fct, T expected, NumberType<T> inputType, T inputValue, double delta)
+    {
+        ByteBuffer input = inputType.decompose(inputValue);
+        T actual = inputType.compose(executeFunction(fct, input));
+        if (Double.isNaN(expected.doubleValue())) {
+            Assert.assertTrue(Double.isNaN(actual.doubleValue()));
+        } else
+        {
+            Assert.assertTrue(Math.abs(expected.doubleValue() - actual.doubleValue()) <= delta);
+        }
+
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org