private PatternCalculationResult calculatePointsMatch(
      List<Section> querySections, List<Section> matchedSections, boolean partialQuery) {
    if (PatternMatchConfig.CHECK_QUERY_COMPATIBILITY) {
      if (!areCompatibleSections(querySections, matchedSections, !partialQuery)) {
        return null;
      }
    } else {
      if (querySections.size() > matchedSections.size()) {
        matchedSections = expandSections(matchedSections, querySections.size());
      } else if (querySections.size() < matchedSections.size()) {
        matchedSections = reduceSections(matchedSections, querySections.size());
      }
      if (matchedSections == null) {
        return null;
      }
      if (!areCompatibleSections(querySections, matchedSections, !partialQuery)) {
        return null;
      }
    }

    double centroidsDifference;
    int i, si;

    Bounds matchedSecBounds =
        getBounds(
            matchedSections,
            (matchedSections.size() > 2 ? 1 : 0),
            matchedSections.size() - (matchedSections.size() > 2 ? 2 : 1));

    Bounds queryBounds =
        getBounds(
            querySections,
            (querySections.size() > 2 ? 1 : 0),
            querySections.size() - (querySections.size() > 2 ? 2 : 1));

    double subSequenceScaleFactorX =
        (matchedSecBounds.getMaxX() - matchedSecBounds.getMinX())
            / (queryBounds.getMaxX() - queryBounds.getMinX());
    double subSequenceScaleFactorY =
        (matchedSecBounds.getMaxY() - matchedSecBounds.getMinY())
            / (queryBounds.getMaxY() - queryBounds.getMinY());

    List<Point> matchedPoints = new ArrayList<>();
    double pointDifferencesCost = 0;
    double rescalingCost = 0;
    double sum = 0;
    double num = 0;

    for (si = 0; si < querySections.size(); si++) {
      SectionCalculation dataSect = new SectionCalculation();
      SectionCalculation querySect = new SectionCalculation();
      sum = 0;
      num = 0;

      querySect.setPoints(querySections.get(si).getPoints());
      querySect.setWidth(calcWidth(querySect.getPoints()));
      querySect.setHeight(calcHeight(querySect.getPoints()));
      if (querySect.getHeight() == 0) {
        continue;
      }

      if (si == 0 && querySections.size() > 2 && PatternMatchConfig.START_END_CUT_IN_SUBPARTS) {
        dataSect.setPoints(
            sectionEndSubpartPoints(
                matchedSections.get(si), querySect.getWidth() * subSequenceScaleFactorX));
      } else if (si == querySections.size() - 1
          && querySections.size() > 2
          && PatternMatchConfig.START_END_CUT_IN_SUBPARTS_IN_RESULTS) {
        dataSect.setPoints(
            sectionStartSubpartPoints(
                matchedSections.get(si), querySect.getWidth() * subSequenceScaleFactorX));
      } else {
        dataSect.setPoints(matchedSections.get(si).getPoints());
      }
      dataSect.setWidth(calcWidth(dataSect.getPoints()));
      dataSect.setHeight(calcHeight(dataSect.getPoints()));
      if (dataSect.getHeight() == 0) {
        continue;
      }

      double scaleFactorX = dataSect.getWidth() / (querySect.getWidth() * subSequenceScaleFactorX);
      double scaleFactorY =
          dataSect.getHeight()
              / (querySect.getHeight()
                  * (PatternMatchConfig.RESCALING_Y
                      ? subSequenceScaleFactorY
                      : subSequenceScaleFactorX));

      if (scaleFactorX != 0 && scaleFactorY != 0) {
        rescalingCost +=
            (Math.pow(Math.log(scaleFactorX), 2) + Math.pow(Math.log(scaleFactorY), 2));
      }

      // calculate the centroid of the two sections to align them
      dataSect.setCentroidY(0);
      for (i = 0; i < dataSect.getPoints().size(); i++) {
        dataSect.setCentroidY(dataSect.getCentroidY() + dataSect.getPoints().get(i).getY());
      }
      dataSect.setCentroidY(dataSect.getCentroidY() / dataSect.getPoints().size());
      querySect.setCentroidY(0);
      for (i = 0; i < querySect.getPoints().size(); i++) {
        querySect.setCentroidY(
            querySect.getPoints().get(0).getY()
                * (PatternMatchConfig.RESCALING_Y
                    ? subSequenceScaleFactorY
                    : subSequenceScaleFactorX)
                * scaleFactorY);
      }
      querySect.setCentroidY(querySect.getCentroidY() / querySect.getPoints().size());
      centroidsDifference =
          querySect.getPoints().get(0).getY()
                  * (PatternMatchConfig.RESCALING_Y
                      ? subSequenceScaleFactorY
                      : subSequenceScaleFactorX)
                  * scaleFactorY
              - dataSect.getPoints().get(0).getY();

      double queryPtsStep = (double) querySect.getPoints().size() / dataSect.getPoints().size();

      for (i = 0; i < dataSect.getPoints().size(); i++) {
        Point dataPt = dataSect.getPoints().get(i);
        Point queryPt = querySect.getPoints().get((int) Math.floor(i * queryPtsStep));

        double resSum =
            Math.abs(
                    (queryPt.getY()
                                * (PatternMatchConfig.RESCALING_Y
                                    ? subSequenceScaleFactorY
                                    : subSequenceScaleFactorX)
                                * scaleFactorY
                            - centroidsDifference)
                        - dataPt.getY())
                / dataSect.getHeight();
        sum += resSum;
        num++;
      }

      if (!partialQuery) {
        if (PatternMatchConfig.START_END_CUT_IN_SUBPARTS_IN_RESULTS) {
          for (i = 0; i < dataSect.getPoints().size(); i++) {
            matchedPoints.add(dataSect.getPoints().get(i));
          }
        } else {
          for (i = 0; i < matchedSections.get(si).getPoints().size(); i++) {
            matchedPoints.add(matchedSections.get(si).getPoints().get(i));
          }
        }
      }

      if (num > 0) {
        pointDifferencesCost += sum / num;
      }
    }
    PatternCalculationResult result = new PatternCalculationResult();
    result.setMatch(
        pointDifferencesCost * PatternMatchConfig.VALUE_DIFFERENCE_WEIGHT
            + rescalingCost * PatternMatchConfig.RESCALING_COST_WEIGHT);
    result.setMatchedPoints(matchedPoints);
    return result;
  }
Given a list of tangents it divides the list of tangents in a list of sections. Each section
shares the same tangent signs (a section could be an increasing curve or a decreasing curve,
but not both)

@return Array of sections.
library-udf/src/main/java/org/apache/iotdb/library/match/PatternExecutor.java