package org.matheclipse.core.reflection.system;

import static org.matheclipse.core.expression.F.CN1;
import static org.matheclipse.core.expression.F.Ceiling;
import static org.matheclipse.core.expression.F.Times;

import org.matheclipse.core.builtin.function.NumericQ;
import org.matheclipse.core.eval.exception.Validate;
import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator;
import org.matheclipse.core.eval.interfaces.INumeric;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.ISignedNumber;
import org.matheclipse.core.interfaces.ISymbol;

import com.google.common.base.Function;

/**
 * Returns the largest (closest to positive infinity) ISignedNumber value that is not greater than this
 * and is equal to a mathematical integer.
 * 
 * See Wikipedia - Floor and ceiling functions
 */
public class Floor extends AbstractFunctionEvaluator implements INumeric {

	private final class FloorPlusFunction implements Function {
		@Override
		public IExpr apply(IExpr expr) {
			if (expr.isInteger()) {
				return expr;
			}
			return null;
		}
	}

	// private final class FloorNumericFunction implements INumericFunction {
	// public IExpr apply(double value) {
	// if (value < Integer.MAX_VALUE && value > Integer.MIN_VALUE) {
	// long result = Math.round(Math.floor(value));
	// return F.integer(result);
	// }
	// return null;
	// }
	// }

	public Floor() {
	}

	@Override
	public double evalReal(final double[] stack, final int top, final int size) {
		if (size != 1) {
			throw new UnsupportedOperationException();
		}
		return Math.floor(stack[top]);
	}

	@Override
	public IExpr evaluate(final IAST ast) {
		Validate.checkSize(ast, 2);

		try {
			IExpr arg1 = ast.arg1();
			if (arg1.isSignedNumber()) {
				return ((ISignedNumber) arg1).floor();
			}
			if (NumericQ.CONST.apply(arg1)) {
				IExpr result = F.evaln(arg1);
				if (result.isSignedNumber()) {
					return ((ISignedNumber) result).floor();
				}
			}
			if (arg1.isPlus()) {
				IAST[] result = ((IAST) arg1).split(new FloorPlusFunction());
				if (result[0].size() > 1) {
					if (result[1].size() > 1) {
						result[0].add(F.Floor(result[1]));
					}
					return result[0];
				}
			}
			if (AbstractFunctionEvaluator.isNegativeExpression(arg1)) {
				return Times(CN1, Ceiling(Times(CN1, arg1)));
			}
		} catch (ArithmeticException ae) {
			// ISignedNumber#floor() may throw ArithmeticException
		}
		return null;
	}

	@Override
	public IExpr numericEval(final IAST functionList) {
		return evaluate(functionList);
	}

	@Override
	public void setUp(final ISymbol symbol) {
		symbol.setAttributes(ISymbol.NHOLDALL | ISymbol.LISTABLE | ISymbol.NUMERICFUNCTION);
		super.setUp(symbol);
	}
}

 

Ads help maintain this website.