/**
 * Tests for the approximate percentile expression semantics.
 * @tags: [
 *   requires_fcv_70,
 * ]
 */
import {
    testLargeInput,
    testLargeNonNumericInput,
    testWithProject
} from "jstests/aggregation/libs/percentiles_util.js";

const coll = db[jsTestName()];

/**
 * Tests with input as a single expression which evaluates to an array.
 */
testWithProject({
    coll: coll,
    doc: {x: [0, "non-numeric", 1, 2]},
    percentileSpec: {$percentile: {p: [0.5], input: "$x", method: "approximate"}},
    expectedResult: [1],
    msg: "Non-numeric data in input field which evaluates to an array should be ignored"
});

testWithProject({
    coll: coll,
    doc: {x: [10, 5, 27], x1: 5},
    percentileSpec: {$percentile: {p: [0], input: "$x", method: "approximate"}},
    expectedResult: [5],
    msg: "Minimum percentile"
});

testWithProject({
    coll: coll,
    doc: {x: [0, 1, 2]},
    percentileSpec: {$percentile: {p: [0.5, 0.9, 0.1], input: "$x", method: "approximate"}},
    expectedResult: [1, 2, 0],
    msg: "Multiple percentiles when input field evaluates to an array"
});

/**
 * Tests with input as an array of expressions.
 */
testWithProject({
    coll: coll,
    doc: {x: 0, x1: "non-numeric", x2: 1, x3: 2, x4: [2, 2, 2]},
    percentileSpec:
        {$percentile: {p: [0.5], input: ["$x", "$x1", "$x2", "$x3", "$x4"], method: "approximate"}},
    expectedResult: [1],
    msg: "Non-numeric data in input field passed in as an array should be ignored"
});

testWithProject({
    coll: coll,
    doc: {x: "non-numeric"},
    percentileSpec: {$percentile: {p: [0.5], input: ["$x"], method: "approximate"}},
    expectedResult: [null],
    msg: "Percentile of completely non-numeric data when input field is an array"
});

testWithProject({
    coll: coll,
    doc: {x: "non-numeric", x1: "also non-numeric", x2: [1, 2, 3]},
    percentileSpec:
        {$percentile: {p: [0.5, 0.9], input: ["$x", "$x1", "$x2"], method: "approximate"}},
    expectedResult: [null, null],
    msg: "Multiple percentiles of completely non-numeric data in input field passed as an array"
});

testWithProject({
    coll: coll,
    doc: {x: [-Infinity, Infinity, Infinity, Infinity]},
    percentileSpec: {$percentile: {p: [0.1, 0.5], input: "$x", method: "approximate"}},
    expectedResult: [-Infinity, Infinity],
    msg: "Percentile of all infinities"
});

testWithProject({
    coll: coll,
    doc: {x: [0, "non-numeric", 1, 2, 3, Infinity]},
    percentileSpec: {$percentile: {p: [0.5], input: "$x", method: "approximate"}},
    expectedResult: [2],
    msg: "Rank not that of infinity returns numeric, counts infinity as number"
});

testWithProject({
    coll: coll,
    doc: {x: 10, x1: 5, x2: 27},
    percentileSpec: {$percentile: {p: [1], input: ["$x", "$x1", "$x2"], method: "approximate"}},
    expectedResult: [27],
    msg: "Maximum percentile"
});

testWithProject({
    coll: coll,
    doc: {x: 0, x1: 1, x2: 2},
    percentileSpec:
        {$percentile: {p: [0.5, 0.9, 0.1], input: ["$x", "$x1", "$x2"], method: "approximate"}},
    expectedResult: [1, 2, 0],
    msg: "Multiple percentiles when input field is passed as an array"
});

/**
 * Tests with input as a single expression.
 */
testWithProject({
    coll: coll,
    doc: {x: 0, x1: 1, x2: 2},
    percentileSpec: {$percentile: {p: [0.5, 0.9, 0.1], input: "$x", method: "approximate"}},
    expectedResult: [0, 0, 0],
    msg: "Multiple percentiles when single input expression resolves to a numeric scalar"
});

testWithProject({
    coll: coll,
    doc: {x: 0, x1: "non-numeric", x2: 2},
    percentileSpec: {$percentile: {p: [0.5, 0.9, 0.1], input: "$x1", method: "approximate"}},
    expectedResult: [null, null, null],
    msg: "Multiple percentiles when single input expression resolves to a non-numeric scalar"
});

testWithProject({
    coll: coll,
    doc: {x: [2, 1], y: 3},
    percentileSpec: {
        $percentile: {
            p: [0.5, 0.9],
            input: {$concatArrays: ["$x", [{$add: [42, "$y"]}]]},
            method: "approximate"
        }
    },
    expectedResult: [2, 42 + 3],
    msg: "Input as complex expression"
});

testWithProject({
    coll: coll,
    doc: {x: [2, 3, 1]},
    percentileSpec: {$percentile: {p: "$$ps", input: "$x", method: "approximate"}},
    letSpec: {ps: [0.1, 0.5, 0.9]},
    expectedResult: [1, 2, 3],
    msg: "'p' specified as a variable"
});

/**
 * 'rand()' generates a uniform distribution from [0.0, 1.0] so we can check accuracy of the result
 * in terms of values rather than in terms of rank.
 */
testLargeInput(coll, "approximate");

testLargeNonNumericInput(coll, "approximate");
