001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2006-2012 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap.fun;
011
012import mondrian.calc.*;
013import mondrian.calc.impl.AbstractDoubleCalc;
014import mondrian.mdx.ResolvedFunCall;
015import mondrian.olap.*;
016
017/**
018 * Definition of the <code>Percentile</code> MDX function.
019 *
020 * <p>There is some discussion about what the "right" percentile function is.
021 * Here is a <a href="http://cnx.org/content/m10805/latest/">good overview</a>.
022 * Wikipedia also lists
023 * <a href="http://en.wikipedia.org/wiki/Percentile">another method</a>:
024 *
025 * <blockquote>rank = P / 100 * (N - 1) + 1</blockquote>
026 *
027 * <p>Implemented here is example 1 on that page, except we use</p>
028 *
029 * <blockquote>rank = P / 100 * N</blockquote>
030 *
031 * <p>for the rank instead of</p>
032 *
033 * <blockquote>rank = P / 100 * (N + 1)</blockquote>
034 *
035 * <p>That's because this is how it used to be in the code.</p>
036 */
037class PercentileFunDef extends AbstractAggregateFunDef {
038    static final ReflectiveMultiResolver Resolver =
039        new ReflectiveMultiResolver(
040            "Percentile",
041            "Percentile(<Set>, <Numeric Expression>, <Percent>)",
042            "Returns the value of the tuple that is at a given percentile of a set.",
043            new String[] {"fnxnn"},
044            PercentileFunDef.class);
045
046    public PercentileFunDef(FunDef dummyFunDef) {
047        super(dummyFunDef);
048    }
049
050    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
051        final ListCalc listCalc =
052            compiler.compileList(call.getArg(0));
053        final Calc calc =
054            compiler.compileScalar(call.getArg(1), true);
055        final DoubleCalc percentCalc =
056            compiler.compileDouble(call.getArg(2));
057        return new AbstractDoubleCalc(
058            call, new Calc[] {listCalc, calc, percentCalc})
059        {
060            public double evaluateDouble(Evaluator evaluator) {
061                TupleList list = evaluateCurrentList(listCalc, evaluator);
062                double percent = percentCalc.evaluateDouble(evaluator) * 0.01;
063                final int savepoint = evaluator.savepoint();
064                try {
065                    evaluator.setNonEmpty(false);
066                    final double percentile =
067                        percentile(evaluator, list, calc, percent);
068                    return percentile;
069                } finally {
070                    evaluator.restore(savepoint);
071                }
072            }
073
074            public boolean dependsOn(Hierarchy hierarchy) {
075                return anyDependsButFirst(getCalcs(), hierarchy);
076            }
077        };
078    }
079}
080
081// End PercentileFunDef.java