// Path: src/main/java/ro/hasna/ts/math/exception/ArrayLengthIsTooSmallException.java
// public class ArrayLengthIsTooSmallException extends NumberIsTooSmallException {
//     private static final long serialVersionUID = -2633584088507009304L;
// 
//     /**
//      * Construct the exception.
//      *
//      * @param wrong          Value that is smaller than the minimum.
//      * @param min            Minimum.
//      * @param boundIsAllowed Whether {@code min} is included in the allowed range.
//      */
//     public ArrayLengthIsTooSmallException(Number wrong, Number min, boolean boundIsAllowed) {
//         super(wrong, min, boundIsAllowed);
//     }
// 
//     /**
//      * Construct the exception with a specific context.
//      *
//      * @param specific       Specific context pattern.
//      * @param wrong          Value that is smaller than the minimum.
//      * @param min            Minimum.
//      * @param boundIsAllowed Whether {@code min} is included in the allowed range.
//      */
//     public ArrayLengthIsTooSmallException(Localizable specific, Number wrong, Number min, boolean boundIsAllowed) {
//         super(specific, wrong, min, boundIsAllowed);
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/util/TimeSeriesPrecision.java
// public class TimeSeriesPrecision {
//     public static final double EPSILON = FastMath.pow(2, -30);
// 
//     /**
//      * Private constructor.
//      */
//     private TimeSeriesPrecision() {
//     }
// }


import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.fitting.AbstractCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoint;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.Precision;
import ro.hasna.ts.math.exception.ArrayLengthIsTooSmallException;
import ro.hasna.ts.math.util.TimeSeriesPrecision;
import java.util.ArrayList;
import java.util.List;
            List<WeightedObservedPoint> segment = new ArrayList<>(segmentSize);
            for (int i = 0; i < segmentSize; i++) {
                segment.add(null);
            }

            int n = 0;
            for (int i = 0; i < len; i++) {
                int index = i % segmentSize;
                segment.set(index, new WeightedObservedPoint(1, index, values[i]));
                if (index + 1 == segmentSize) {
                    reducedValues[n++] = curveFitter.fit(segment);
                    if (n == segments) break;
                }
            }
        } else {
            double segmentSize = len * 1.0 / segments;
            int k = 0;
            int segmentSizeReal = (int) FastMath.ceil(segmentSize) + 1;
            int index = 0;
            List<WeightedObservedPoint> segment = new ArrayList<>(segmentSizeReal);

            for (int i = 0; i < segments - 1; i++) {
                double x = (i + 1) * segmentSize - 1;

                for (; k < x; k++) {
                    segment.add(new WeightedObservedPoint(1, index, values[k]));
                    index++;
                }

                double delta = x - (int) x;
// Path: src/main/java/ro/hasna/ts/math/exception/ArrayLengthIsTooSmallException.java
// public class ArrayLengthIsTooSmallException extends NumberIsTooSmallException {
//     private static final long serialVersionUID = -2633584088507009304L;
// 
//     /**
//      * Construct the exception.
//      *
//      * @param wrong          Value that is smaller than the minimum.
//      * @param min            Minimum.
//      * @param boundIsAllowed Whether {@code min} is included in the allowed range.
//      */
//     public ArrayLengthIsTooSmallException(Number wrong, Number min, boolean boundIsAllowed) {
//         super(wrong, min, boundIsAllowed);
//     }
// 
//     /**
//      * Construct the exception with a specific context.
//      *
//      * @param specific       Specific context pattern.
//      * @param wrong          Value that is smaller than the minimum.
//      * @param min            Minimum.
//      * @param boundIsAllowed Whether {@code min} is included in the allowed range.
//      */
//     public ArrayLengthIsTooSmallException(Localizable specific, Number wrong, Number min, boolean boundIsAllowed) {
//         super(specific, wrong, min, boundIsAllowed);
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/util/TimeSeriesPrecision.java
// public class TimeSeriesPrecision {
//     public static final double EPSILON = FastMath.pow(2, -30);
// 
//     /**
//      * Private constructor.
//      */
//     private TimeSeriesPrecision() {
//     }
// }


// Path: src/main/java/ro/hasna/ts/math/representation/PiecewiseCurveFitterApproximation.java
import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.fitting.AbstractCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoint;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.Precision;
import ro.hasna.ts.math.exception.ArrayLengthIsTooSmallException;
import ro.hasna.ts.math.util.TimeSeriesPrecision;
import java.util.ArrayList;
import java.util.List;

            List<WeightedObservedPoint> segment = new ArrayList<>(segmentSize);
            for (int i = 0; i < segmentSize; i++) {
                segment.add(null);
            }

            int n = 0;
            for (int i = 0; i < len; i++) {
                int index = i % segmentSize;
                segment.set(index, new WeightedObservedPoint(1, index, values[i]));
                if (index + 1 == segmentSize) {
                    reducedValues[n++] = curveFitter.fit(segment);
                    if (n == segments) break;
                }
            }
        } else {
            double segmentSize = len * 1.0 / segments;
            int k = 0;
            int segmentSizeReal = (int) FastMath.ceil(segmentSize) + 1;
            int index = 0;
            List<WeightedObservedPoint> segment = new ArrayList<>(segmentSizeReal);

            for (int i = 0; i < segments - 1; i++) {
                double x = (i + 1) * segmentSize - 1;

                for (; k < x; k++) {
                    segment.add(new WeightedObservedPoint(1, index, values[k]));
                    index++;
                }

                double delta = x - (int) x;
                if (!Precision.equals(delta, 0, TimeSeriesPrecision.EPSILON)) {