#include "formula.h" #include #include #include #include struct parser { const char *p; double x; char err[128]; }; static void skip_ws(struct parser *ps) { while (*ps->p == ' ' || *ps->p == '\t') ps->p++; } static int parse_expr(struct parser *ps, double *out); static int parse_primary(struct parser *ps, double *out) { skip_ws(ps); if (*ps->p == '(') { ps->p++; if (parse_expr(ps, out) != 0) return -1; skip_ws(ps); if (*ps->p != ')') { snprintf(ps->err, sizeof ps->err, "saknar ')'"); return -1; } ps->p++; return 0; } if (*ps->p == 'x' || *ps->p == 'X') { ps->p++; *out = ps->x; return 0; } if ((*ps->p >= '0' && *ps->p <= '9') || *ps->p == '.') { char *end = NULL; double v = strtod(ps->p, &end); if (!end || end == ps->p) { snprintf(ps->err, sizeof ps->err, "ogiltigt tal"); return -1; } ps->p = end; *out = v; return 0; } snprintf(ps->err, sizeof ps->err, "förväntade tal, x eller '('"); return -1; } static int parse_factor(struct parser *ps, double *out) { skip_ws(ps); if (*ps->p == '-') { ps->p++; if (parse_factor(ps, out) != 0) return -1; *out = -*out; return 0; } if (*ps->p == '+') { ps->p++; return parse_factor(ps, out); } return parse_primary(ps, out); } static int parse_term(struct parser *ps, double *out) { double left; if (parse_factor(ps, &left) != 0) return -1; for (;;) { skip_ws(ps); char op = *ps->p; if (op != '*' && op != '/') break; ps->p++; double right; if (parse_factor(ps, &right) != 0) return -1; if (op == '*') { left *= right; } else { if (right == 0.0) { snprintf(ps->err, sizeof ps->err, "division med noll"); return -1; } left /= right; } } *out = left; return 0; } static int parse_expr(struct parser *ps, double *out) { double left; if (parse_term(ps, &left) != 0) return -1; for (;;) { skip_ws(ps); char op = *ps->p; if (op != '+' && op != '-') break; ps->p++; double right; if (parse_term(ps, &right) != 0) return -1; if (op == '+') left += right; else left -= right; } *out = left; return 0; } int formula_eval(const char *formula, double x, double *out_kr, char *errbuf, size_t errlen) { if (errbuf && errlen) errbuf[0] = '\0'; if (!formula || !*formula) { if (errbuf) snprintf(errbuf, errlen, "tom formel"); return -1; } struct parser ps; ps.p = formula; ps.x = x; ps.err[0] = '\0'; double out = 0; if (parse_expr(&ps, &out) != 0) { if (errbuf) snprintf(errbuf, errlen, "%s", ps.err); return -1; } skip_ws(&ps); if (*ps.p) { if (errbuf) snprintf(errbuf, errlen, "oväntat tecken '%c'", *ps.p); return -1; } if (!isfinite(out)) { if (errbuf) snprintf(errbuf, errlen, "ogiltigt resultat"); return -1; } *out_kr = out; return 0; } int formula_valid(const char *formula) { double v; return formula_eval(formula, 1.0, &v, NULL, 0) == 0; } int formula_resolve_rows(const struct template_row *rows, size_t nrows, double x, struct resolved_row *out, size_t *out_n, char *errbuf, size_t errlen) { if (errbuf && errlen) errbuf[0] = '\0'; size_t n = 0; for (size_t i = 0; i < nrows; i++) { double kr = 0; char err[128] = ""; if (formula_eval(rows[i].formula, x, &kr, err, sizeof err) != 0) { if (errbuf) snprintf(errbuf, errlen, "rad %zu: %s", i + 1, err); return -1; } int64_t ore = (int64_t)llround(kr * 100.0); if (ore == 0) continue; struct resolved_row *r = &out[n++]; memset(r, 0, sizeof *r); snprintf(r->account, sizeof r->account, "%s", rows[i].account); if (ore > 0) r->debit_ore = ore; else r->credit_ore = -ore; if (rows[i].description) snprintf(r->description, sizeof r->description, "%s", rows[i].description); } if (n < 2) { if (errbuf) snprintf(errbuf, errlen, "mallen gav färre än två rader med belopp"); return -1; } int64_t sum_d = 0, sum_c = 0; for (size_t i = 0; i < n; i++) { sum_d += out[i].debit_ore; sum_c += out[i].credit_ore; } int64_t diff = sum_d - sum_c; if (diff != 0) { /* the rounding remainder goes to the largest row */ size_t biggest = 0; int64_t best = -1; for (size_t i = 0; i < n; i++) { int64_t amount = out[i].debit_ore ? out[i].debit_ore : out[i].credit_ore; if (amount > best) { best = amount; biggest = i; } } struct resolved_row *r = &out[biggest]; if (r->debit_ore) { r->debit_ore -= diff; if (r->debit_ore < 0) { r->credit_ore = -r->debit_ore; r->debit_ore = 0; } } else { r->credit_ore += diff; if (r->credit_ore < 0) { r->debit_ore = -r->credit_ore; r->credit_ore = 0; } } if (r->debit_ore == 0 && r->credit_ore == 0) { if (errbuf) snprintf(errbuf, errlen, "kunde inte balansera avrundningen"); return -1; } } *out_n = n; return 0; }