/*
* This software is subject to the terms of the Eclipse Public License v1.0
* Agreement, available at the following URL:
* http://www.eclipse.org/legal/epl-v10.html.
* You must accept the terms of that agreement to use this software.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package mondrian.udf;
import mondrian.olap.Evaluator;
import mondrian.olap.Syntax;
import mondrian.olap.fun.MondrianEvaluationException;
import mondrian.olap.type.NumericType;
import mondrian.olap.type.Type;
import mondrian.spi.UserDefinedFunction;
import org.apache.commons.math.MathException;
import org.apache.commons.math.distribution.NormalDistribution;
import org.apache.commons.math.distribution.NormalDistributionImpl;
import org.apache.log4j.Logger;
/**
* A user-defined function which returns the inverse normal distribution value
* of its argument.
*
* <p>This particular function is useful in Six Sigma calculations, for
* example,
*
* <blockquote><code><pre>
* WITH MEMBER [Measures].[Yield]
* AS '([Measures].[Number of Failures] / [Measures].[Population])',
* FORMAT_STRING = "0.00%"
* MEMBER [Measures].[Sigma]
* AS 'IIf([Measures].[Yield] <> 0,
* IIf([Measures].[Yield] > 0.5,
* 0,
* InverseNormal(1 - ([Measures].[Yield])) + 1.5), 6)',
* FORMAT_STRING = "0.0000"
* </pre></code></blockquote>
*/
public class InverseNormalUdf implements UserDefinedFunction {
private static final Logger LOGGER =
Logger.getLogger(InverseNormalUdf.class);
// the zero arg constructor sets the mean equal to zero and standard
// deviation equal to one
private static final NormalDistribution nd = new NormalDistributionImpl();
public String getName() {
return "InverseNormal";
}
public String getDescription() {
return "Returns inverse normal distribution of its argument";
}
public Syntax getSyntax() {
return Syntax.Function;
}
public Type getReturnType(Type[] types) {
return new NumericType();
}
public Type[] getParameterTypes() {
return new Type[] {new NumericType()};
}
public Object execute(Evaluator evaluator, Argument[] args) {
final Object argValue = args[0].evaluateScalar(evaluator);
LOGGER.debug("Inverse Normal argument was : " + argValue);
if (!(argValue instanceof Number)) {
// Argument might be a RuntimeException indicating that
// the cache does not yet have the required cell value. The
// function will be called again when the cache is loaded.
return null;
}
final Double d = new Double(((Number) argValue).doubleValue());
LOGGER.debug("Inverse Normal argument as Double was : " + d);
if (d.isNaN()) {
return null;
}
// If probability is nonnumeric or
// probability < 0 or
// probability > 1,
// returns an error.
double dbl = d.doubleValue();
if (dbl < 0.0 || dbl > 1.0) {
LOGGER.debug(
"Invalid value for inverse normal distribution: " + dbl);
throw new MondrianEvaluationException(
"Invalid value for inverse normal distribution: " + dbl);
}
try {
Double result = new Double(nd.inverseCumulativeProbability(dbl));
LOGGER.debug("Inverse Normal result : " + result.doubleValue());
return result;
} catch (MathException e) {
LOGGER.debug(
"Exception calculating inverse normal distribution: " + dbl, e);
throw new MondrianEvaluationException(
"Exception calculating inverse normal distribution: " + dbl);
}
}
public String[] getReservedWords() {
return null;
}
}
// End InverseNormalUdf.java