There are many packages in R that provide B-spline bases (packages splines and fda come to mind). I decided to ignore all this excellent prior work and write my own, using the .C() interface. I know, I know, there is Rcpp. But ever since I first heard about C++ in the early eighties I have convinced myself that I am too old to learn that high-level stuff. For me R is mainly a prototyping environment for optimization and numerics, and a convenient wrapper around compiled C and FORTRAN subroutines and libraries.

The code in this note is a straightforward C translation of FORTRAN code by Samiran Sinha, who has a nice pdf document with some details.

The R function bsplineBasis() requires a vector x of values where the splines are evaluated, a spline degree d, and a vector of interior knots. The boundary knots are d+1 copies of lowknot and d+1 copies of highknot. It is assumed that the elements of x are between lowknot and highknot, and that the interior knots are increasing (and thus all different). There may be no interior knots, i.e. innerknots=numeric(0), in which case we generate a basis for the polynomials.

We give the R code, the C code, and some simple runs.

bsplineBasis <-
  function (x, degree, innerknots, lowknot = min(x,innerknots), highknot = max(x,innerknots)) {
  innerknots <- unique (sort (innerknots))
  knots <-
  c(rep(lowknot, degree + 1), innerknots, rep(highknot, degree + 1))
  n <- length (x)
  m <- length (innerknots) + 2 * (degree + 1)
  nf <- length (innerknots) + degree + 1
  basis <- rep (0,  n * nf)
  res <- .C(
  "splinebasis", d = as.integer(degree),
  n = as.integer(n), m = as.integer (m), x = as.double (x), knots = as.double (knots), basis = as.double(basis)
  )
  basis <- matrix (res$basis, n, nf)
  basis <- basis[,which(colSums(basis) > 0)]
  return (basis)
  }
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

double bs (int nknots, int nspline, int degree, double x, double * knots);
int mindex (int i, int j, int nrow);

void splinebasis (int *d, int *n, int *m, double * x, double * knots, double * basis) {
    int mm = *m, dd = *d, nn = *n;
    int k = mm - dd - 1, i , j, ir, jr;
    for (i = 0; i < nn; i++) {
        ir = i + 1;
        if (x[i] == knots[mm - 1]) {
           basis [mindex (ir, k, nn) - 1] =  1.0;
            for (j = 0; j <  (k - 1);  j++) {
                jr = j + 1;
                basis [mindex (ir, jr, nn) - 1] = 0.0;
            }
        } else {
            for (j = 0; j < k ; j++) {
                jr = j + 1;
                basis [mindex (ir, jr, nn) - 1] = bs (mm, jr, dd + 1, x[i], knots);
            }
        }
    }
}

int mindex (int i, int j, int nrow) {
    return (j - 1) * nrow + i;
}

double bs (int nknots, int nspline, int updegree, double x, double * knots) {
    double y, y1, y2, temp1, temp2;
     if (updegree == 1) {
        if ((x >= knots[nspline - 1]) && (x < knots[nspline]))
            y = 1.0;
        else
            y = 0.0;
     }
     else {
        temp1 = 0.0;
        if ((knots[nspline + updegree - 2] - knots[nspline - 1]) > 0)
            temp1 = (x - knots[nspline - 1]) / (knots[nspline + updegree - 2] - knots[nspline - 1]);
        temp2 = 0.0;
        if ((knots[nspline + updegree - 1] - knots[nspline]) > 0)
            temp2 = (knots[nspline + updegree - 1] - x) / (knots[nspline + updegree - 1] - knots[nspline]);
        y1 = bs(nknots, nspline, updegree - 1, x, knots);
        y2 = bs(nknots, nspline + 1, updegree - 1, x, knots);
        y =  temp1 * y1 + temp2 * y2;
    }
    return y;
}
set.seed <- 12345
x <- rnorm(10)
print(b<-bsplineBasis (x, 0, c(-2,-1,0,1,2)))
##       [,1] [,2] [,3] [,4]
##  [1,]    0    0    0    1
##  [2,]    0    0    0    1
##  [3,]    0    1    0    0
##  [4,]    0    1    0    0
##  [5,]    0    1    0    0
##  [6,]    0    0    1    0
##  [7,]    0    0    1    0
##  [8,]    0    0    1    0
##  [9,]    0    0    0    1
## [10,]    1    0    0    0
rowSums(b)
##  [1] 1 1 1 1 1 1 1 1 1 1
print(b<-bsplineBasis (x, 2, c(-2,-1,0,1,2),-3,3))
##             [,1]        [,2]      [,3]        [,4]      [,5]       [,6]
##  [1,] 0.00000000 0.000000000 0.0000000 0.000000000 0.4410854 0.55706865
##  [2,] 0.00000000 0.000000000 0.0000000 0.000000000 0.3423986 0.64272756
##  [3,] 0.00000000 0.013004617 0.6352646 0.351730830 0.0000000 0.00000000
##  [4,] 0.00000000 0.084227989 0.7419779 0.173794099 0.0000000 0.00000000
##  [5,] 0.00000000 0.002331621 0.5636247 0.434043689 0.0000000 0.00000000
##  [6,] 0.00000000 0.000000000 0.0000000 0.074742124 0.7371477 0.18811020
##  [7,] 0.00000000 0.000000000 0.0000000 0.005649418 0.5949971 0.39935343
##  [8,] 0.00000000 0.000000000 0.0000000 0.291817461 0.6803251 0.02785743
##  [9,] 0.00000000 0.000000000 0.0000000 0.000000000 0.1174929 0.74976754
## [10,] 0.00690014 0.572716869 0.4203830 0.000000000 0.0000000 0.00000000
##              [,7]
##  [1,] 0.001845918
##  [2,] 0.014873861
##  [3,] 0.000000000
##  [4,] 0.000000000
##  [5,] 0.000000000
##  [6,] 0.000000000
##  [7,] 0.000000000
##  [8,] 0.000000000
##  [9,] 0.132739526
## [10,] 0.000000000
rowSums(b)
##  [1] 1 1 1 1 1 1 1 1 1 1
print(b<-bsplineBasis (x, 4, c(-2,-1,0,1,2)))
##       [,1]         [,2]      [,3]         [,4]        [,5]        [,6]
##  [1,]    0 0.0000000000 0.0000000 0.000000e+00 0.032426060 0.282162657
##  [2,]    0 0.0000000000 0.0000000 0.000000e+00 0.019539465 0.214962181
##  [3,]    0 0.0005324626 0.2084478 5.343299e-01 0.236070762 0.020619096
##  [4,]    0 0.0223360715 0.4094825 4.563143e-01 0.106833058 0.005034065
##  [5,]    0 0.0000171163 0.1500423 5.265868e-01 0.291954887 0.031398987
##  [6,]    0 0.0000000000 0.0000000 1.207971e-03 0.149107784 0.508659540
##  [7,]    0 0.0000000000 0.0000000 6.901337e-06 0.062384684 0.382634910
##  [8,]    0 0.0000000000 0.0000000 1.841401e-02 0.328221637 0.524465003
##  [9,]    0 0.0000000000 0.0000000 0.000000e+00 0.002300765 0.056767092
## [10,]    1 0.0000000000 0.0000000 0.000000e+00 0.000000000 0.000000000
##            [,7]         [,8]         [,9]
##  [1,] 0.5271619 0.1582357249 1.362966e-05
##  [2,] 0.5301589 0.2344545663 8.849270e-04
##  [3,] 0.0000000 0.0000000000 0.000000e+00
##  [4,] 0.0000000 0.0000000000 0.000000e+00
##  [5,] 0.0000000 0.0000000000 0.000000e+00
##  [6,] 0.3233320 0.0176927228 0.000000e+00
##  [7,] 0.4752319 0.0797415821 0.000000e+00
##  [8,] 0.1285113 0.0003880183 0.000000e+00
##  [9,] 0.3524752 0.5179778105 7.047913e-02
## [10,] 0.0000000 0.0000000000 0.000000e+00
rowSums(b)
##  [1] 1 1 1 1 1 1 1 1 1 1