[
    {
        "cases": [
            {
                "expression": "abs(foo)",
                "result": 1
            },
            {
                "expression": "abs(foo)",
                "result": 1
            },
            {
                "error": "invalid-type",
                "expression": "abs(str)"
            },
            {
                "expression": "abs(array[1])",
                "result": 3
            },
            {
                "expression": "abs(array[1])",
                "result": 3
            },
            {
                "error": "invalid-type",
                "expression": "abs(`false`)"
            },
            {
                "expression": "abs(`-24`)",
                "result": 24
            },
            {
                "expression": "abs(`-24`)",
                "result": 24
            },
            {
                "error": "invalid-arity",
                "expression": "abs(`1`, `2`)"
            },
            {
                "error": "invalid-arity",
                "expression": "abs()"
            },
            {
                "error": "unknown-function",
                "expression": "unknown_function(`1`, `2`)"
            },
            {
                "expression": "avg(numbers)",
                "result": 2.75
            },
            {
                "error": "invalid-type",
                "expression": "avg(array)"
            },
            {
                "error": "invalid-type",
                "expression": "avg('abc')"
            },
            {
                "error": "invalid-type",
                "expression": "avg(foo)"
            },
            {
                "error": "invalid-type",
                "expression": "avg(@)"
            },
            {
                "error": "invalid-type",
                "expression": "avg(strings)"
            },
            {
                "expression": "ceil(`1.2`)",
                "result": 2
            },
            {
                "expression": "ceil(decimals[0])",
                "result": 2
            },
            {
                "expression": "ceil(decimals[1])",
                "result": 2
            },
            {
                "expression": "ceil(decimals[2])",
                "result": -1
            },
            {
                "error": "invalid-type",
                "expression": "ceil('string')"
            },
            {
                "expression": "contains('abc', 'a')",
                "result": true
            },
            {
                "expression": "contains('abc', 'd')",
                "result": false
            },
            {
                "error": "invalid-type",
                "expression": "contains(`false`, 'd')"
            },
            {
                "expression": "contains(strings, 'a')",
                "result": true
            },
            {
                "expression": "contains(decimals, `1.2`)",
                "result": true
            },
            {
                "expression": "contains(decimals, `false`)",
                "result": false
            },
            {
                "expression": "ends_with(str, 'r')",
                "result": true
            },
            {
                "expression": "ends_with(str, 'tr')",
                "result": true
            },
            {
                "expression": "ends_with(str, 'Str')",
                "result": true
            },
            {
                "expression": "ends_with(str, 'SStr')",
                "result": false
            },
            {
                "expression": "ends_with(str, 'foo')",
                "result": false
            },
            {
                "error": "invalid-type",
                "expression": "ends_with(str, `0`)"
            },
            {
                "expression": "floor(`1.2`)",
                "result": 1
            },
            {
                "error": "invalid-type",
                "expression": "floor('string')"
            },
            {
                "expression": "floor(decimals[0])",
                "result": 1
            },
            {
                "expression": "floor(foo)",
                "result": -1
            },
            {
                "error": "invalid-type",
                "expression": "floor(str)"
            },
            {
                "expression": "length('abc')",
                "result": 3
            },
            {
                "expression": "length('\u2713foo')",
                "result": 4
            },
            {
                "expression": "length('')",
                "result": 0
            },
            {
                "expression": "length(@)",
                "result": 12
            },
            {
                "expression": "length(strings[0])",
                "result": 1
            },
            {
                "expression": "length(str)",
                "result": 3
            },
            {
                "expression": "length(array)",
                "result": 6
            },
            {
                "expression": "length(objects)",
                "result": 2
            },
            {
                "error": "invalid-type",
                "expression": "length(`false`)"
            },
            {
                "error": "invalid-type",
                "expression": "length(foo)"
            },
            {
                "expression": "length(strings[0])",
                "result": 1
            },
            {
                "expression": "max(numbers)",
                "result": 5
            },
            {
                "expression": "max(decimals)",
                "result": 1.2
            },
            {
                "expression": "max(strings)",
                "result": "c"
            },
            {
                "error": "invalid-type",
                "expression": "max(abc)"
            },
            {
                "error": "invalid-type",
                "expression": "max(array)"
            },
            {
                "expression": "max(decimals)",
                "result": 1.2
            },
            {
                "expression": "max(empty_list)",
                "result": null
            },
            {
                "expression": "merge(`{}`)",
                "is_deeply": 1,
                "result": {}
            },
            {
                "expression": "merge(`{}`, `{}`)",
                "is_deeply": 1,
                "result": {}
            },
            {
                "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)",
                "is_deeply": 1,
                "result": {
                    "a": 1,
                    "b": 2
                }
            },
            {
                "expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)",
                "is_deeply": 1,
                "result": {
                    "a": 2
                }
            },
            {
                "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)",
                "is_deeply": 1,
                "result": {
                    "a": 2,
                    "b": 2,
                    "c": 3,
                    "d": 4
                }
            },
            {
                "expression": "min(numbers)",
                "result": -1
            },
            {
                "expression": "min(decimals)",
                "result": -1.5
            },
            {
                "error": "invalid-type",
                "expression": "min(abc)"
            },
            {
                "error": "invalid-type",
                "expression": "min(array)"
            },
            {
                "expression": "min(empty_list)",
                "result": null
            },
            {
                "expression": "min(decimals)",
                "result": -1.5
            },
            {
                "expression": "min(strings)",
                "result": "a"
            },
            {
                "expression": "type('abc')",
                "result": "string"
            },
            {
                "expression": "type(`1.0`)",
                "result": "number"
            },
            {
                "expression": "type(`2`)",
                "result": "number"
            },
            {
                "expression": "type(`true`)",
                "result": "boolean"
            },
            {
                "expression": "type(`false`)",
                "result": "boolean"
            },
            {
                "expression": "type(`null`)",
                "result": "null"
            },
            {
                "expression": "type(`[0]`)",
                "result": "array"
            },
            {
                "expression": "type(`{\"a\": \"b\"}`)",
                "result": "object"
            },
            {
                "expression": "type(@)",
                "result": "object"
            },
            {
                "expression": "sort(keys(objects))",
                "is_deeply": 1,
                "result": [
                    "bar",
                    "foo"
                ]
            },
            {
                "error": "invalid-type",
                "expression": "keys(foo)"
            },
            {
                "error": "invalid-type",
                "expression": "keys(strings)"
            },
            {
                "error": "invalid-type",
                "expression": "keys(`false`)"
            },
            {
                "expression": "sort(values(objects))",
                "is_deeply": 1,
                "result": [
                    "bar",
                    "baz"
                ]
            },
            {
                "expression": "keys(empty_hash)",
                "is_deeply": 1,
                "result": []
            },
            {
                "error": "invalid-type",
                "expression": "values(foo)"
            },
            {
                "expression": "join(', ', strings)",
                "result": "a, b, c"
            },
            {
                "expression": "join(', ', strings)",
                "result": "a, b, c"
            },
            {
                "expression": "join(',', `[\"a\", \"b\"]`)",
                "result": "a,b"
            },
            {
                "error": "invalid-type",
                "expression": "join(',', `[\"a\", 0]`)"
            },
            {
                "error": "invalid-type",
                "expression": "join(', ', str)"
            },
            {
                "expression": "join('|', strings)",
                "result": "a|b|c"
            },
            {
                "error": "invalid-type",
                "expression": "join(`2`, strings)"
            },
            {
                "error": "invalid-type",
                "expression": "join('|', decimals)"
            },
            {
                "expression": "join('|', decimals[].to_string(@))",
                "result": "1.01|1.2|-1.5"
            },
            {
                "expression": "join('|', empty_list)",
                "result": ""
            },
            {
                "expression": "reverse(numbers)",
                "is_deeply": 1,
                "result": [
                    5,
                    4,
                    3,
                    -1
                ]
            },
            {
                "expression": "reverse(array)",
                "is_deeply": 1,
                "result": [
                    "100",
                    "a",
                    5,
                    4,
                    3,
                    -1
                ]
            },
            {
                "expression": "reverse(`[]`)",
                "is_deeply": 1,
                "result": []
            },
            {
                "expression": "reverse('')",
                "result": ""
            },
            {
                "expression": "reverse('hello world')",
                "result": "dlrow olleh"
            },
            {
                "expression": "starts_with(str, 'S')",
                "result": true
            },
            {
                "expression": "starts_with(str, 'St')",
                "result": true
            },
            {
                "expression": "starts_with(str, 'Str')",
                "result": true
            },
            {
                "expression": "starts_with(str, 'String')",
                "result": false
            },
            {
                "error": "invalid-type",
                "expression": "starts_with(str, `0`)"
            },
            {
                "expression": "sum(numbers)",
                "result": 11
            },
            {
                "expression": "sum(decimals)",
                "result": 0.71
            },
            {
                "error": "invalid-type",
                "expression": "sum(array)"
            },
            {
                "expression": "sum(array[].to_number(@))",
                "result": 111
            },
            {
                "expression": "sum(`[]`)",
                "result": 0
            },
            {
                "expression": "to_array('foo')",
                "is_deeply": 1,
                "result": [
                    "foo"
                ]
            },
            {
                "expression": "to_array(`0`)",
                "is_deeply": 1,
                "result": [
                    0
                ]
            },
            {
                "expression": "to_array(objects)",
                "is_deeply": 1,
                "result": [
                    {
                        "bar": "baz",
                        "foo": "bar"
                    }
                ]
            },
            {
                "expression": "to_array(`[1, 2, 3]`)",
                "is_deeply": 1,
                "result": [
                    1,
                    2,
                    3
                ]
            },
            {
                "expression": "to_array(false)",
                "is_deeply": 1,
                "result": [
                    false
                ]
            },
            {
                "expression": "to_string('foo')",
                "result": "foo"
            },
            {
                "expression": "to_string(`1.2`)",
                "result": "1.2"
            },
            {
                "expression": "to_string(`[0, 1]`)",
                "result": "[0,1]"
            },
            {
                "expression": "to_number('1.0')",
                "result": 1.0
            },
            {
                "expression": "to_number('1.1')",
                "result": 1.1
            },
            {
                "expression": "to_number('4')",
                "result": 4
            },
            {
                "expression": "to_number('notanumber')",
                "result": null
            },
            {
                "expression": "to_number(`false`)",
                "result": null
            },
            {
                "expression": "to_number(`null`)",
                "result": null
            },
            {
                "expression": "to_number(`[0]`)",
                "result": null
            },
            {
                "expression": "to_number(`{\"foo\": 0}`)",
                "result": null
            },
            {
                "error": "syntax",
                "expression": "\"to_string\"(`1.0`)"
            },
            {
                "expression": "sort(numbers)",
                "is_deeply": 1,
                "result": [
                    -1,
                    3,
                    4,
                    5
                ]
            },
            {
                "expression": "sort(strings)",
                "is_deeply": 1,
                "result": [
                    "a",
                    "b",
                    "c"
                ]
            },
            {
                "expression": "sort(decimals)",
                "is_deeply": 1,
                "result": [
                    -1.5,
                    1.01,
                    1.2
                ]
            },
            {
                "error": "invalid-type",
                "expression": "sort(array)"
            },
            {
                "error": "invalid-type",
                "expression": "sort(abc)"
            },
            {
                "expression": "sort(empty_list)",
                "is_deeply": 1,
                "result": []
            },
            {
                "error": "invalid-type",
                "expression": "sort(@)"
            },
            {
                "expression": "not_null(unknown_key, str)",
                "result": "Str"
            },
            {
                "expression": "not_null(unknown_key, foo.bar, empty_list, str)",
                "is_deeply": 1,
                "result": []
            },
            {
                "expression": "not_null(unknown_key, null_key, empty_list, str)",
                "is_deeply": 1,
                "result": []
            },
            {
                "expression": "not_null(all, expressions, are_null)",
                "result": null
            },
            {
                "error": "invalid-arity",
                "expression": "not_null()"
            },
            {
                "comment": "function projection on single arg function",
                "expression": "numbers[].to_string(@)",
                "is_deeply": 1,
                "result": [
                    "-1",
                    "3",
                    "4",
                    "5"
                ]
            },
            {
                "comment": "function projection on single arg function",
                "expression": "array[].to_number(@)",
                "is_deeply": 1,
                "result": [
                    -1,
                    3,
                    4,
                    5,
                    100
                ]
            }
        ],
        "given": {
            "array": [
                -1,
                3,
                4,
                5,
                "a",
                "100"
            ],
            "decimals": [
                1.01,
                1.2,
                -1.5
            ],
            "empty_hash": {},
            "empty_list": [],
            "false": false,
            "foo": -1,
            "null_key": null,
            "numbers": [
                -1,
                3,
                4,
                5
            ],
            "objects": {
                "bar": "baz",
                "foo": "bar"
            },
            "str": "Str",
            "strings": [
                "a",
                "b",
                "c"
            ],
            "zero": 0
        }
    },
    {
        "cases": [
            {
                "comment": "function projection on variadic function",
                "expression": "foo[].not_null(f, e, d, c, b, a)",
                "is_deeply": 1,
                "result": [
                    "b",
                    "c",
                    "d",
                    "e",
                    "f"
                ]
            }
        ],
        "given": {
            "foo": [
                {
                    "a": "a",
                    "b": "b"
                },
                {
                    "b": "b",
                    "c": "c"
                },
                {
                    "c": "c",
                    "d": "d"
                },
                {
                    "d": "d",
                    "e": "e"
                },
                {
                    "e": "e",
                    "f": "f"
                }
            ]
        }
    },
    {
        "cases": [
            {
                "comment": "sort by field expression",
                "expression": "sort_by(people, &age)",
                "is_deeply": 1,
                "result": [
                    {
                        "age": 10,
                        "age_str": "10",
                        "bool": true,
                        "name": 3
                    },
                    {
                        "age": 20,
                        "age_str": "20",
                        "bool": true,
                        "extra": "foo",
                        "name": "a"
                    },
                    {
                        "age": 30,
                        "age_str": "30",
                        "bool": true,
                        "name": "c"
                    },
                    {
                        "age": 40,
                        "age_str": "40",
                        "bool": false,
                        "extra": "bar",
                        "name": "b"
                    },
                    {
                        "age": 50,
                        "age_str": "50",
                        "bool": false,
                        "name": "d"
                    }
                ]
            },
            {
                "expression": "sort_by(people, &age_str)",
                "is_deeply": 1,
                "result": [
                    {
                        "age": 10,
                        "age_str": "10",
                        "bool": true,
                        "name": 3
                    },
                    {
                        "age": 20,
                        "age_str": "20",
                        "bool": true,
                        "extra": "foo",
                        "name": "a"
                    },
                    {
                        "age": 30,
                        "age_str": "30",
                        "bool": true,
                        "name": "c"
                    },
                    {
                        "age": 40,
                        "age_str": "40",
                        "bool": false,
                        "extra": "bar",
                        "name": "b"
                    },
                    {
                        "age": 50,
                        "age_str": "50",
                        "bool": false,
                        "name": "d"
                    }
                ]
            },
            {
                "comment": "sort by function expression",
                "expression": "sort_by(people, &to_number(age_str))",
                "is_deeply": 1,
                "result": [
                    {
                        "age": 10,
                        "age_str": "10",
                        "bool": true,
                        "name": 3
                    },
                    {
                        "age": 20,
                        "age_str": "20",
                        "bool": true,
                        "extra": "foo",
                        "name": "a"
                    },
                    {
                        "age": 30,
                        "age_str": "30",
                        "bool": true,
                        "name": "c"
                    },
                    {
                        "age": 40,
                        "age_str": "40",
                        "bool": false,
                        "extra": "bar",
                        "name": "b"
                    },
                    {
                        "age": 50,
                        "age_str": "50",
                        "bool": false,
                        "name": "d"
                    }
                ]
            },
            {
                "comment": "function projection on sort_by function",
                "expression": "sort_by(people, &age)[].name",
                "is_deeply": 1,
                "result": [
                    3,
                    "a",
                    "c",
                    "b",
                    "d"
                ]
            },
            {
                "error": "invalid-type",
                "expression": "sort_by(people, &extra)"
            },
            {
                "error": "invalid-type",
                "expression": "sort_by(people, &bool)"
            },
            {
                "error": "invalid-type",
                "expression": "sort_by(people, &name)"
            },
            {
                "error": "invalid-type",
                "expression": "sort_by(people, name)"
            },
            {
                "expression": "sort_by(people, &age)[].extra",
                "is_deeply": 1,
                "result": [
                    "foo",
                    "bar"
                ]
            },
            {
                "expression": "sort_by(`[]`, &age)",
                "is_deeply": 1,
                "result": []
            },
            {
                "expression": "max_by(people, &age)",
                "is_deeply": 1,
                "result": {
                    "age": 50,
                    "age_str": "50",
                    "bool": false,
                    "name": "d"
                }
            },
            {
                "expression": "max_by(people, &age_str)",
                "is_deeply": 1,
                "result": {
                    "age": 50,
                    "age_str": "50",
                    "bool": false,
                    "name": "d"
                }
            },
            {
                "error": "invalid-type",
                "expression": "max_by(people, &bool)"
            },
            {
                "error": "invalid-type",
                "expression": "max_by(people, &extra)"
            },
            {
                "expression": "max_by(people, &to_number(age_str))",
                "is_deeply": 1,
                "result": {
                    "age": 50,
                    "age_str": "50",
                    "bool": false,
                    "name": "d"
                }
            },
            {
                "expression": "min_by(people, &age)",
                "is_deeply": 1,
                "result": {
                    "age": 10,
                    "age_str": "10",
                    "bool": true,
                    "name": 3
                }
            },
            {
                "expression": "min_by(people, &age_str)",
                "is_deeply": 1,
                "result": {
                    "age": 10,
                    "age_str": "10",
                    "bool": true,
                    "name": 3
                }
            },
            {
                "error": "invalid-type",
                "expression": "min_by(people, &bool)"
            },
            {
                "error": "invalid-type",
                "expression": "min_by(people, &extra)"
            },
            {
                "expression": "min_by(people, &to_number(age_str))",
                "is_deeply": 1,
                "result": {
                    "age": 10,
                    "age_str": "10",
                    "bool": true,
                    "name": 3
                }
            }
        ],
        "given": {
            "people": [
                {
                    "age": 20,
                    "age_str": "20",
                    "bool": true,
                    "extra": "foo",
                    "name": "a"
                },
                {
                    "age": 40,
                    "age_str": "40",
                    "bool": false,
                    "extra": "bar",
                    "name": "b"
                },
                {
                    "age": 30,
                    "age_str": "30",
                    "bool": true,
                    "name": "c"
                },
                {
                    "age": 50,
                    "age_str": "50",
                    "bool": false,
                    "name": "d"
                },
                {
                    "age": 10,
                    "age_str": "10",
                    "bool": true,
                    "name": 3
                }
            ]
        }
    },
    {
        "cases": [
            {
                "comment": "stable sort order",
                "expression": "sort_by(people, &age)",
                "is_deeply": 1,
                "result": [
                    {
                        "age": 10,
                        "order": "1"
                    },
                    {
                        "age": 10,
                        "order": "2"
                    },
                    {
                        "age": 10,
                        "order": "3"
                    },
                    {
                        "age": 10,
                        "order": "4"
                    },
                    {
                        "age": 10,
                        "order": "5"
                    },
                    {
                        "age": 10,
                        "order": "6"
                    },
                    {
                        "age": 10,
                        "order": "7"
                    },
                    {
                        "age": 10,
                        "order": "8"
                    },
                    {
                        "age": 10,
                        "order": "9"
                    },
                    {
                        "age": 10,
                        "order": "10"
                    },
                    {
                        "age": 10,
                        "order": "11"
                    }
                ]
            }
        ],
        "given": {
            "people": [
                {
                    "age": 10,
                    "order": "1"
                },
                {
                    "age": 10,
                    "order": "2"
                },
                {
                    "age": 10,
                    "order": "3"
                },
                {
                    "age": 10,
                    "order": "4"
                },
                {
                    "age": 10,
                    "order": "5"
                },
                {
                    "age": 10,
                    "order": "6"
                },
                {
                    "age": 10,
                    "order": "7"
                },
                {
                    "age": 10,
                    "order": "8"
                },
                {
                    "age": 10,
                    "order": "9"
                },
                {
                    "age": 10,
                    "order": "10"
                },
                {
                    "age": 10,
                    "order": "11"
                }
            ]
        }
    },
    {
        "cases": [
            {
                "expression": "map(&a, people)",
                "is_deeply": 1,
                "result": [
                    10,
                    10,
                    10,
                    10,
                    10,
                    10,
                    10,
                    10,
                    10
                ]
            },
            {
                "expression": "map(&c, people)",
                "is_deeply": 1,
                "result": [
                    "z",
                    null,
                    null,
                    "z",
                    null,
                    null,
                    "z",
                    null,
                    null
                ]
            },
            {
                "error": "invalid-type",
                "expression": "map(&a, badkey)"
            },
            {
                "expression": "map(&foo, empty)",
                "is_deeply": 1,
                "result": []
            }
        ],
        "given": {
            "empty": [],
            "people": [
                {
                    "a": 10,
                    "b": 1,
                    "c": "z"
                },
                {
                    "a": 10,
                    "b": 2,
                    "c": null
                },
                {
                    "a": 10,
                    "b": 3
                },
                {
                    "a": 10,
                    "b": 4,
                    "c": "z"
                },
                {
                    "a": 10,
                    "b": 5,
                    "c": null
                },
                {
                    "a": 10,
                    "b": 6
                },
                {
                    "a": 10,
                    "b": 7,
                    "c": "z"
                },
                {
                    "a": 10,
                    "b": 8,
                    "c": null
                },
                {
                    "a": 10,
                    "b": 9
                }
            ]
        }
    },
    {
        "cases": [
            {
                "expression": "map(&foo.bar, array)",
                "is_deeply": 1,
                "result": [
                    "yes1",
                    "yes2",
                    null
                ]
            },
            {
                "expression": "map(&foo1.bar, array)",
                "is_deeply": 1,
                "result": [
                    null,
                    null,
                    "no"
                ]
            },
            {
                "expression": "map(&foo.bar.baz, array)",
                "is_deeply": 1,
                "result": [
                    null,
                    null,
                    null
                ]
            }
        ],
        "given": {
            "array": [
                {
                    "foo": {
                        "bar": "yes1"
                    }
                },
                {
                    "foo": {
                        "bar": "yes2"
                    }
                },
                {
                    "foo1": {
                        "bar": "no"
                    }
                }
            ]
        }
    },
    {
        "cases": [
            {
                "expression": "map(&[], array)",
                "is_deeply": 1,
                "result": [
                    [
                        1,
                        2,
                        3,
                        4
                    ],
                    [
                        5,
                        6,
                        7,
                        8,
                        9
                    ]
                ]
            }
        ],
        "given": {
            "array": [
                [
                    1,
                    2,
                    3,
                    [
                        4
                    ]
                ],
                [
                    5,
                    6,
                    7,
                    [
                        8,
                        9
                    ]
                ]
            ]
        }
    }
]