All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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