class DefaultLineChartReducer implements LineChartReducer {

  @NotNull
  @Override
  public Path2D reducePath(@NotNull Path2D path, @NotNull LineConfig config) {
    if (path.getCurrentPoint() == null) {
      return path;
    }

    Path2D resultPath = new Path2D.Float();
    float[] coords = new float[PATH_ITERATOR_COORDS_COUNT];
    float pixel = -1;
    float minX = -1, minY = -1;
    float maxX = -1, maxY = -1;
    float curX = -1, curY = -1;
    int minIndex = -1, maxIndex = -1;
    int curIndex = 0;

    PathIterator iterator = path.getPathIterator(null);
    while (!iterator.isDone()) {
      int segType = iterator.currentSegment(coords);
      assert segType == PathIterator.SEG_MOVETO || segType == PathIterator.SEG_LINETO;

      float previousX = curX;
      float previousY = curY;
      curX = coords[0];
      curY = coords[1];

      if (curIndex > 0 && curX < previousX) {
        // This can happen only for a filled line
        break;
      }

      if (curIndex == 0 || curX >= pixel) {
        // We entered into a new pixel

        if (curIndex > 0) {
          // Add min and max points from the previous pixel
          addMinMaxPoints(resultPath, config, minIndex, minX, minY, maxIndex, maxX, maxY);

          // Add the last point from the previous pixel
          addToResultPath(resultPath, config, previousX, previousY);
        }

        pixel = (float)Math.floor(curX) + 1;
        minX = maxX = curX;
        minY = maxY = curY;
        minIndex = maxIndex = curIndex;

        // Add the first point from the current pixel
        addToResultPath(resultPath, config, curX, curY);
      } else {
        // We are in the same pixel

        if (minY > curY) {
          minIndex = curIndex;
          minX = curX;
          minY = curY;
        }

        if (maxY <= curY) {
          maxIndex = curIndex;
          maxX = curX;
          maxY = curY;
        }
      }

      iterator.next();
      curIndex++;
    }

    addMinMaxPoints(resultPath, config, minIndex, minX, minY, maxIndex, maxX, maxY);
    addToResultPath(resultPath, config, curX, curY);

    if (config.isStepped()) {
      // The last point won't be added if Y value is the same with previous point, so let's add it
      if (resultPath.getCurrentPoint() == null || equals((float)resultPath.getCurrentPoint().getY(), curY)) {
        addToPath(resultPath, curX, curY);
      }
    }
    return resultPath;
  }

}