@Override
  public void validate(UDFParameterValidator validator) throws UDFException {
    validator
        .validateInputSeriesNumber(1)
        .validateInputSeriesDataType(0, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE);

    if (!validator.getParameters().hasAttribute(WINDOW_SIZE_KEY)
        && !validator.getParameters().hasAttribute(TIME_INTERVAL_KEY)) {
      throw new UDFParameterNotValidException(
          String.format(
              "attribute \"%s\"/\"%s\" is required but was not provided.",
              WINDOW_SIZE_KEY, TIME_INTERVAL_KEY));
    }
    if (validator.getParameters().hasAttribute(WINDOW_SIZE_KEY)
        && validator.getParameters().hasAttribute(TIME_INTERVAL_KEY)) {
      throw new UDFParameterNotValidException(
          String.format(
              "use attribute \"%s\" or \"%s\" only one at a time.",
              WINDOW_SIZE_KEY, TIME_INTERVAL_KEY));
    }
    if (validator.getParameters().hasAttribute(WINDOW_SIZE_KEY)) {
      accessStrategy = AccessStrategy.SIZE_WINDOW;
    } else {
      accessStrategy = AccessStrategy.TIME_WINDOW;
    }

    dataType =
        UDFDataTypeTransformer.transformToTsDataType(validator.getParameters().getDataType(0));
  }
For each sliding window, M4 returns the first, last, bottom, top points. The window can be
controlled by either point size or time interval length. The aggregated points in the output
series has been sorted and deduplicated.

<p>SlidingSizeWindow usage Example: "select M4(s1,'windowSize'='10','slidingStep'='10') from
root.vehicle.d1" (windowSize is required, slidingStep is optional.)

<p>SlidingTimeWindow usage Example: "select
M4(s1,'timeInterval'='25','slidingStep'='25','displayWindowBegin'='0','displayWindowEnd'='100')
from root.vehicle.d1" (timeInterval is required, slidingStep/displayWindowBegin/displayWindowEnd
are optional.)
iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFM4.java