C11/C++ library for least-squares polynomial regression. Aims to have a pleasant, portable API with plenty of fluff.
| WasabiThumb/polyfit | natedomin/polyfit | henryfo/polyfit | |
|---|---|---|---|
| Test coverage | ✅ | ✅ | ✅ |
| Unbounded | ✅ | ❌1 | ✅ |
| Stringification | ✅ | ❌ | ✅ |
| Memory safety | ✅ | ✅ | ❌2 |
| Error handling | ✅ | ✅3 | ✅3 4 |
| Point set abstraction | ✅ | ❌ | ❌ |
| Evaluation | ✅ | ❌ | ❌5 |
| Non-allocating | ❌ | ✅ | ❌ |
- maxOrder can be increased, but bounded by the stack
- Buffers are not properly freed when errors arise
- Lacks strerror
- Elides multiple error states
- See open issue
pf_err_t pf_fit(const pf_points_t *points, unsigned int order, double *coefficients)
Fits an N-order polynomial to a set of input points (see point set abstraction).
pf_err_t pf_fit_easy(unsigned int count, const double *xs, const double *ys, unsigned int, double *coefficients)
Fits an N-order polynomial to a set of input points.
const char *pf_strerror(pf_err_t code)
Returns a human-readable string describing a polyfit status code.
size_t pf_strpoly(pf_len_t order, const double *coefficients, char *buf, size_t len)
Writes into "buf" a string representing a polynomial.
double pf_eval(pf_len_t order, const double *coefficients, double x);
Computes the value of a polynomial expression for a given x
While optimal for many use cases, the const double *xs, const double *ys signature
may cause unecessary allocations and/or memcpy calls. Consider the case where you have
an array of vector structs:
typedef struct {
double x;
double y;
} v2;
v2 arr[N_POINTS];This array is fundamentally incompatible with the aforementioned signature. Instead of mangling this
data into the format demanded by the signature, we can instead use the pf_points_t abstraction:
struct pf_points_by_v2s {
pf_points_t super;
const v2 *v2s;
};
static double pf_points_by_v2s_x(const pf_points_t *self, pf_len_t idx) {
return ((struct pf_points_by_v2s *) self)->v2s[idx].x;
}
static double pf_points_by_v2s_y(const pf_points_t *self, pf_len_t idx) {
return ((struct pf_points_by_v2s *) self)->v2s[idx].y;
}
//
struct pf_points_by_v2s points_impl;
points_impl.super.count = N_POINTS;
points_impl.super.x = pf_points_by_v2s_x;
points_impl.super.y = pf_points_by_v2s_y;
points_impl.v2s = arr;
pf_points_t *points = &points_impl.super;
// pass "points" to pf_fitThis involves 0 heap allocations, and is a reasonable tradeoff given that each point is only read approximately once by the internal algorithm. This also means that the x/y accessor may be fairly expensive while incurring no additional penalty.