REF: Separate window bounds calculation from aggregation functions
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`

Pre-req for https://github.com/pandas-dev/pandas/issues/28987

Currently many of the aggregation functions in `window.pyx` follow the form:

```
def roll_func(values, window, minp, N, closed):
    # calculate window bounds _and_ validate arguments
    start, end, ... = get_window_bounds(values, window, minp, N, ...)
    for i in range(values):
        s = start[i]
        ....
```

This PR refactors out the window bound calculation into `window_indexer.pyx` and validation so the aggregation functions can be of the form:

```
def roll_func(values, start, end, minp):
    for i in range(values):
        s = start[i]
        ....
```

The methods therefore in `rolling.py` now have the following pattern:

1. Fetch the correct cython aggregation function (whether the window is fixed or variable), and prep it with kwargs if needed
2. Compute the `start` and `end` window bounds from functionality in `window_indexer.pyx`
3. Pass in the `values`, `start`, `end`, `min periods` into the aggregation function.