double predict(size_t steps) {
        if (points_.empty()) {
            return 0;
        }

        if (points_.size() != nPoints_ - 1 + nDim_) {
            return points_.back();
        }

        for (size_t i = 0; i < nDim_; ++i) {
            for (size_t j = 0; j < nPoints_; ++j) {
                x_(i, j) = points_[i + j];
            }
        }

        auto c = x_ * x_.transpose() / nPoints_;

        SelfAdjointEigenSolver<MatrixXd> esC(c);
        auto v = esC.eigenvectors();
        // LOG(INFO) << OUTLN(esC.eigenvalues());
        // LOG(INFO) << OUTLN(v);

        for (size_t i = 0; i < nDim_ - 1; ++i) {
            for (size_t j = 0; j < nEigen_; ++j) {
                vStar_(i, j) = v(i, nDim_ - 1 - j);
            }
        }

        for (size_t i = 0; i < nEigen_; ++i) {
            vTau_(0, i) = v(nDim_ - 1, nDim_ - 1 - i);
        }

        auto predictionM = (vTau_ * vStar_.transpose()) / (1.0 - (vTau_ * vTau_.transpose())(0, 0));
        // cout << OUTLN(predictionM) << OUTLN(predictionM.sum());

        DoubleVector computed;
        auto getPoint = [&](size_t index) {
            if (index < points_.size()) {
                return points_[index];
            }
            return computed[index - points_.size()];
        };

        for (size_t iStep = 0; iStep < steps; ++iStep) {
            for (size_t i = 0; i < nDim_ - 1; ++i) {
                q_(i, 0) = getPoint((nPoints_ - 1 + nDim_) + i - (nDim_ - 1) + iStep);
            }

            computed.emplace_back((predictionM * q_)(0, 0));
        }

        return computed.back();
    }
    double predict(size_t steps) {
        if (points_.empty()) {
            return 0;
        }

        if (points_.size() < nDim_ + steps + 40) {
            return points_.back();
        }

        VectorPoints points;
        for (size_t i = 0; i + nDim_ + steps < points_.size(); ++i) {
            VectorPoint point(nDim_ + 1);
            point.b_ = points_[i + nDim_ - 1 + steps];
            for (size_t j = 0; j < nDim_; ++j) {
                point.a_[j] = points_[i + j];
            }
            point.a_[nDim_] = 1.0;
            points.emplace_back(std::move(point));
        }

        auto b = linearRegression(points, points_[0] * points_[0] * points_.size() / 10000000);
        // LOG_EVERY_MS(INFO, 100) << OUTLN(b);
        // cout << OUTLN(b);

        DoubleVector computed;
        auto getPoint = [&](size_t index) {
            if (index < points_.size()) {
                return points_[index];
            }
            return computed[index - points_.size()];
        };

        for (size_t iStep = 0; iStep < steps; ++iStep) {
            for (size_t i = 0; i < nDim_; ++i) {
                q_[i] = getPoint((points_.size() - 1) + i - nDim_ + iStep);
            }

            computed.emplace_back(dot(b, q_));
        }
        // cout << OUTLN(computed);

        return computed.back();
    }