public FastCollinearPoints(Point[] points) {
        checkNull(points);
        checkDuplicate(points);

        int n = points.length;
        Point[] pointsCopy = new Point[n];
        System.arraycopy(points, 0, pointsCopy, 0, n);
        Arrays.sort(pointsCopy);

        segmentId = 0;
        segmentObjects = new LineSegment[n];

        for (int i = 0; i < n - 3; i++) {
            Point p = pointsCopy[i];
            Point[] sortedPoints = new Point[n];  // content length = n - 1, last = null
            System.arraycopy(pointsCopy, 0, sortedPoints, 0, i);
            System.arraycopy(pointsCopy, i + 1, sortedPoints, i, n - i - 1);
            Arrays.sort(sortedPoints, 0, n - 1, p.slopeOrder());

            int currentStartIndex = 0;
            for (int j = 1; j < n; j++) {
                Point currentStartPoint = sortedPoints[currentStartIndex];
                Point currentPoint = sortedPoints[j];
                if (currentPoint == null || currentPoint.slopeTo(p) != currentStartPoint.slopeTo(p)) {
                    int len = j - currentStartIndex;
                    /* critical - if current starting point of this segment is smaller than p,
                       then this line segment is already in the collection */
                    if (len >= 3 && currentStartPoint.compareTo(p) > 0) {
                        segmentObjects[segmentId] = new LineSegment(p, sortedPoints[j - 1]);
                        segmentId++;
                        if (segmentId == segmentObjects.length) {
                            resize(segmentId * 2);
                        }
                    }
                    currentStartIndex = j;
                }
            }
        }

        segments = new LineSegment[segmentId];

        for (int i = 0; i < segmentId; i++) {
            segments[i] = (LineSegment) segmentObjects[i];
        }
    }