![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.rdf.internal.constraints.MathUtility Maven / Gradle / Ivy
package com.bigdata.rdf.internal.constraints;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.datatypes.XMLDatatypeUtil;
import org.openrdf.model.vocabulary.XMLSchema;
import com.bigdata.rdf.error.SparqlTypeErrorException;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.XPathMathFunctions;
import com.bigdata.rdf.internal.constraints.MathBOp.MathOp;
import com.bigdata.rdf.internal.constraints.NumericBOp.NumericOp;
import com.bigdata.rdf.internal.impl.extensions.CompressedTimestampExtension;
import com.bigdata.rdf.internal.impl.literal.NumericIV;
import com.bigdata.rdf.internal.impl.literal.XSDDecimalIV;
import com.bigdata.rdf.internal.impl.literal.XSDIntegerIV;
import com.bigdata.rdf.internal.impl.literal.XSDNumericIV;
import com.bigdata.rdf.model.BigdataValueFactory;
public class MathUtility implements IMathOpHandler {
public static boolean checkNumericDatatype(final Literal... args) {
for (Literal lit : args) {
final URI dt = lit.getDatatype();
if (dt == null)
return false;
boolean isNumeric = false;
isNumeric |= XMLDatatypeUtil.isNumericDatatype(dt);
isNumeric |= dt.equals(CompressedTimestampExtension.COMPRESSED_TIMESTAMP);
if (!isNumeric)
return false;
}
return true;
}
@Override
public boolean canInvokeMathOp(final Literal... args) {
return checkNumericDatatype(args);
}
@Override
public NumericIV doMathOp(final Literal l1, final IV iv1,
final Literal l2, final IV iv2,
final MathOp op,
final BigdataValueFactory vf)
{
return literalMath(l1, l2, op);
}
public static NumericIV literalMath(final Literal l1, final Literal l2,
final MathOp op)
{
if (!checkNumericDatatype(l1, l2))
throw new IllegalArgumentException("Not numbers: " + l1 + ", " + l2);
final URI dt1 = l1.getDatatype();
final URI dt2 = l2.getDatatype();
// Determine most specific datatype that the arguments have in common,
// choosing from xsd:integer, xsd:decimal, xsd:float and xsd:double as
// per the SPARQL/XPATH spec
URI commonDatatype;
if (dt1.equals(XMLSchema.DOUBLE) || dt2.equals(XMLSchema.DOUBLE)) {
commonDatatype = XMLSchema.DOUBLE;
} else if (dt1.equals(XMLSchema.FLOAT) || dt2.equals(XMLSchema.FLOAT)) {
commonDatatype = XMLSchema.FLOAT;
} else if (dt1.equals(XMLSchema.DECIMAL) || dt2.equals(XMLSchema.DECIMAL)) {
commonDatatype = XMLSchema.DECIMAL;
} else if (op == MathOp.DIVIDE) {
// Result of integer divide is decimal and requires the arguments to
// be handled as such, see for details:
// http://www.w3.org/TR/xpath-functions/#func-numeric-divide
commonDatatype = XMLSchema.DECIMAL;
} else {
commonDatatype = XMLSchema.INTEGER;
}
// Note: Java already handles cases like divide-by-zero appropriately
// for floats and doubles, see:
// http://www.particle.kth.se/~lindsey/JavaCourse/Book/Part1/Tech/
// Chapter02/floatingPt2.html
try {
if (commonDatatype.equals(XMLSchema.DOUBLE)) {
double left = l1.doubleValue();
double right = l2.doubleValue();
return numericalMath(left, right, op);
}
else if (commonDatatype.equals(XMLSchema.FLOAT)) {
float left = l1.floatValue();
float right = l2.floatValue();
return numericalMath(left, right, op);
}
else if (commonDatatype.equals(XMLSchema.DECIMAL)) {
BigDecimal left = l1.decimalValue();
BigDecimal right = l2.decimalValue();
return numericalMath(left, right, op);
}
else { // XMLSchema.INTEGER
BigInteger left = l1.integerValue();
BigInteger right = l2.integerValue();
return numericalMath(left, right, op);
}
} catch (NumberFormatException e) {
throw new SparqlTypeErrorException();
} catch (ArithmeticException e) {
throw new SparqlTypeErrorException();
}
}
// public static final IV numericalMath(final Literal l1, final IV iv2,
// final MathOp op) {
//
// final URI dt1 = l1.getDatatype();
//
// // Only numeric value can be used in math expressions
// if (dt1 == null || !XMLDatatypeUtil.isNumericDatatype(dt1)) {
// throw new IllegalArgumentException("Not a number: " + l1);
// }
//
// if (!iv2.isInline())
// throw new IllegalArgumentException(
// "right term is not inline: left=" + l1 + ", right=" + iv2);
//
// if (!iv2.isLiteral())
// throw new IllegalArgumentException(
// "right term is not literal: left=" + l1 + ", right=" + iv2);
//
// final DTE dte2 = iv2.getDTE();
//
// if (!dte2.isNumeric())
// throw new IllegalArgumentException(
// "right term is not numeric: left=" + l1 + ", right=" + iv2);
//
// final AbstractLiteralIV num2 = (AbstractLiteralIV) iv2;
//
// // Determine most specific datatype that the arguments have in common,
// // choosing from xsd:integer, xsd:decimal, xsd:float and xsd:double as
// // per the SPARQL/XPATH spec
//
// if (dte2 == DTE.XSDDouble || dt1.equals(XMLSchema.DOUBLE)) {
// return numericalMath(l1.doubleValue(), num2.doubleValue(), op);
// } else if (dte2 == DTE.XSDFloat || dt1.equals(XMLSchema.FLOAT)) {
// return numericalMath(l1.floatValue(), num2.floatValue(), op);
// } else if (dte2 == DTE.XSDDecimal || dt1.equals(XMLSchema.DECIMAL)) {
// return numericalMath(l1.decimalValue(), num2.decimalValue(), op);
// } else if (op == MathOp.DIVIDE) {
// // Result of integer divide is decimal and requires the arguments to
// // be handled as such, see for details:
// // http://www.w3.org/TR/xpath-functions/#func-numeric-divide
// return numericalMath(l1.decimalValue(), num2.decimalValue(), op);
// } else {
// return numericalMath(l1.integerValue(), num2.integerValue(), op);
// }
//
// }
//
// public static final IV numericalMath(final Literal l1, final Literal l2,
// final MathOp op) {
//
// final URI dt1 = l1.getDatatype();
//
// // Only numeric value can be used in math expressions
// if (dt1 == null || !XMLDatatypeUtil.isNumericDatatype(dt1)) {
// throw new IllegalArgumentException("Not a number: " + l1);
// }
//
// final URI dt2 = l2.getDatatype();
//
// // Only numeric value can be used in math expressions
// if (dt2 == null || !XMLDatatypeUtil.isNumericDatatype(dt2)) {
// throw new IllegalArgumentException("Not a number: " + l2);
// }
//
// // Determine most specific datatype that the arguments have in common,
// // choosing from xsd:integer, xsd:decimal, xsd:float and xsd:double as
// // per the SPARQL/XPATH spec
//
// if (dt2.equals(XMLSchema.DOUBLE)|| dt1.equals(XMLSchema.DOUBLE)) {
// return numericalMath(l1.doubleValue(), l2.doubleValue(), op);
// } else if ( dt2.equals(XMLSchema.FLOAT)|| dt1.equals(XMLSchema.FLOAT)) {
// return numericalMath(l1.floatValue(), l2.floatValue(), op);
// } else if (dt2.equals(XMLSchema.DECIMAL)|| dt1.equals(XMLSchema.DECIMAL)) {
// return numericalMath(l1.decimalValue(), l2.decimalValue(), op);
// } else if (op == MathOp.DIVIDE) {
// // Result of integer divide is decimal and requires the arguments to
// // be handled as such, see for details:
// // http://www.w3.org/TR/xpath-functions/#func-numeric-divide
// return numericalMath(l1.decimalValue(), l2.decimalValue(), op);
// } else {
// return numericalMath(l1.integerValue(), l2.integerValue(), op);
// }
//
// }
//
// public static final IV numericalMath(final IV iv1, final IV iv2,
// final MathOp op) {
//
// if (!iv1.isInline())
// throw new IllegalArgumentException(
// "left term is not inline: left=" + iv1 + ", right=" + iv2);
//
// if (!iv2.isInline())
// throw new IllegalArgumentException(
// "right term is not inline: left=" + iv1 + ", right=" + iv2);
//
// if (!iv1.isLiteral())
// throw new IllegalArgumentException(
// "left term is not literal: left=" + iv1 + ", right=" + iv2);
//
// if (!iv2.isLiteral())
// throw new IllegalArgumentException(
// "right term is not literal: left=" + iv1 + ", right=" + iv2);
//
// final DTE dte1 = iv1.getDTE();
// final DTE dte2 = iv2.getDTE();
//
// if (!dte1.isNumeric())
// throw new IllegalArgumentException(
// "right term is not numeric: left=" + iv1 + ", right=" + iv2);
//
// if (!dte2.isNumeric())
// throw new IllegalArgumentException(
// "left term is not numeric: left=" + iv1 + ", right=" + iv2);
//
// final Literal num1 = (Literal) iv1;
// final Literal num2 = (Literal) iv2;
//
// // Determine most specific datatype that the arguments have in common,
// // choosing from xsd:integer, xsd:decimal, xsd:float and xsd:double as
// // per the SPARQL/XPATH spec
//
// if (dte1 == DTE.XSDDouble || dte2 == DTE.XSDDouble) {
// return numericalMath(num1.doubleValue(), num2.doubleValue(), op);
// } else if (dte1 == DTE.XSDFloat || dte2 == DTE.XSDFloat) {
// return numericalMath(num1.floatValue(), num2.floatValue(), op);
// } if (dte1 == DTE.XSDDecimal || dte2 == DTE.XSDDecimal) {
// return numericalMath(num1.decimalValue(), num2.decimalValue(), op);
// } if (op == MathOp.DIVIDE) {
// // Result of integer divide is decimal and requires the arguments to
// // be handled as such, see for details:
// // http://www.w3.org/TR/xpath-functions/#func-numeric-divide
// return numericalMath(num1.decimalValue(), num2.decimalValue(), op);
// } else {
// return numericalMath(num1.integerValue(), num2.integerValue(), op);
// }
//
//// // if one's a BigDecimal we should use the BigDecimal comparator for both
//// if (dte1 == DTE.XSDDecimal || dte2 == DTE.XSDDecimal) {
//// return numericalMath(num1.decimalValue(), num2.decimalValue(), op);
//// }
////
//// // same for BigInteger
//// if (dte1 == DTE.XSDInteger || dte2 == DTE.XSDInteger) {
//// return numericalMath(num1.integerValue(), num2.integerValue(), op);
//// }
////
//// // fixed length numerics
//// if (dte1.isFloatingPointNumeric() || dte2.isFloatingPointNumeric()) {
//// // non-BigDecimal floating points
//// if (dte1 == DTE.XSDFloat || dte2 == DTE.XSDFloat)
//// return numericalMath(num1.floatValue(), num2.floatValue(), op);
//// else
//// return numericalMath(num1.doubleValue(), num2.doubleValue(), op);
//// } else {
//// // non-BigInteger integers
//// if (dte1 == DTE.XSDInt && dte2 == DTE.XSDInt)
//// return numericalMath(num1.intValue(), num2.intValue(), op);
//// else
//// return numericalMath(num1.longValue(), num2.longValue(), op);
//// }
//
// }
/**
* The XPath numeric functions: abs, ceiling, floor, and round.
*
* @param iv1
* The operand.
* @param op
* The operation.
* @return The result.
*
* @see XPathMathFunctions
*/
public final static NumericIV numericalFunc(final Literal lit, final NumericOp op) {
if (!checkNumericDatatype(lit))
throw new IllegalArgumentException("not numeric: " + lit);
final URI dte1 = lit.getDatatype();
/*
* FIXME These xpath functions have very custom semantics. They need to
* be lifted out of this class and put into their own static methods
* with their own test suites.
*/
// switch (op) {
// case ABS:
// return XPathMathFunctions.abs(iv1);
// case CEIL:
// return XPathMathFunctions.ceiling(iv1);
// case FLOOR:
// return XPathMathFunctions.floor(iv1);
// case ROUND:
// return XPathMathFunctions.round(iv1);
// default:
// throw new UnsupportedOperationException(op.toString());
// }
if (dte1.equals(XMLSchema.DECIMAL)) {
return numericalFunc(lit.decimalValue(), op);
} else if (dte1.equals(XMLSchema.INTEGER)) {
return numericalFunc(lit.integerValue(), op);
} else if (dte1.equals(XMLSchema.FLOAT)) {
return numericalFunc(lit.floatValue(), op);
} else if (dte1.equals(XMLSchema.INT)) {
return numericalFunc(lit.intValue(), op);
} else if (dte1.equals(XMLSchema.DOUBLE)) {
return numericalFunc(lit.doubleValue(), op);
} else {
return numericalFunc(lit.longValue(), op);
}
}
@Deprecated
private static final NumericIV numericalFunc(final BigDecimal left, final NumericOp op) {
switch(op) {
case ABS:
return new XSDDecimalIV(left.abs());
case CEIL:
return new XSDDecimalIV(new BigDecimal(Math.round(Math.ceil(left.doubleValue()))));
case FLOOR:
return new XSDDecimalIV(new BigDecimal(Math.round(Math.floor(left.doubleValue()))));
case ROUND:
return new XSDDecimalIV(new BigDecimal(Math.round(left.doubleValue())));
default:
throw new UnsupportedOperationException();
}
}
@Deprecated
private static final NumericIV numericalFunc(final BigInteger left, final NumericOp op) {
switch(op) {
case ABS:
return new XSDIntegerIV(left.abs());
case CEIL:
return new XSDNumericIV(Math.ceil(left.doubleValue()));
case FLOOR:
return new XSDNumericIV(Math.floor(left.doubleValue()));
case ROUND:
return new XSDNumericIV(Math.round(left.doubleValue()));
default:
throw new UnsupportedOperationException();
}
}
@Deprecated
private static final NumericIV numericalFunc(final float left, final NumericOp op) {
switch(op) {
case ABS:
return new XSDNumericIV(Math.abs(left));
case CEIL:
return new XSDNumericIV(Math.ceil(left));
case FLOOR:
return new XSDNumericIV(Math.floor(left));
case ROUND:
return new XSDNumericIV(Math.round(left));
default:
throw new UnsupportedOperationException();
}
}
@Deprecated
private static final NumericIV numericalFunc(final int left, final NumericOp op) {
switch(op) {
case ABS:
return new XSDNumericIV(Math.abs(left));
case CEIL:
return new XSDNumericIV(Math.ceil(left));
case FLOOR:
return new XSDNumericIV(Math.floor(left));
case ROUND:
return new XSDNumericIV(Math.round(left));
default:
throw new UnsupportedOperationException();
}
}
@Deprecated
private static final NumericIV numericalFunc(final long left, final NumericOp op) {
switch(op) {
case ABS:
return new XSDNumericIV(Math.abs(left));
case CEIL:
return new XSDNumericIV(Math.ceil(left));
case FLOOR:
return new XSDNumericIV(Math.floor(left));
case ROUND:
return new XSDNumericIV(Math.round(left));
default:
throw new UnsupportedOperationException();
}
}
@Deprecated
private static final NumericIV numericalFunc(final double left, final NumericOp op) {
switch(op) {
case ABS:
return new XSDNumericIV(Math.abs(left));
case CEIL:
return new XSDNumericIV(Math.ceil(left));
case FLOOR:
return new XSDNumericIV(Math.floor(left));
case ROUND:
return new XSDNumericIV(Math.round(left));
default:
throw new UnsupportedOperationException();
}
}
private static final NumericIV numericalMath(final BigDecimal left,
final BigDecimal right, final MathOp op) {
switch(op) {
case PLUS:
return new XSDDecimalIV(left.add(right));
case MINUS:
return new XSDDecimalIV(left.subtract(right));
case MULTIPLY:
return new XSDDecimalIV(left.multiply(right));
case DIVIDE:
/*
* Note: Change per mroycsi. Reverts to a half-rounding mode iff
* an exact quotient can not be represented.
*/
try {
// try for exact quotient
return new XSDDecimalIV(left.divide(right));
} catch (ArithmeticException ae) {
// half-rounding mode.
return new XSDDecimalIV(left.divide(right, 20,
RoundingMode.HALF_UP));
}
// return new XSDDecimalIV(left.divide(right, RoundingMode.HALF_UP));
case MIN:
return new XSDDecimalIV(left.compareTo(right) < 0 ? left : right);
case MAX:
return new XSDDecimalIV(left.compareTo(right) > 0 ? left : right);
default:
throw new UnsupportedOperationException();
}
}
private static final NumericIV numericalMath(final BigInteger left,
final BigInteger right, final MathOp op) {
switch(op) {
case PLUS:
return new XSDIntegerIV(left.add(right));
case MINUS:
return new XSDIntegerIV(left.subtract(right));
case MULTIPLY:
return new XSDIntegerIV(left.multiply(right));
case DIVIDE:
return new XSDIntegerIV(left.divide(right));
case MIN:
return new XSDIntegerIV(left.compareTo(right) < 0 ? left : right);
case MAX:
return new XSDIntegerIV(left.compareTo(right) > 0 ? left : right);
default:
throw new UnsupportedOperationException();
}
}
private static final NumericIV numericalMath(final float left,
final float right, final MathOp op) {
switch(op) {
case PLUS:
return new XSDNumericIV(left+right);
case MINUS:
return new XSDNumericIV(left-right);
case MULTIPLY:
return new XSDNumericIV(left*right);
case DIVIDE:
return new XSDNumericIV(left/right);
case MIN:
return new XSDNumericIV(Math.min(left,right));
case MAX:
return new XSDNumericIV(Math.max(left,right));
default:
throw new UnsupportedOperationException();
}
}
private static final NumericIV numericalMath(final double left,
final double right, final MathOp op) {
switch(op) {
case PLUS:
return new XSDNumericIV(left+right);
case MINUS:
return new XSDNumericIV(left-right);
case MULTIPLY:
return new XSDNumericIV(left*right);
case DIVIDE:
return new XSDNumericIV(left/right);
case MIN:
return new XSDNumericIV(Math.min(left,right));
case MAX:
return new XSDNumericIV(Math.max(left,right));
default:
throw new UnsupportedOperationException();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy