81 lines
2.2 KiB
C++
81 lines
2.2 KiB
C++
#include "Formula.h"
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "tinyexpr.h"
|
|
|
|
namespace
|
|
{
|
|
// tinyexpr has no built-in min/max; expose them so config formulas can
|
|
// clamp (e.g. a floored overclaim penalty "max(0.5, 1 - 0.1*x)").
|
|
double formulaMin(double a, double b) { return a < b ? a : b; }
|
|
double formulaMax(double a, double b) { return a > b ? a : b; }
|
|
}
|
|
|
|
Formula::Formula(Formula&& other) noexcept
|
|
: m_source(std::move(other.m_source))
|
|
, m_x(std::move(other.m_x))
|
|
, m_expr(other.m_expr)
|
|
{
|
|
other.m_expr = nullptr;
|
|
}
|
|
|
|
Formula& Formula::operator=(Formula&& other) noexcept
|
|
{
|
|
if (this != &other)
|
|
{
|
|
te_free(m_expr);
|
|
m_source = std::move(other.m_source);
|
|
m_x = std::move(other.m_x);
|
|
m_expr = other.m_expr;
|
|
other.m_expr = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Formula::~Formula()
|
|
{
|
|
te_free(m_expr);
|
|
}
|
|
|
|
Formula Formula::compile(const std::string& source)
|
|
{
|
|
Formula result;
|
|
result.m_source = source;
|
|
result.m_x = std::make_unique<double>(0.0);
|
|
|
|
const te_variable variables[] = {
|
|
{ "x", result.m_x.get(), TE_VARIABLE, nullptr },
|
|
{ "min", reinterpret_cast<const void*>(&formulaMin), TE_FUNCTION2 | TE_FLAG_PURE, nullptr },
|
|
{ "max", reinterpret_cast<const void*>(&formulaMax), TE_FUNCTION2 | TE_FLAG_PURE, nullptr },
|
|
};
|
|
|
|
int errorPos = 0;
|
|
const int variableCount = static_cast<int>(sizeof(variables) / sizeof(variables[0]));
|
|
result.m_expr = te_compile(result.m_source.c_str(), variables, variableCount, &errorPos);
|
|
|
|
if (result.m_expr == nullptr)
|
|
{
|
|
throw std::runtime_error(
|
|
"Formula parse error at position " + std::to_string(errorPos)
|
|
+ " in \"" + source + "\"");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
double Formula::evaluate(double x) const
|
|
{
|
|
if (m_expr == nullptr)
|
|
{
|
|
throw std::runtime_error("Formula::evaluate called on uninitialized formula");
|
|
}
|
|
|
|
// The variable at *m_x is what the compiled tree dereferences during
|
|
// te_eval. Mutating it here does not affect logical const-ness of the
|
|
// formula — the formula itself is unchanged.
|
|
*m_x = x;
|
|
return te_eval(m_expr);
|
|
}
|
|
|