Workspace
Reusable scratch for operations that would otherwise allocate their temporaries on every call.
Buffers are pooled by width: an operation borrows a vector of the length it needs and returns it, and the workspace hands out a different buffer for every borrow that is outstanding at the same time. That is what makes nesting safe without any central registry — a condition estimate borrowing four vectors and then calling a triangular solve that borrows a fifth simply grows that width's pool to five. One workspace can therefore serve several dimensions at once, and no two call sites can collide by accident.
A pool grows on demand and never shrinks, so a loop allocates during its first iterations and nothing afterwards. Use reserve to pay that cost up front instead.
Borrowed contents are undefined: an operation that needs zeros clears what it uses.
Not thread-safe, deliberately. A workspace is caller-owned state, so give each solver instance (or each thread) its own; sharing one across concurrent operations corrupts results. Passing none keeps the allocating behaviour, which is always correct.
val ws = Workspace().apply { reserve(n, count = 5) }
val x = DoubleArray(n)
repeat(iterations) {
koblas.solveInto(lu, b, x, workspace = ws)
if (koblas.rcond(lu, anorm, ws) < threshold) koblas.factorInto(a, lu)
}