public void transformFloat(RowWindow rowWindow, PointCollector collector) throws IOException {
    if (rowWindow.windowSize() > 0) { // else empty window do nothing
      int index = 0;
      int size = rowWindow.windowSize();
      int firstValueIndex = -1;
      float firstValue = 0;

      for (; index < size; index++) {
        if (!rowWindow.getRow(index).isNull(0)) {
          firstValueIndex = index;
          firstValue = rowWindow.getRow(index).getFloat(0);
          break;
        }
      }

      if (firstValueIndex != -1) { // else empty window do nothing
        int lastValueIndex = firstValueIndex;
        float lastValue = firstValue;
        int minValueIndex = firstValueIndex;
        float minValue = firstValue;
        int maxValueIndex = firstValueIndex;
        float maxValue = firstValue;

        for (; index < size; index++) {
          if (!rowWindow.getRow(index).isNull(0)) {
            lastValueIndex = index;
            lastValue = rowWindow.getRow(index).getFloat(0);
            if (lastValue < minValue) {
              minValue = lastValue;
              minValueIndex = index;
            }
            if (lastValue > maxValue) {
              maxValue = lastValue;
              maxValueIndex = index;
            }
          }
        }
        // first value
        collector.putFloat(rowWindow.getRow(firstValueIndex).getTime(), firstValue);
        // min and max value if not duplicate
        // if min/max value is equal to first/last value, we keep first/last value
        int smallerIndex = Math.min(minValueIndex, maxValueIndex);
        int largerIndex = Math.max(minValueIndex, maxValueIndex);
        if (smallerIndex > firstValueIndex
            && rowWindow.getRow(smallerIndex).getFloat(0) != lastValue) {
          collector.putFloat(
              rowWindow.getRow(smallerIndex).getTime(), rowWindow.getRow(smallerIndex).getFloat(0));
        }
        if (largerIndex > smallerIndex && rowWindow.getRow(largerIndex).getFloat(0) != lastValue) {
          collector.putFloat(
              rowWindow.getRow(largerIndex).getTime(), rowWindow.getRow(largerIndex).getFloat(0));
        }
        // last value
        if (lastValueIndex > firstValueIndex) {
          collector.putFloat(rowWindow.getRow(lastValueIndex).getTime(), lastValue);
        }
      }
    }
  }
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