# The `Spline`

type and related functions

A spline consists of a B-spline basis and a coefficient vector of the same length. The `Spline{B, C}`

type represents a spline with a basis of type `B`

and a coefficient vector of type `C`

. The constructor `Spline(basis, coeffs)`

returns a spline with the given B-spline basis and coefficients. The basis on which the spline is defined can be obtained with the `basis`

function. The `coeffs`

function returns the coefficient vector of a spline.

```
julia> b = BSplineBasis(4, 0:5);
julia> c = rand(length(b));
julia> spl = Spline(b, c)
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Float64}}:
basis: 8-element BSplineBasis{UnitRange{Int64}}:
order: 4
breakpoints: 0:5
coeffs: [0.515674, 0.449004, 0.584884, 0.752577, 0.641247, 0.649501, 0.0297466, 0.755586]
julia> basis(spl) === b
true
julia> coeffs(spl) === c
true
```

## Evaluating `Spline`

s and their derivatives

To evaluate a `Spline`

, it can be called as a function. Alternatively, the `splinevalue`

function can be used:

```
julia> spl = Spline(BSplineBasis(4, 0:5), rand(8));
julia> spl(2.5)
0.7403064987200976
julia> splinevalue(spl, 2.5)
0.7403064987200976
```

To evaluate derivatives of a spline, the optional `Derivative(N)`

argument is used:

```
julia> spl(2.5, Derivative(1))
-0.04865618099668792
julia> splinevalue(spl, 2.5, Derivative(2))
-0.37245607106893674
```

The `AllDerivatives{N}`

type is not supported as an argument.

### Improving performance

Two optional keyword arguments can be used to speed up the evaluation of splines:

`workspace`

: To evaluate a spline, a vector of length`order(spline)`

is used to store intermediate values. By default, a new vector is allocated for each evaluation. To avoid this, a pre-allocated vector can be specified with the`workspace`

keyword argument. Note that in this case, the calculations are performed with numbers of type`eltype(workspace)`

.`leftknot`

: Evaluating a spline at a point`x`

requires finding the largest index`i`

such that`t[i] ≤ x`

and`t[i] < t[end]`

where`t`

is the knot vector of the basis. By default, the`intervalindex`

function is used to find this index. If the index is already known, it can be specified with the`leftknot`

keyword argument.

```
julia> using BenchmarkTools
julia> spl = Spline(BSplineBasis(4, 0:5), rand(8));
julia> work = zeros(order(spl));
julia> left = intervalindex(basis(spl), 2.5);
julia> @btime $spl(2.5);
328.248 ns (1 allocation: 112 bytes)
julia> @btime $spl(2.5, workspace=$work);
313.321 ns (0 allocations: 0 bytes)
julia> @btime $spl(2.5, workspace=$work, leftknot=$left);
279.443 ns (0 allocations: 0 bytes)
```

## Arithmetic with `Spline`

s

A `Spline`

can be multiplied or divided by a real number using `*`

, `/`

, or `\`

. These operations return a new spline:

```
julia> spl = Spline(BSplineBasis(3, 0:5), [1:7;])
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Int64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [1, 2, 3, 4, 5, 6, 7]
julia> 3.0 * spl
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Float64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0]
julia> spl / 4
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Float64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75]
julia> 4 \ spl == ans
true
```

Using the `lmul!`

/`rmul!`

and `ldiv!`

/`rdiv!`

functions from the `LinearAlgebra`

stdlib, a `Spline`

can be multiplied/divided by a real number *in-place*, which modifies the spline (if its coefficient vector is mutable):

```
julia> using LinearAlgebra: rmul!
julia> spl = Spline(BSplineBasis(3, 0:5), [1:7;])
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Int64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [1, 2, 3, 4, 5, 6, 7]
julia> rmul!(spl, 3);
julia> spl
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Int64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [3, 6, 9, 12, 15, 18, 21]
```

If two splines are defined on the same basis, they can be added and subtracted from each other using `+`

and `-`

:

```
julia> spl1 = Spline(BSplineBasis(3, 0:5), [1:7;]);
julia> spl2 = Spline(BSplineBasis(3, 0:5), [2:8;]);
julia> spl1 + spl2
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Int64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [3, 5, 7, 9, 11, 13, 15]
julia> spl1 - spl2
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Int64}}:
basis: 7-element BSplineBasis{UnitRange{Int64}}:
order: 3
breakpoints: 0:5
coeffs: [-1, -1, -1, -1, -1, -1, -1]
```

## The `BSpline`

type

Indexing into or iterating over a B-spline basis yields `BSpline`

s. The parametric type `BSpline{T}`

represents a single B-spline from a B-spline basis of type `T`

:

```
julia> b = BSplineBasis(4, 0:5);
julia> b[3]
BSpline{BSplineBasis{UnitRange{Int64}}}:
basis: 8-element BSplineBasis{UnitRange{Int64}}:
order: 4
breakpoints: 0:5
index: 3 (knots: [0, 0, 1, 2, 3])
```

Actually, a `BSpline`

is just a `Spline`

with a coefficient vector of a special type, i.e., `BSpline{T}`

is an alias for `Spline{T,BSplines.StandardBasisVector{Bool}}`

.

`BSpline`

s can be used like any other `Spline`

. However, they behave differently in two ways:

`BSpline`

s are printed differently: instead of the coefficient vector, its index within the basis is shown.- Calling
`support`

on a`BSpline`

yields its actual support (the interval on which it is non-zero) instead of the support of the basis.

The second point is illustrated by the following example: even though `spl`

and `bspl`

compare equal, their `support`

differs:

```
julia> spl = Spline(b, [0,0,1,0,0,0,0,0])
Spline{BSplineBasis{UnitRange{Int64}}, Vector{Int64}}:
basis: 8-element BSplineBasis{UnitRange{Int64}}:
order: 4
breakpoints: 0:5
coeffs: [0, 0, 1, 0, 0, 0, 0, 0]
julia> bspl = b[3]
BSpline{BSplineBasis{UnitRange{Int64}}}:
basis: 8-element BSplineBasis{UnitRange{Int64}}:
order: 4
breakpoints: 0:5
index: 3 (knots: [0, 0, 1, 2, 3])
julia> spl == bspl
true
julia> support(spl) # support of the basis
(0, 5)
julia> support(bspl) # actual support of the B-spline
(0, 3)
```