blob: 74667033a9779047a072d6c08082b9bbb78f770b [file] [log] [blame]
/*
* 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.vxquery.runtime.functions.numeric;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
import org.apache.hyracks.api.context.IHyracksTaskContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.api.IPointable;
import org.apache.hyracks.data.std.primitive.BytePointable;
import org.apache.hyracks.data.std.primitive.DoublePointable;
import org.apache.hyracks.data.std.primitive.FloatPointable;
import org.apache.hyracks.data.std.primitive.IntegerPointable;
import org.apache.hyracks.data.std.primitive.LongPointable;
import org.apache.hyracks.data.std.primitive.ShortPointable;
import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
import org.apache.vxquery.datamodel.accessors.TaggedValuePointable;
import org.apache.vxquery.datamodel.accessors.TypedPointables;
import org.apache.vxquery.datamodel.accessors.atomic.XSDecimalPointable;
import org.apache.vxquery.datamodel.values.ValueTag;
import org.apache.vxquery.exceptions.ErrorCode;
import org.apache.vxquery.exceptions.SystemException;
import org.apache.vxquery.runtime.functions.base.AbstractTaggedValueArgumentScalarEvaluator;
import org.apache.vxquery.runtime.functions.base.AbstractTaggedValueArgumentScalarEvaluatorFactory;
import org.apache.vxquery.runtime.functions.cast.CastToDecimalOperation;
import org.apache.vxquery.types.BuiltinTypeRegistry;
public class FnRoundHalfToEvenScalarEvaluatorFactory extends AbstractTaggedValueArgumentScalarEvaluatorFactory {
private static final long serialVersionUID = 1L;
public FnRoundHalfToEvenScalarEvaluatorFactory(IScalarEvaluatorFactory[] args) {
super(args);
}
@Override
protected AbstractTaggedValueArgumentScalarEvaluator createEvaluator(IHyracksTaskContext ctx,
IScalarEvaluator[] args) throws HyracksDataException {
return new AbstractTaggedValueArgumentScalarEvaluator(args) {
final ArrayBackedValueStorage abvs = new ArrayBackedValueStorage();
final DataOutput dOut = abvs.getDataOutput();
final ArrayBackedValueStorage abvsInner = new ArrayBackedValueStorage();
final DataOutput dOutInner = abvsInner.getDataOutput();
final TypedPointables tp = new TypedPointables();
final LongPointable longp = (LongPointable) LongPointable.FACTORY.createPointable();
final CastToDecimalOperation castToDecimal = new CastToDecimalOperation();
@Override
protected void evaluate(TaggedValuePointable[] args, IPointable result) throws SystemException {
TaggedValuePointable tvp1 = args[0];
int tid = getBaseTypeForArithmetics(tvp1.getTag());
long precision = 0;
if (args.length > 1) {
TaggedValuePointable tvp2 = args[1];
if (tvp2.getTag() != ValueTag.XS_INTEGER_TAG) {
throw new SystemException(ErrorCode.FORG0006);
}
tvp2.getValue(longp);
precision = longp.getLong();
}
// Check special cases.
try {
switch (tid) {
case ValueTag.XS_FLOAT_TAG:
tvp1.getValue(tp.floatp);
if (tp.floatp.getFloat() == 0 || Float.isNaN(tp.floatp.getFloat())
|| Float.isInfinite(tp.floatp.getFloat())) {
result.set(tvp1.getByteArray(), tvp1.getStartOffset(),
FloatPointable.TYPE_TRAITS.getFixedLength() + 1);
return;
}
break;
case ValueTag.XS_DOUBLE_TAG:
tvp1.getValue(tp.doublep);
if (tp.doublep.getDouble() == 0 || Double.isNaN(tp.doublep.getDouble())
|| Double.isInfinite(tp.doublep.getDouble())) {
result.set(tvp1.getByteArray(), tvp1.getStartOffset(),
DoublePointable.TYPE_TRAITS.getFixedLength() + 1);
return;
}
break;
}
} catch (Exception e) {
throw new SystemException(ErrorCode.SYSE0001, e);
}
// Prepare input.
try {
getDecimalPointable(tp, tvp1);
} catch (IOException e) {
throw new SystemException(ErrorCode.SYSE0001, e);
}
// Perform rounding on decimal value.
byte decimalPlace = tp.decp.getDecimalPlace();
long decimalValue = tp.decp.getDecimalValue();
long newValue;
//check if the input needs to rounded to even or normally
if (decimalPlace - precision == 1 && (Math.abs(decimalValue) % 10 == 5)) {
newValue = decimalValue / 10;
if (!(newValue % 2 == 0)) {
if (newValue > 0) {
newValue += 1;
} else {
newValue -= 1;
}
}
tp.decp.setDecimal(newValue, (byte) precision);
} else if ((precision - decimalPlace) < 0) {
decimalValue = (long) Math.round(decimalValue / Math.pow(10, -(precision - decimalPlace)));
tp.decp.setDecimal(decimalValue, (byte) precision);
}
// Return result.
try {
switch (tvp1.getTag()) {
case ValueTag.XS_DECIMAL_TAG:
dOut.write(ValueTag.XS_DECIMAL_TAG);
dOut.write(tp.decp.getByteArray(), tp.decp.getStartOffset(),
XSDecimalPointable.TYPE_TRAITS.getFixedLength());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
XSDecimalPointable.TYPE_TRAITS.getFixedLength() + 1);
return;
case ValueTag.XS_INTEGER_TAG:
case ValueTag.XS_LONG_TAG:
case ValueTag.XS_NEGATIVE_INTEGER_TAG:
case ValueTag.XS_NON_POSITIVE_INTEGER_TAG:
case ValueTag.XS_NON_NEGATIVE_INTEGER_TAG:
case ValueTag.XS_POSITIVE_INTEGER_TAG:
case ValueTag.XS_UNSIGNED_INT_TAG:
case ValueTag.XS_UNSIGNED_LONG_TAG:
dOut.write(tvp1.getTag());
dOut.writeLong(tp.decp.longValue());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
LongPointable.TYPE_TRAITS.getFixedLength() + 1);
return;
case ValueTag.XS_INT_TAG:
case ValueTag.XS_UNSIGNED_SHORT_TAG:
dOut.write(tvp1.getTag());
dOut.writeInt(tp.decp.intValue());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
IntegerPointable.TYPE_TRAITS.getFixedLength() + 1);
return;
case ValueTag.XS_SHORT_TAG:
case ValueTag.XS_UNSIGNED_BYTE_TAG:
dOut.write(tvp1.getTag());
dOut.writeShort(tp.decp.shortValue());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
ShortPointable.TYPE_TRAITS.getFixedLength() + 1);
return;
case ValueTag.XS_BYTE_TAG:
dOut.write(tvp1.getTag());
dOut.writeByte(tp.decp.byteValue());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
BytePointable.TYPE_TRAITS.getFixedLength() + 1);
return;
case ValueTag.XS_FLOAT_TAG:
dOut.write(ValueTag.XS_FLOAT_TAG);
dOut.writeFloat(tp.decp.floatValue());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
FloatPointable.TYPE_TRAITS.getFixedLength() + 1);
return;
case ValueTag.XS_DOUBLE_TAG:
dOut.write(ValueTag.XS_DOUBLE_TAG);
dOut.writeDouble(tp.decp.doubleValue());
result.set(abvs.getByteArray(), abvs.getStartOffset(),
DoublePointable.TYPE_TRAITS.getFixedLength() + 1);
return;
}
} catch (Exception e) {
throw new SystemException(ErrorCode.SYSE0001, e);
}
}
private void getDecimalPointable(TypedPointables tp, TaggedValuePointable tvp) throws SystemException,
IOException {
abvsInner.reset();
long value;
switch (tvp.getTag()) {
case ValueTag.XS_DECIMAL_TAG:
tvp.getValue(tp.decp);
return;
case ValueTag.XS_FLOAT_TAG:
tvp.getValue(tp.floatp);
castToDecimal.convertFloat(tp.floatp, dOutInner);
tp.decp.set(abvsInner.getByteArray(), abvsInner.getStartOffset() + 1,
XSDecimalPointable.TYPE_TRAITS.getFixedLength());
return;
case ValueTag.XS_DOUBLE_TAG:
tvp.getValue(tp.doublep);
castToDecimal.convertDouble(tp.doublep, dOutInner);
tp.decp.set(abvsInner.getByteArray(), abvsInner.getStartOffset() + 1,
XSDecimalPointable.TYPE_TRAITS.getFixedLength());
return;
case ValueTag.XS_INTEGER_TAG:
case ValueTag.XS_LONG_TAG:
case ValueTag.XS_NEGATIVE_INTEGER_TAG:
case ValueTag.XS_NON_POSITIVE_INTEGER_TAG:
case ValueTag.XS_NON_NEGATIVE_INTEGER_TAG:
case ValueTag.XS_POSITIVE_INTEGER_TAG:
case ValueTag.XS_UNSIGNED_INT_TAG:
case ValueTag.XS_UNSIGNED_LONG_TAG:
tvp.getValue(tp.longp);
value = tp.longp.longValue();
break;
case ValueTag.XS_INT_TAG:
case ValueTag.XS_UNSIGNED_SHORT_TAG:
tvp.getValue(tp.intp);
value = tp.intp.longValue();
break;
case ValueTag.XS_SHORT_TAG:
case ValueTag.XS_UNSIGNED_BYTE_TAG:
tvp.getValue(tp.shortp);
value = tp.shortp.longValue();
break;
case ValueTag.XS_BYTE_TAG:
tvp.getValue(tp.bytep);
value = tp.bytep.longValue();
break;
default:
throw new SystemException(ErrorCode.XPTY0004);
}
dOutInner.write(ValueTag.XS_DECIMAL_TAG);
tp.decp.set(abvsInner.getByteArray(), abvsInner.getStartOffset() + 1,
XSDecimalPointable.TYPE_TRAITS.getFixedLength());
tp.decp.setDecimal(value, (byte) 0);
}
private int getBaseTypeForArithmetics(int tid) throws SystemException {
while (true) {
switch (tid) {
case ValueTag.XS_DECIMAL_TAG:
case ValueTag.XS_DOUBLE_TAG:
case ValueTag.XS_FLOAT_TAG:
case ValueTag.XS_INTEGER_TAG:
return tid;
case ValueTag.XS_ANY_ATOMIC_TAG:
throw new SystemException(ErrorCode.XPTY0004);
default:
tid = BuiltinTypeRegistry.INSTANCE.getSchemaTypeById(tid).getBaseType().getTypeId();
}
}
}
};
}
}