class CatmullRomInterpolator implements Interpolator<CatmullRomInterpolator.Params> {

    @Override
    public List<XYCoords> interpolate(XYSeries series, Params params) {
        if (params.getPointPerSegment() < 2) {
            throw new IllegalArgumentException(
                    "pointsPerSegment must be greater than 2, since 2 points is just the linear segment.");
        }

        // Cannot interpolate curves given only two points.  Two points
        // is best represented as a simple line segment.
        if (series.size() < 3) {
            throw new IllegalArgumentException("Cannot interpolate a series with fewer than 3 vertices.");
        }

        // Get the change in x and y between the first and second coordinates.
        double dx = series.getX(1).doubleValue() - series.getX(0).doubleValue();
        double dy = series.getY(1).doubleValue() - series.getY(0).doubleValue();

        // Then using the change, extrapolate backwards to find a control point.
        double x1 = series.getX(0).doubleValue() - dx;
        double y1 = series.getY(0).doubleValue() - dy;

        // Actually create the start point from the extrapolated values.
        XYCoords start = new XYCoords(x1, y1);

        // Repeat for the end control point.
        int n = series.size() -1;
        dx = series.getX(n).doubleValue() - series.getX(n-1).doubleValue();
        dy = series.getY(n).doubleValue() - series.getY(n - 1).doubleValue();
        double xn = series.getX(n).doubleValue() + dx;
        double yn = series.getY(n).doubleValue() + dy;
        XYCoords end = new XYCoords(xn, yn);

        // TODO: figure out whether this extra control-point synthesis is
        // TODO: really necessary and either remove the above or fix the below.
        // insert the start control point at the start of the vertices list.
        // TODO vertices.add(0, start);

        // append the end control ponit to the end of the vertices list.
        // TODO vertices.add(end);
        //}

        ExtrapolatedXYSeries extrapolatedXYSeries = new ExtrapolatedXYSeries(series, start, end);

        // Dimension a result list of coordinates.
        List<XYCoords> result = new ArrayList<>();

        // When looping, remember that each cycle requires 4 points, starting
        // with i and ending with i+3.  So we don't loop through all the points.
        for (int i = 0; i < extrapolatedXYSeries.size() - 3; i++) {

            // Actually calculate the Catmull-Rom curve for one segment.
            List<XYCoords> points = interpolate(extrapolatedXYSeries, i, params);

            // Since the middle points are added twice, once for each bordering
            // segment, we only add the 0 index result point for the first
            // segment.  Otherwise we will have duplicate points.
            if (result.size() > 0) {
                points.remove(0);
            }

            // Add the coordinates for the segment to the result list.
            result.addAll(points);
        }
        return result;

    }

}