Repository: hyrodium/BasicBSpline.jl
Pull request #116 (2021-12-27 17:31:40)

Cleanup


Patch:
diff --git a/src/BasicBSpline.jl b/src/BasicBSpline.jl
index 6393bbb8..d594ebaa 100644
--- a/src/BasicBSpline.jl
+++ b/src/BasicBSpline.jl
@@ -9,7 +9,7 @@ export BSplineDerivativeSpace
 export BSplineManifold, CustomBSplineManifold
 export bsplinebasis₊₀, bsplinebasis₋₀, bsplinebasis
 export bsplinebasis′₊₀, bsplinebasis′₋₀, bsplinebasis′
-export bsplinesupport, domain, changebasis
+export bsplinesupport, domain, changebasis, expandspace
 export bsplinebasisall, intervalindex
 export refinement, bsplinespaces, controlpoints
 export isproper, properdim
@@ -21,7 +21,8 @@ export fittingcontrolpoints
 include("_Knots.jl")
 include("_BSplineSpace.jl")
 include("_BSplineBasis.jl")
-include("_Derivative.jl")
+include("_DerivativeSpace.jl")
+include("_DerivativeBasis.jl")
 include("_ChangeBasis.jl")
 include("_BSplineManifold.jl")
 include("_CustomBSplineManifold.jl")
diff --git a/src/_BSplineSpace.jl b/src/_BSplineSpace.jl
index 3f67ee7c..5ba69e0e 100644
--- a/src/_BSplineSpace.jl
+++ b/src/_BSplineSpace.jl
@@ -115,12 +115,6 @@ function properdim(P::AbstractBSplineSpace)
     return dim(P) - sum(iszeros(P))
 end
 
-function _knotindex(P::AbstractBSplineSpace{p},t) where p
-    k = knots(P)
-    l = length(k)
-    return _knotindex(view(k.vector, 1+p:l-p), t) + p
-end
-
 @doc raw"""
 Return the support of ``i``-th B-spline basis function.
 ```math
@@ -145,10 +139,11 @@ Return a B-spline space of one degree lower.
 """
 _lower
 
-_lower(::Type{AbstractBSplineSpace{p}}) where p = AbstractBSplineSpace{p-1}
-_lower(::Type{AbstractBSplineSpace{p,T}}) where {p,T} = AbstractBSplineSpace{p-1,T}
-_lower(::Type{BSplineSpace{p}}) where p = BSplineSpace{p-1}
-_lower(::Type{BSplineSpace{p,T}}) where {p,T} = BSplineSpace{p-1,T}
+# TODO: Consider we really need these methods.
+# _lower(::Type{AbstractBSplineSpace{p}}) where p = AbstractBSplineSpace{p-1}
+# _lower(::Type{AbstractBSplineSpace{p,T}}) where {p,T} = AbstractBSplineSpace{p-1,T}
+# _lower(::Type{BSplineSpace{p}}) where p = BSplineSpace{p-1}
+# _lower(::Type{BSplineSpace{p,T}}) where {p,T} = BSplineSpace{p-1,T}
 _lower(P::BSplineSpace{p,T}) where {p,T} = BSplineSpace{p-1}(knots(P))
 
 """
@@ -160,3 +155,15 @@ function intervalindex(P::AbstractBSplineSpace{p},t::Real) where p
     v = view(k.vector,2+p:l-p-1)
     return searchsortedlast(v,t)+1
 end
+
+"""
+Expand B-spline space with given additional degree and knots.
+"""
+function expandspace(P::BSplineSpace{p,T}; p₊::Integer=0, k₊::Knots{T}=Knots{T}()) where {p,T}
+    k = knots(P)
+    k0 = unique(k[1+p:end-p])
+    p′ = p + p₊
+    k′ = k + p₊*k0 + k₊
+    P′ = BSplineSpace{p′}(k′)
+    return P′
+end
diff --git a/src/_ChangeBasis.jl b/src/_ChangeBasis.jl
index 434438d4..d8f5a766 100644
--- a/src/_ChangeBasis.jl
+++ b/src/_ChangeBasis.jl
@@ -1,4 +1,5 @@
 # Change Basis between B-Spline Spaces
+# See https://hackmd.io/lpA0D0ySTQ6Hq1CdaaONxQ for more information.
 
 @doc raw"""
 Return a coefficient matrix A which satisfy
diff --git a/src/_CustomBSplineManifold.jl b/src/_CustomBSplineManifold.jl
index 89ae0f1b..00e6a5a5 100644
--- a/src/_CustomBSplineManifold.jl
+++ b/src/_CustomBSplineManifold.jl
@@ -1,4 +1,4 @@
-# B-spline manifold
+# B-spline manifold with given type as a type of control points
 
 struct CustomBSplineManifold{Dim,Deg,C,S<:Tuple} <: AbstractBSplineManifold{Dim,Deg}
     bsplinespaces::S
diff --git a/src/_Derivative.jl b/src/_DerivativeBasis.jl
similarity index 77%
rename from src/_Derivative.jl
rename to src/_DerivativeBasis.jl
index 7e6aba42..55599fc2 100644
--- a/src/_Derivative.jl
+++ b/src/_DerivativeBasis.jl
@@ -1,22 +1,6 @@
-# Derivative
+# Derivative of B-spline basis function
 
-struct BSplineDerivativeSpace{r, T<:AbstractBSplineSpace}
-    bsplinespace::T
-    function BSplineDerivativeSpace{r}(P::T) where {r, T<:AbstractBSplineSpace}
-        new{r,T}(P)
-    end
-end
-
-bsplinespace(dP::BSplineDerivativeSpace) = dP.bsplinespace
-knots(dP::BSplineDerivativeSpace) = knots(bsplinespace(dP))
-degree(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p} = p - r
-dim(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p} = dim(bsplinespace(dP))
-properdim(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p} = properdim(bsplinespace(dP)) - r
-intervalindex(dP::BSplineDerivativeSpace,t::Real) = intervalindex(bsplinespace(dP),t)
-domain(dP::BSplineDerivativeSpace) = domain(bsplinespace(dP))
-_lower(dP::BSplineDerivativeSpace{r}) where r = BSplineDerivativeSpace{r-1}(_lower(bsplinespace(dP)))
-
-@generated function bsplinebasis₊₀(P::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
+@generated function bsplinebasis₊₀(dP::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
     ks = [Symbol(:k,i) for i in 1:p+2]
     Ks = [Symbol(:K,i) for i in 1:p+1]
     Bs = [Symbol(:B,i) for i in 1:p+1]
@@ -40,7 +24,7 @@ _lower(dP::BSplineDerivativeSpace{r}) where r = BSplineDerivativeSpace{r-1}(_low
             push!(exs, :($(B_l(p+1-i)) = $(C_r(p+1-i))))
         end
         Expr(:block,
-            :(v = knots(P).vector),
+            :(v = knots(dP).vector),
             :($k_l = $k_r),
             :($(B_l(p+1)) = $(A_r(p+1))),
             exs...,
@@ -51,7 +35,7 @@ _lower(dP::BSplineDerivativeSpace{r}) where r = BSplineDerivativeSpace{r-1}(_low
     end
 end
 
-@generated function bsplinebasis₋₀(P::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
+@generated function bsplinebasis₋₀(dP::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
     ks = [Symbol(:k,i) for i in 1:p+2]
     Ks = [Symbol(:K,i) for i in 1:p+1]
     Bs = [Symbol(:B,i) for i in 1:p+1]
@@ -75,7 +59,7 @@ end
             push!(exs, :($(B_l(p+1-i)) = $(C_r(p+1-i))))
         end
         Expr(:block,
-            :(v = knots(P).vector),
+            :(v = knots(dP).vector),
             :($k_l = $k_r),
             :($(B_l(p+1)) = $(A_r(p+1))),
             exs...,
@@ -86,7 +70,7 @@ end
     end
 end
 
-@generated function bsplinebasis(P::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
+@generated function bsplinebasis(dP::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
     ks = [Symbol(:k,i) for i in 1:p+2]
     Ks = [Symbol(:K,i) for i in 1:p+1]
     Bs = [Symbol(:B,i) for i in 1:p+1]
@@ -110,7 +94,7 @@ end
             push!(exs, :($(B_l(p+1-i)) = $(C_r(p+1-i))))
         end
         Expr(:block,
-            :(v = knots(P).vector),
+            :(v = knots(dP).vector),
             :($k_l = $k_r),
             :($(B_l(p+1)) = $(A_r(p+1))),
             :($(Bs[end]) += $(T)(t == $(ks[end]) == v[end])),
@@ -165,7 +149,7 @@ for suffix in ("", "₋₀", "₊₀")
     end
 end
 
-@generated function bsplinebasisall(P::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
+@generated function bsplinebasisall(dP::BSplineDerivativeSpace{r,BSplineSpace{p,T}}, i::Integer, t::Real) where {r, p, T}
     bs = [Symbol(:b,i) for i in 1:p]
     Bs = [Symbol(:B,i) for i in 1:p+1]
     K1s = [:($(p)/(k[i+$(j)]-k[i+$(p+j)])) for j in 1:p]
@@ -176,8 +160,8 @@ end
     if r ≤ p
         Expr(:block,
             :($(Expr(:meta, :inline))),
-            :(k = knots(P)),
-            :($b = bsplinebasisall(_lower(P),i+1,t)),
+            :(k = knots(dP)),
+            :($b = bsplinebasisall(_lower(dP),i+1,t)),
             :($(Bs[1]) = $(K1s[1])*$(bs[1])),
             exs...,
             :($(Bs[p+1]) = $(K2s[p])*$(bs[p])),
@@ -189,9 +173,7 @@ end
     end
 end
 
-function bsplinebasisall(dP::BSplineDerivativeSpace{0,BSplineSpace{p,T}}, i::Integer, t::Real) where {p, T}
+@inline function bsplinebasisall(dP::BSplineDerivativeSpace{0,BSplineSpace{p,T}}, i::Integer, t::Real) where {p, T}
     P = bsplinespace(dP)
     bsplinebasisall(P,i,t)
 end
-
-# TODO: Add issubset(::BSplineDerivativeSpace, i, t)
diff --git a/src/_DerivativeSpace.jl b/src/_DerivativeSpace.jl
new file mode 100644
index 00000000..33bf243e
--- /dev/null
+++ b/src/_DerivativeSpace.jl
@@ -0,0 +1,48 @@
+# Space of derivative of B-spline basis function
+
+struct BSplineDerivativeSpace{r, T<:AbstractBSplineSpace}
+    bsplinespace::T
+    function BSplineDerivativeSpace{r}(P::T) where {r, T<:AbstractBSplineSpace}
+        new{r,T}(P)
+    end
+end
+
+bsplinespace(dP::BSplineDerivativeSpace) = dP.bsplinespace
+knots(dP::BSplineDerivativeSpace) = knots(bsplinespace(dP))
+degree(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p} = p - r
+dim(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p} = dim(bsplinespace(dP))
+properdim(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p} = properdim(bsplinespace(dP)) - r
+intervalindex(dP::BSplineDerivativeSpace,t::Real) = intervalindex(bsplinespace(dP),t)
+domain(dP::BSplineDerivativeSpace) = domain(bsplinespace(dP))
+_lower(dP::BSplineDerivativeSpace{r}) where r = BSplineDerivativeSpace{r-1}(_lower(bsplinespace(dP)))
+
+function Base.issubset(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}, P′::AbstractBSplineSpace) where {r,p}
+    k = knots(dP)
+    P = BSplineSpace{p-r}(k)
+    return P ⊆ P′
+end
+function Base.issubset(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}, dP′::BSplineDerivativeSpace{0}) where {r,p}
+    P′ = bsplinespace(dP′)
+    return dP ⊆ P′
+end
+function Base.issubset(dP::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}, dP′::BSplineDerivativeSpace{r′,<:AbstractBSplineSpace{p}}) where {r,p,r′,p′}
+    if r > r′
+        P = bsplinespace(dP)
+        P′ = bsplinespace(dP′)
+        _dP = BSplineDerivativeSpace{r-r′}(P)
+        return _dP ⊆ P′
+    elseif r == r′
+        P = bsplinespace(dP)
+        P′ = bsplinespace(dP′)
+        return P ⊆ P′
+    else
+        # This might not be correct.
+        return false
+    end
+end
+function Base.issubset(P::AbstractBSplineSpace, dP′::BSplineDerivativeSpace{r,<:AbstractBSplineSpace{p}}) where {r,p}
+    # This might not be correct.
+    return false
+end
+
+# TODO: Add issqsubset
diff --git a/src/_Knots.jl b/src/_Knots.jl
index 553fc26c..dbfa7133 100644
--- a/src/_Knots.jl
+++ b/src/_Knots.jl
@@ -207,29 +207,3 @@ function 𝔫(k::Knots, t::Real)
     # for large case, this is faster
     return length(searchsorted(k,t))
 end
-
-"""
-Find an index ``i`` such that ``k_{i} ≤ t < k_{i+1}``.
-"""
-function _knotindex₊₀(k::Union{<:Knots, AbstractVector{<:Real}}, t::Real)
-    return searchsortedlast(k, t)
-end
-
-"""
-Find an index ``i`` such that ``k_{i} < t ≤ k_{i+1}``.
-"""
-function _knotindex₋₀(k::Union{<:Knots, AbstractVector{<:Real}}, t::Real)
-    return searchsortedfirst(k, t) - 1
-end
-
-"""
-Find an index ``i`` such that ``k_{i} ≤ t < k_{i+1}`` or ``k_{i} < t = k_{i+1} = k_{\text{end}}``.
-"""
-function _knotindex(k::Union{<:Knots, AbstractVector{<:Real}}, t::Real)
-    j = searchsortedlast(k, t)
-    if j < length(k)
-        return j
-    else
-        return searchsortedfirst(k, t) - 1
-    end
-end
diff --git a/src/_Refinement.jl b/src/_Refinement.jl
index 465b68e1..5334ffd5 100644
--- a/src/_Refinement.jl
+++ b/src/_Refinement.jl
@@ -69,7 +69,7 @@ function refinement(M::CustomBSplineManifold{3}, Ps′::Tuple{<:AbstractBSplineS
     (n1′,n2′,n3′) = dim.(Ps′)
     (A1,A2,A3) = changebasis.(Ps, Ps′)
 
-    a′ = [sum(A1[I₁,J₁] * A2[I₂,J₂]* A3[I₃,J₃] * a[I₁,I₂,I₃] for I₁ in 1:n1, I₂ in 1:n2, I₃ in 1:n3) for J₁ in 1:n1′, J₂ in 1:n2′, J₃ in 1:n3′]
+    a′ = [sum(A1[I₁,J₁] * A2[I₂,J₂] * A3[I₃,J₃] * a[I₁,I₂,I₃] for I₁ in 1:n1, I₂ in 1:n2, I₃ in 1:n3) for J₁ in 1:n1′, J₂ in 1:n2′, J₃ in 1:n3′]
     return CustomBSplineManifold(Ps′, a′)
 end
 
@@ -90,11 +90,7 @@ function refinement(M::BSplineManifold{Dim}; p₊::Union{Nothing,NTuple{Dim,Int}
         throw(DimensionMismatch())
     end
 
-    Ps′ = [(P = Ps[i];
-    p = degree(P);
-    k = knots(P);
-    k_unique = unique(k[1+p:end-p]);
-    BSplineSpace{p + p₊[i]}(k + p₊[i] * k_unique + k₊[i])) for i in 1:Dim]
+    Ps′ = [expandspace(Ps[i], p₊=p₊[i], k₊=k₊[i]) for i in 1:Dim]
 
     return refinement(M, tuple(Ps′...))
 end
@@ -116,11 +112,7 @@ function refinement(M::CustomBSplineManifold; p₊::Union{Nothing,NTuple{Dim,Int
         throw(DimensionMismatch())
     end
 
-    Ps′ = [(P = Ps[i];
-    p = degree(P);
-    k = knots(P);
-    k_unique = unique(k[1+p:end-p]);
-    BSplineSpace{p + p₊[i]}(k + p₊[i] * k_unique + k₊[i])) for i in 1:Dim]
+    Ps′ = [expandspace(Ps[i], p₊=p₊[i], k₊=k₊[i]) for i in 1:Dim]
 
     return refinement(M, tuple(Ps′...))
 end


Test patch:
diff --git a/test/test_Fitting.jl b/test/test_Fitting.jl
index b535aa39..32943dd0 100644
--- a/test/test_Fitting.jl
+++ b/test/test_Fitting.jl
@@ -11,12 +11,10 @@
         a_org = [Point(i1, rand()) for i1 in 1:n1]
         M = CustomBSplineManifold((P1,), a_org)
 
-        p1′ = p1 + 1
-        k1′ = k1 + unique(k1) + Knots(rand(2))
-        P1′ = BSplineSpace{p1′}(k1′)
+        P1′ = expandspace(P1, p₊=1, k₊=Knots(rand(2)))
 
         M′ = refinement(M, (P1′,))
-        a_ref = M′.controlpoints
+        a_ref = controlpoints(M′)
 
         a_fit = fittingcontrolpoints(M, (P1′,))
         @test norm(a_fit - a_ref) < ε
@@ -35,12 +33,10 @@
         a_org = [Point(i1, rand()) for i1 in 1:n1]
         M = CustomBSplineManifold((P1,), a_org)
 
-        p1′ = p1 + 1
-        k1′ = k1 + unique(k1[1+p1:end-p1]) + Knots(rand(2))
-        P1′ = BSplineSpace{p1′}(k1′)
+        P1′ = expandspace(P1, p₊=1, k₊=Knots(rand(2)))
 
         M′ = refinement(M, (P1′,))
-        a_ref = M′.controlpoints
+        a_ref = controlpoints(M′)
 
         a_fit = fittingcontrolpoints(M, (P1′,))
 
@@ -61,15 +57,11 @@
         a_org = [Point(i1, i2, rand()) for i1 in 1:n1, i2 in 1:n2]
         M = CustomBSplineManifold((P1, P2), a_org)
 
-        p1′ = p1 + 1
-        k1′ = k1 + unique(k1) + Knots(rand(2))
-        P1′ = BSplineSpace{p1′}(k1′)
-        p2′ = p2 + 1
-        k2′ = k2 + unique(k2) + Knots(rand(2))
-        P2′ = BSplineSpace{p2′}(k2′)
+        P1′ = expandspace(P1, p₊=1, k₊=Knots(rand(2)))
+        P2′ = expandspace(P2, p₊=1, k₊=Knots(rand(2)))
 
         M′ = refinement(M, (P1′, P2′))
-        a_ref = M′.controlpoints
+        a_ref = controlpoints(M′)
 
         a_fit = fittingcontrolpoints(M, (P1′, P2′))
         @test norm(a_fit - a_ref) < ε
@@ -92,15 +84,11 @@
         a_org = [Point(i1, i2, rand()) for i1 in 1:n1, i2 in 1:n2]
         M = CustomBSplineManifold((P1, P2), a_org)
 
-        p1′ = p1 + 1
-        k1′ = k1 + unique(k1[1+p1:end-p1]) + Knots(rand(2))
-        P1′ = BSplineSpace{p1′}(k1′)
-        p2′ = p2 + 1
-        k2′ = k2 + unique(k2[1+p2:end-p2]) + Knots(rand(2))
-        P2′ = BSplineSpace{p2′}(k2′)
+        P1′ = expandspace(P1, p₊=1, k₊=Knots(rand(2)))
+        P2′ = expandspace(P2, p₊=1, k₊=Knots(rand(2)))
 
         M′ = refinement(M, (P1′, P2′))
-        a_ref = M′.controlpoints
+        a_ref = controlpoints(M′)
 
         a_fit = fittingcontrolpoints(M, (P1′, P2′))
         @test norm(a_fit - a_ref) < ε
@@ -124,18 +112,12 @@
         a_org = [Point(i1, i2, i3, rand()) for i1 in 1:n1, i2 in 1:n2, i3 in 1:n3]
         M = CustomBSplineManifold((P1, P2, P3), a_org)
 
-        p1′ = p1 + 1
-        k1′ = k1 + unique(k1) + Knots(rand(2))
-        P1′ = BSplineSpace{p1′}(k1′)
-        p2′ = p2 + 1
-        k2′ = k2 + unique(k2) + Knots(rand(2))
-        P2′ = BSplineSpace{p2′}(k2′)
-        p3′ = p3 + 1
-        k3′ = k3 + unique(k3) + Knots(rand(2))
-        P3′ = BSplineSpace{p3′}(k3′)
+        P1′ = expandspace(P1, p₊=1, k₊=Knots(rand(2)))
+        P2′ = expandspace(P2, p₊=1, k₊=Knots(rand(2)))
+        P3′ = expandspace(P3, p₊=1, k₊=Knots(rand(2)))
 
         M′ = refinement(M, (P1′, P2′, P3′))
-        a_ref = M′.controlpoints
+        a_ref = controlpoints(M′)
 
         a_fit = fittingcontrolpoints(M, (P1′, P2′, P3′))
         @test norm(a_fit - a_ref) < ε
@@ -162,18 +144,12 @@
         a_org = [Point(i1, i2, i3, rand()) for i1 in 1:n1, i2 in 1:n2, i3 in 1:n3]
         M = CustomBSplineManifold((P1, P2, P3), a_org)
 
-        p1′ = p1 + 1
-        k1′ = k1 + unique(k1[1+p1:end-p1]) + Knots(rand(2))
-        P1′ = BSplineSpace{p1′}(k1′)
-        p2′ = p2 + 1
-        k2′ = k2 + unique(k2[1+p2:end-p2]) + Knots(rand(2))
-        P2′ = BSplineSpace{p2′}(k2′)
-        p3′ = p3 + 1
-        k3′ = k3 + unique(k3[1+p3:end-p3]) + Knots(rand(2))
-        P3′ = BSplineSpace{p3′}(k3′)
+        P1′ = expandspace(P1, p₊=1, k₊=Knots(rand(2)))
+        P2′ = expandspace(P2, p₊=1, k₊=Knots(rand(2)))
+        P3′ = expandspace(P3, p₊=1, k₊=Knots(rand(2)))
 
         M′ = refinement(M, (P1′, P2′, P3′))
-        a_ref = M′.controlpoints
+        a_ref = controlpoints(M′)
 
         a_fit = fittingcontrolpoints(M, (P1′, P2′, P3′))
         @test norm(a_fit - a_ref) < 1.0e-6
diff --git a/test/test_Knots.jl b/test/test_Knots.jl
index 8214e3f1..e3f964e4 100644
--- a/test/test_Knots.jl
+++ b/test/test_Knots.jl
@@ -44,37 +44,6 @@
         @test Knots([1,2,3,5])   ⊉ Knots([1,2,2,3])
     end
 
-    @testset "knotindex for usual case" begin
-        k = Knots([1,2,2,3])
-        @test BasicBSpline._knotindex(k,1.5) == 1
-        @test BasicBSpline._knotindex(k,2.5) == 3
-        @test BasicBSpline._knotindex₋₀(k,1.5) == 1
-        @test BasicBSpline._knotindex₋₀(k,2.5) == 3
-        @test BasicBSpline._knotindex₊₀(k,1.5) == 1
-        @test BasicBSpline._knotindex₊₀(k,2.5) == 3
-    end
-
-    @testset "knotindex for corner case" begin
-        k = Knots([1,2,2,3])
-        @test BasicBSpline._knotindex(k,0) == 0
-        @test BasicBSpline._knotindex(k,1) == 1
-        @test BasicBSpline._knotindex(k,2) == 3
-        @test BasicBSpline._knotindex(k,3) == 3
-        @test BasicBSpline._knotindex(k,4) == 4
-
-        @test BasicBSpline._knotindex₋₀(k,0) == 0
-        @test BasicBSpline._knotindex₋₀(k,1) == 0
-        @test BasicBSpline._knotindex₋₀(k,2) == 1
-        @test BasicBSpline._knotindex₋₀(k,3) == 3
-        @test BasicBSpline._knotindex₋₀(k,4) == 4
-
-        @test BasicBSpline._knotindex₊₀(k,0) == 0
-        @test BasicBSpline._knotindex₊₀(k,1) == 1
-        @test BasicBSpline._knotindex₊₀(k,2) == 3
-        @test BasicBSpline._knotindex₊₀(k,3) == 4
-        @test BasicBSpline._knotindex₊₀(k,4) == 4
-    end
-
     @testset "string" begin
         k = Knots([1,2,2,3])
         @test string(k) == "Knots([1.0, 2.0, 2.0, 3.0])"