SparseMatrix BSplineBasis1D::reduceSupport(double lb, double ub)
{
    // Check bounds
    if (lb < knots.front() || ub > knots.back())
        throw Exception("BSplineBasis1D::reduceSupport: Cannot increase support!");

    unsigned int k = degree + 1;

    int index_lower = indexSupportedBasisfunctions(lb).front();
    int index_upper = indexSupportedBasisfunctions(ub).back();

    // Check lower bound index
    if (k != knotMultiplicity(knots.at(index_lower)))
    {
        int suggested_index = index_lower - 1;
        if (0 <= suggested_index)
        {
            index_lower = suggested_index;
        }
        else
        {
            throw Exception("BSplineBasis1D::reduceSupport: Suggested index is negative!");
        }
    }

    // Check upper bound index
    if (knotMultiplicity(ub) == k && knots.at(index_upper) == ub)
    {
        index_upper -= k;
    }

    // New knot vector
    std::vector<double> si;
    si.insert(si.begin(), knots.begin()+index_lower, knots.begin()+index_upper+k+1);

    // Construct selection matrix A
    int numOld = knots.size()-k; // Current number of basis functions
    int numNew = si.size()-k; // Number of basis functions after update

    if (numOld < numNew)
        throw Exception("BSplineBasis1D::reduceSupport: Number of basis functions is increased instead of reduced!");

    DenseMatrix Ad = DenseMatrix::Zero(numOld, numNew);
    Ad.block(index_lower, 0, numNew, numNew) = DenseMatrix::Identity(numNew, numNew);
    SparseMatrix A = Ad.sparseView();

    // Update knots
    knots = si;

    return A;
}
SparseMatrix BSplineBasis1D::decomposeToBezierForm()
{
    // Build refine knot vector
    std::vector<double> refinedKnots = knots;

    // Start at first knot and add knots until all knots have multiplicity degree + 1
    std::vector<double>::iterator knoti = refinedKnots.begin();
    while (knoti != refinedKnots.end())
    {
        // Insert new knots
        int mult = degree + 1 - knotMultiplicity(*knoti);
        if (mult > 0)
        {
            std::vector<double> newKnots(mult, *knoti);
            refinedKnots.insert(knoti, newKnots.begin(), newKnots.end());
        }

        // Advance to next knot
        knoti = std::upper_bound(refinedKnots.begin(), refinedKnots.end(), *knoti);
    }

    if (!isKnotVectorRegular(refinedKnots) || !isRefinement(refinedKnots))
        throw Exception("BSplineBasis1D::refineKnots: New knot vector is not a proper refinement!");

    // Return knot insertion matrix
    SparseMatrix A = buildKnotInsertionMatrix(refinedKnots);

    // Update knots
    knots = refinedKnots;

    return A;
}
// Insert knots and compute knot insertion matrix (to update control points)
SparseMatrix BSplineBasis1D::insertKnots(double tau, unsigned int multiplicity)
{
    if (!insideSupport(tau))
        throw Exception("BSplineBasis1D::insertKnots: Cannot insert knot outside domain!");

    if (knotMultiplicity(tau) + multiplicity > degree + 1)
        throw Exception("BSplineBasis1D::insertKnots: Knot multiplicity is too high!");

    // New knot vector
    int index = indexHalfopenInterval(tau);

    std::vector<double> extKnots = knots;
    for (unsigned int i = 0; i < multiplicity; i++)
        extKnots.insert(extKnots.begin()+index+1, tau);

    if (!isKnotVectorRegular(extKnots))
        throw Exception("BSplineBasis1D::insertKnots: New knot vector is not regular!");

    // Return knot insertion matrix
    SparseMatrix A = buildKnotInsertionMatrix(extKnots);

    // Update knots
    knots = extKnots;

    return A;
}
SparseMatrix BSplineBasis1D::refineKnotsLocally(double x)
{
    if (!insideSupport(x))
        throw Exception("BSplineBasis1D::refineKnotsLocally: Cannot refine outside support!");

    if (getNumBasisFunctions() >= getNumBasisFunctionsTarget()
            || assertNear(knots.front(), knots.back()))
    {
        unsigned int n = getNumBasisFunctions();
        DenseMatrix A = DenseMatrix::Identity(n,n);
        return A.sparseView();
    }

    // Refined knot vector
    std::vector<double> refinedKnots = knots;

    auto upper = std::lower_bound(refinedKnots.begin(), refinedKnots.end(), x);

    // Check left boundary
    if (upper == refinedKnots.begin())
        std::advance(upper, degree+1);

    // Get previous iterator
    auto lower = std::prev(upper);

    // Do not insert if upper and lower bounding knot are close
    if (assertNear(*upper, *lower))
    {
        unsigned int n = getNumBasisFunctions();
        DenseMatrix A = DenseMatrix::Identity(n,n);
        return A.sparseView();
    }

    // Insert knot at x
    double insertVal = x;

    // Adjust x if it is on or close to a knot
    if (knotMultiplicity(x) > 0
            || assertNear(*upper, x, 1e-6, 1e-6)
            || assertNear(*lower, x, 1e-6, 1e-6))
    {
        insertVal = (*upper + *lower)/2.0;
    }

    // Insert new knot
    refinedKnots.insert(upper, insertVal);

    if (!isKnotVectorRegular(refinedKnots) || !isRefinement(refinedKnots))
        throw Exception("BSplineBasis1D::refineKnotsLocally: New knot vector is not a proper refinement!");

    // Build knot insertion matrix
    SparseMatrix A = buildKnotInsertionMatrix(refinedKnots);

    // Update knots
    knots = refinedKnots;

    return A;
}
// Insert knots and compute knot insertion matrix (to update control points)
bool BSplineBasis1D::insertKnots(SparseMatrix &A, double tau, unsigned int multiplicity)
{
    if(!insideSupport(tau) || knotMultiplicity(tau) + multiplicity > degree + 1)
        return false;

    // New knot vector
    int index = indexHalfopenInterval(tau);

    std::vector<double> extKnots = knots;
    for(unsigned int i = 0; i < multiplicity; i++)
        extKnots.insert(extKnots.begin()+index+1, tau);

    assert(isKnotVectorRegular(extKnots));

    // Return knot insertion matrix
    if(!buildKnotInsertionMatrix(A, extKnots))
        return false;

    // Update knots
    knots = extKnots;

    return true;
}
bool BSplineBasis1D::reduceSupport(double lb, double ub, SparseMatrix &A)
{
    // Check bounds
    if(lb < knots.front() || ub > knots.back())
    {
        return false;
    }

    unsigned int k = degree + 1;

    int index_lower = indexSupportedBasisfunctions(lb).front();
    int index_upper = indexSupportedBasisfunctions(ub).back();

    // Check lower bound index
    unsigned int count = knotMultiplicity(knots.at(index_lower));
    bool is_p_regular = (k == count);

    if(!is_p_regular)
    {
        int suggested_index = index_lower - 1;
        if(0 <= suggested_index)
        {
            index_lower = suggested_index;
        }
        else
        {

#ifndef NDEBUG
            std::cout << "\n\n----------------adjust_index_for_domain_reduction-----------------" << std::endl;
            std::cout << "Error: not enough knots to guarantee controlpoint convergence" << std::endl;
            std::cout << "----------------adjust_index_for_domain_reduction-----------------\n\n" << std::endl;
#endif // NDEBUG

            return false;
        }
    }

    // Check upper bound index
    if(knotMultiplicity(ub) == k && knots.at(index_upper) == ub)
    {
        index_upper -= k;
    }

    // New knot vector
    std::vector<double> si;
    si.insert(si.begin(), knots.begin()+index_lower, knots.begin()+index_upper+k+1);

    // Construct selection matrix A
    int n_old = knots.size()-k; // Current number of basis functions
    int n_new = si.size()-k; // Number of basis functions after update

    if (n_old < n_new) return false;

    DenseMatrix Ad = DenseMatrix::Zero(n_old, n_new);
    Ad.block(index_lower, 0, n_new, n_new) = DenseMatrix::Identity(n_new, n_new);
    A = Ad.sparseView();

    // Update knots
    knots = si;

    return true;
}