mirror of https://github.com/apache/cassandra
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:
parent
48d4897828
commit
88dc64d208
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
1
NEWS.txt
1
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ public class NativeFunctions
|
|||
OperationFcts.addFunctionsTo(this);
|
||||
AggregateFcts.addFunctionsTo(this);
|
||||
BytesConversionFcts.addFunctionsTo(this);
|
||||
MathFcts.addFunctionsTo(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue