package org.basex.query.func; import java.math.BigDecimal; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.item.AtomType; import org.basex.query.item.Dbl; import org.basex.query.item.Dec; import org.basex.query.item.Flt; import org.basex.query.item.Int; import org.basex.query.item.Item; import org.basex.query.item.Type; import org.basex.query.util.Err; import org.basex.util.InputInfo; /** * Numeric functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class FNNum extends StandardFunc { /** * Constructor. * @param ii input info * @param f function definition * @param e arguments */ public FNNum(final InputInfo ii, final Function f, final Expr... e) { super(ii, f, e); } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { final Item it = expr[0].item(ctx, input); if(it == null) return null; final Type ip = it.type; if(!ip.isUntyped() && !ip.isNumber()) Err.number(this, it); final double d = it.dbl(input); switch(sig) { case ABS: return abs(it, input); case CEILING: return num(it, d, StrictMath.ceil(d)); case FLOOR: return num(it, d, StrictMath.floor(d)); case ROUND: return rnd(it, d, false, ctx); case ROUND_HALF_TO_EVEN: return rnd(it, d, true, ctx); default: return super.item(ctx, ii); } } /** * Returns a rounded item. * @param it input item * @param d input double value * @param h2e half-to-even flag * @param ctx query context * @return absolute item * @throws QueryException query exception */ private Item rnd(final Item it, final double d, final boolean h2e, final QueryContext ctx) throws QueryException { final int p = expr.length == 1 ? 0 : (int) checkItr(expr[1], ctx); return round(it, d, p, h2e, input); } /** * Returns an absolute number. * @param it input item * @param ii input info * @return absolute item * @throws QueryException query exception */ private static Item abs(final Item it, final InputInfo ii) throws QueryException { final double d = it.dbl(ii); final boolean s = d > 0d || 1 / d > 0; final Type ip = it.type; if(ip instanceof AtomType) { switch((AtomType) ip) { case DBL: return s ? it : Dbl.get(Math.abs(it.dbl(ii))); case FLT: return s ? it : Flt.get(Math.abs((float) it.dbl(ii))); case DEC: return s ? it : Dec.get(it.dec(ii).abs()); case ITR: return s ? it : Int.get(Math.abs(it.itr(ii))); default: break; } } return ip.instanceOf(AtomType.ITR) ? Int.get(Math.abs(it.itr(ii))) : Dec.get(it.dec(ii).abs()); } /** * Returns a rounded item. * @param it input item * @param d input double value * @param h2e half-to-even flag * @param prec precision * @param ii input info * @return absolute item * @throws QueryException query exception */ public static Item round(final Item it, final double d, final int prec, final boolean h2e, final InputInfo ii) throws QueryException { // take care of untyped items final Item num = it.type.isUntyped() ? Dbl.get(it.dbl(ii)) : it; if(num.type == AtomType.DEC && prec >= 0) { final BigDecimal bd = num.dec(ii); final int m = h2e ? BigDecimal.ROUND_HALF_EVEN : bd.signum() > 0 ? BigDecimal.ROUND_HALF_UP : BigDecimal.ROUND_HALF_DOWN; return Dec.get(bd.setScale(prec, m)); } // calculate precision factor double p = 1; for(long i = prec; i > 0; --i) p *= 10; for(long i = prec; i < 0; ++i) p /= 10; double c = d; if(!Double.isNaN(c) && !Double.isInfinite(c)) { if(h2e) { c *= p; if(d < 0) c = -c; final double r = c % 1; c += r == .5 ? c % 2 == 1.5 ? .5 : -.5 : r > .5 ? 1 - r : -r; c /= p; if(d < 0) c = -c; } else if(c >= Long.MIN_VALUE && c < Long.MAX_VALUE) { final double dp = d * p; c = (dp >= -.5d && dp < 0 ? -0d : StrictMath.round(dp)) / p; } } return num(it, d, c); } /** * Returns a numeric result with the correct data type. * @param it input item * @param n input double value * @param d calculated double value * @return numeric item */ private static Item num(final Item it, final double n, final double d) { final Type ip = it.type; final Item i = ip.isUntyped() ? Dbl.get(n) : it; if(n == d) return i; if(ip instanceof AtomType) { switch((AtomType) ip) { case DEC: return Dec.get(d); case DBL: return Dbl.get(d); case FLT: return Flt.get((float) d); case ITR: return Int.get((long) d); default: break; } } return Dbl.get(d); } @Override public boolean uses(final Use u) { return u == Use.X30 && sig == Function.ROUND && expr.length == 2 || super.uses(u); } }