**Use case**

I want to estimate better parameters for fulltext (`ngrambf_v1`) index.

**Describe the solution you'd like**

Implement it as SQL function:

`ngrams(string, N)`

that returns array of FixedString.

**Describe alternatives you've considered**

It can be done with less efficient and more cumbersome way:

```
WITH 3 AS n, extractAll(text, '.') AS chars 
SELECT 
  arrayMap(x -> arrayStringConcat(x), 
    arrayFilter(x -> length(x) = n, 
      arrayMap(x, i -> arraySlice(chars, i, n), 
        chars, arrayEnumerate(chars)))) AS ngrams
```

**Additional context**

`ngramsUTF8` can also be implemented to extract ngrams of Unicode code points.
},
        {
            "Short single token surruonded by whitespace",
            "\t\vfoo\n\r",
            {"foo"}
        }
    })
);
INSTANTIATE_TEST_SUITE_P(UTF8,
    SplitTokenExtractorTest,
    ::testing::ValuesIn(std::initializer_list<SplitTokenExtractorTestCase>{
        {
            "Single token with mixed ASCII and UTF-8 chars",
            "abc\u0442" "123\u0447XYZ\u043A",
            {"abc\u0442" "123\u0447XYZ\u043A"}
        },
        {
            "Multiple UTF-8 tokens",
            "\u043F\u0440\u0438\u0432\u0435\u0442, \u043C\u0438\u0440!",
            {"\u043F\u0440\u0438\u0432\u0435\u0442", "\u043C\u0438\u0440"}
        },
    })
);
INSTANTIATE_TEST_SUITE_P(MultipleTokens,
    SplitTokenExtractorTest,
    ::testing::ValuesIn(std::initializer_list<SplitTokenExtractorTestCase>{
        {
            "Multiple tokens separated by whitespace",
            "\nabc 123\tXYZ\r",
            {