Some aggregate functions are commutative with some arithmetic expressions.

```
avg(x + const) = avg(x) + const
```

This is applicable for aggregate functions: `sum`, `avg`, `min`, `max` and operations: `+`, `-`, `*`, `/` (but not for `intDiv` and `modulo`).

In case of integer overflow it either works correctly (for `sum`) or we may assume that integer overflow is implementation specific behaviour and apply optimization under this assumption.

**Possible implementation**

AST rewrite with InDepthNodeVisitor.
Should not rewrite if expression has alias.
{
    static const std::unordered_map<String, std::unordered_set<String>> supported = {
        { "sum", { "multiply", "divide" } },
        { "min", { "multiply", "divide", "plus", "minus" } },
        { "max", { "multiply", "divide", "plus", "minus" } }
    };
    const ASTFunction * child_func = getInternalFunction(func);
    if (!child_func || !child_func->arguments || child_func->arguments->children.size() != 2 ||
        !supported.count(func.name) || !supported.find(func.name)->second.count(child_func->name))
        return {};
    
    if (!func.tryGetAlias().empty() || !child_func->tryGetAlias().empty())
        return {};
    const auto & child_func_args = child_func->arguments->children;
    const auto * first_literal = child_func_args[0]->as<ASTLiteral>();
    const auto * second_literal = child_func_args[1]->as<ASTLiteral>();
    ASTPtr optimized_ast;
    if (first_literal && !second_literal)
    {
        
        if (child_func->name == "divide")
            return {};
        const String & new_name = changeNameIfNeeded(func.name, child_func->name, *first_literal);
        optimized_ast = exchangeExtractFirstArgument(new_name, *child_func);
    }
    else if (second_literal) 
    {
        const String & new_name = changeNameIfNeeded(func.name, child_func->name, *second_literal);