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.
This commit is contained in:
XV4DE 2022-05-14 12:28:40 -07:00 committed by Benjamin Lerer
parent 48d4897828
commit 88dc64d208
18 changed files with 859 additions and 0 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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()
{
}
}

View File

@ -41,6 +41,7 @@ public class NativeFunctions
OperationFcts.addFunctionsTo(this);
AggregateFcts.addFunctionsTo(this);
BytesConversionFcts.addFunctionsTo(this);
MathFcts.addFunctionsTo(this);
}
};

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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)));
}
}

View File

@ -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)));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}