**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.
_mm_and_si128(_mm_cmpgt_epi8(haystack, alpha_lower_begin), _mm_cmplt_epi8(haystack, alpha_lower_end))),
                _mm_and_si128(_mm_cmpgt_epi8(haystack, alpha_upper_begin), _mm_cmplt_epi8(haystack, alpha_upper_end))));
#endif
        if (result_bitmask == 0)
        {
            if (*token_len != 0)
                
                return true;
            *pos += haystack_length;
            continue;
        }
        const auto token_start_pos_in_current_haystack = getTrailingZeroBitsUnsafe(result_bitmask);
        if (*token_len == 0)
            
            *token_start = *pos + token_start_pos_in_current_haystack;
        else if (token_start_pos_in_current_haystack != 0)
            
            return true;
        const auto token_bytes_in_current_haystack = getTrailingZeroBitsUnsafe(~(result_bitmask >> token_start_pos_in_current_haystack));
        *token_len += token_bytes_in_current_haystack;
        *pos += token_start_pos_in_current_haystack + token_bytes_in_current_haystack;
        if (token_start_pos_in_current_haystack + token_bytes_in_current_haystack == haystack_length)
            
            continue;
        break;
#else
        if (isASCII(data[*pos]) && !isAlphaNumericASCII(data[*pos]))
        {
            
            if (*token_len > 0)