diff --git a/ckiwi/ckiwi.cpp b/ckiwi/ckiwi.cpp index f888123..4c36f43 100644 --- a/ckiwi/ckiwi.cpp +++ b/ckiwi/ckiwi.cpp @@ -99,7 +99,7 @@ using ConstraintRef = SharedRef; using VariableRef = SharedRef; using ConstVariableRef = const SharedRef; -KiwiErrPtr new_error(KiwiErrPtr base, const std::exception& ex) { +const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) { if (!std::strcmp(ex.what(), base->message)) return base; @@ -128,7 +128,7 @@ static const constexpr KiwiErr kKiwiErrNullObjectArg1 { "null object passed as argument #1"}; template -inline KiwiErrPtr wrap_err(F&& f) { +inline const KiwiErr* wrap_err(F&& f) { try { f(); } catch (const UnsatisfiableConstraint& ex) { @@ -183,7 +183,7 @@ inline KiwiErrPtr wrap_err(F&& f) { } template -inline KiwiErrPtr wrap_err(P ptr, F&& f) { +inline const KiwiErr* wrap_err(P ptr, F&& f) { if (!ptr) { return &kKiwiErrNullObjectArg0; } @@ -191,7 +191,7 @@ inline KiwiErrPtr wrap_err(P ptr, F&& f) { } template -inline KiwiErrPtr wrap_err(P ptr, R ref, F&& f) { +inline const KiwiErr* wrap_err(P ptr, R ref, F&& f) { if (!ptr) { return &kKiwiErrNullObjectArg0; } else if (!ref) { @@ -245,23 +245,45 @@ int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other) { return self && other_ref && self->equals(other_ref); } -KiwiConstraintRef -kiwi_constraint_new(KiwiExpressionConstPtr expression, enum KiwiRelOp op, double strength) { +void kiwi_expression_del_vars(KiwiExpression* expr) { + if (!expr) + return; + + for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) { + VariableRef(t->var).release(); + } +} + +KiwiConstraintRef kiwi_constraint_new( + const KiwiExpression* lhs, + const KiwiExpression* rhs, + enum KiwiRelOp op, + double strength +) { if (strength < 0.0) { strength = kiwi::strength::required; } - std::vector terms; - if (expression) { - terms.reserve(expression->term_count); - for (auto* t = expression->terms; t != expression->terms + expression->term_count; ++t) { + std::vector terms; + terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0)); + + if (lhs) { + for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) { ConstVariableRef var(t->var); if (var) terms.emplace_back(var.cref(), t->coefficient); } } + if (rhs) { + for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) { + ConstVariableRef var(t->var); + if (var) + terms.emplace_back(var.cref(), -t->coefficient); + } + } + return make_constraint_cref( - Expression(std::move(terms), expression->constant), + Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)), static_cast(op), strength ); @@ -311,17 +333,17 @@ int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out return n_terms; } -KiwiSolverPtr kiwi_solver_new() { - return reinterpret_cast(new (std::nothrow) Solver()); +KiwiSolver* kiwi_solver_new() { + return reinterpret_cast(new Solver()); } -void kiwi_solver_del(KiwiSolverPtr sp) { +void kiwi_solver_del(KiwiSolver* sp) { auto* solver = reinterpret_cast(sp); if (solver) delete solver; } -KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) { +const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraintRef constraint) { return wrap_err( reinterpret_cast(s), ConstraintRef(constraint), @@ -329,7 +351,7 @@ KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr s, KiwiConstraintRef constra ); } -KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) { +const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraintRef constraint) { return wrap_err( reinterpret_cast(s), ConstraintRef(constraint), @@ -337,15 +359,15 @@ KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr s, KiwiConstraintRef cons ); } -bool kiwi_solver_has_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) { - const auto* solver = reinterpret_cast(s); +bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraintRef constraint) { + const auto* solver = reinterpret_cast(s); ConstraintRef c(constraint); if (!solver || !c) return 0; return solver->hasConstraint(c); } -KiwiErrPtr kiwi_solver_add_edit_var(KiwiSolverPtr s, KiwiVarRef var, double strength) { +const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVarRef var, double strength) { return wrap_err( reinterpret_cast(s), VariableRef(var), @@ -353,21 +375,21 @@ KiwiErrPtr kiwi_solver_add_edit_var(KiwiSolverPtr s, KiwiVarRef var, double stre ); } -KiwiErrPtr kiwi_solver_remove_edit_var(KiwiSolverPtr s, KiwiVarRef var) { +const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVarRef var) { return wrap_err(reinterpret_cast(s), VariableRef(var), [](auto solver, const auto v) { solver->removeEditVariable(v); }); } -bool kiwi_solver_has_edit_var(KiwiSolverPtr s, KiwiVarRef var) { - const auto* solver = reinterpret_cast(s); +bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVarRef var) { + const auto* solver = reinterpret_cast(s); VariableRef v(var); if (!solver || !v) return 0; return solver->hasEditVariable(v); } -KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr s, KiwiVarRef var, double value) { +const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVarRef var, double value) { return wrap_err( reinterpret_cast(s), VariableRef(var), @@ -376,30 +398,30 @@ KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr s, KiwiVarRef var, double val ); } -void kiwi_solver_update_vars(KiwiSolverPtr s) { +void kiwi_solver_update_vars(KiwiSolver* s) { auto* solver = reinterpret_cast(s); if (solver) solver->updateVariables(); } -void kiwi_solver_reset(KiwiSolverPtr s) { +void kiwi_solver_reset(KiwiSolver* s) { auto* solver = reinterpret_cast(s); if (solver) solver->reset(); } -void kiwi_solver_dump(KiwiSolverPtr s) { - auto* solver = reinterpret_cast(s); +void kiwi_solver_dump(const KiwiSolver* s) { + auto* solver = reinterpret_cast(s); if (solver) - solver->dump(); + const_cast(solver)->dump(); // upstream library defect } -char* kiwi_solver_dumps(KiwiSolverPtr s) { - auto* solver = reinterpret_cast(s); +char* kiwi_solver_dumps(const KiwiSolver* s) { + auto* solver = reinterpret_cast(s); if (!solver) return nullptr; - const auto str = solver->dumps(); + const auto str = const_cast(solver)->dumps(); // upstream library defect const auto buf_size = str.size() + 1; auto* buf = static_cast(std::malloc(buf_size)); if (!buf) diff --git a/ckiwi/ckiwi.h b/ckiwi/ckiwi.h index e63ff7d..4982cf3 100644 --- a/ckiwi/ckiwi.h +++ b/ckiwi/ckiwi.h @@ -26,33 +26,27 @@ enum KiwiErrKind { enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ }; -struct KiwiVarRefType; -struct KiwiConstraintRefType; - typedef struct KiwiVarRefType* KiwiVarRef; typedef struct KiwiConstraintRefType* KiwiConstraintRef; -struct KiwiTerm { +typedef struct KiwiTerm { KiwiVarRef var; double coefficient; -}; +} KiwiTerm; typedef struct KiwiExpression { double constant; int term_count; - struct KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?]; -}* KiwiExpressionPtr; - -typedef const struct KiwiExpression* KiwiExpressionConstPtr; + KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?]; +} KiwiExpression; typedef struct KiwiErr { enum KiwiErrKind kind; const char* message; bool must_free; -} const* KiwiErrPtr; +} KiwiErr; -struct KiwiSolver; -typedef struct KiwiSolver* KiwiSolverPtr; +typedef struct KiwiSolver KiwiSolver; KiwiVarRef kiwi_var_new(const char* name); void kiwi_var_del(KiwiVarRef var); @@ -64,32 +58,37 @@ double kiwi_var_value(KiwiVarRef var); void kiwi_var_set_value(KiwiVarRef var, double value); int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other); -KiwiConstraintRef -kiwi_constraint_new(KiwiExpressionConstPtr expression, enum KiwiRelOp op, double strength); +void kiwi_expression_del_vars(KiwiExpression* expr); + +KiwiConstraintRef kiwi_constraint_new( + const KiwiExpression* lhs, + const KiwiExpression* rhs, + enum KiwiRelOp op, + double strength +); void kiwi_constraint_del(KiwiConstraintRef constraint); KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint); double kiwi_constraint_strength(KiwiConstraintRef constraint); - enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint); - bool kiwi_constraint_violated(KiwiConstraintRef constraint); -int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpressionPtr out, int out_size); +int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size); -KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint); -KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint); -bool kiwi_solver_has_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint); -KiwiErrPtr kiwi_solver_add_edit_var(KiwiSolverPtr sp, KiwiVarRef var, double strength); -KiwiErrPtr kiwi_solver_remove_edit_var(KiwiSolverPtr sp, KiwiVarRef var); -bool kiwi_solver_has_edit_var(KiwiSolverPtr sp, KiwiVarRef var); -KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr sp, KiwiVarRef var, double value); -void kiwi_solver_update_vars(KiwiSolverPtr sp); -void kiwi_solver_reset(KiwiSolverPtr sp); -void kiwi_solver_dump(KiwiSolverPtr sp); -char* kiwi_solver_dumps(KiwiSolverPtr sp); +KiwiSolver* kiwi_solver_new(); +void kiwi_solver_del(KiwiSolver* sp); + +const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); +const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); +bool kiwi_solver_has_constraint(const KiwiSolver* sp, KiwiConstraintRef constraint); +const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* sp, KiwiVarRef var, double strength); +const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* sp, KiwiVarRef var); +bool kiwi_solver_has_edit_var(const KiwiSolver* sp, KiwiVarRef var); +const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* sp, KiwiVarRef var, double value); +void kiwi_solver_update_vars(KiwiSolver* sp); +void kiwi_solver_reset(KiwiSolver* sp); +void kiwi_solver_dump(const KiwiSolver* sp); +char* kiwi_solver_dumps(const KiwiSolver* sp); -KiwiSolverPtr kiwi_solver_new(); -void kiwi_solver_del(KiwiSolverPtr sp); // LuaJIT end #ifdef __cplusplus diff --git a/kiwi.lua b/kiwi.lua index 61c0a03..d8c2204 100644 --- a/kiwi.lua +++ b/kiwi.lua @@ -28,33 +28,27 @@ enum KiwiErrKind { enum KiwiRelOp { LE, GE, EQ }; -struct KiwiVarRefType; -struct KiwiConstraintRefType; - typedef struct KiwiVarRefType* KiwiVarRef; typedef struct KiwiConstraintRefType* KiwiConstraintRef; -struct KiwiTerm { +typedef struct KiwiTerm { KiwiVarRef var; double coefficient; -}; +} KiwiTerm; typedef struct KiwiExpression { double constant; int term_count; - struct KiwiTerm terms_[?]; -}* KiwiExpressionPtr; - -typedef const struct KiwiExpression* KiwiExpressionConstPtr; + KiwiTerm terms_[?]; +} KiwiExpression; typedef struct KiwiErr { enum KiwiErrKind kind; const char* message; bool must_free; -} const* KiwiErrPtr; +} KiwiErr; -struct KiwiSolver; -typedef struct KiwiSolver* KiwiSolverPtr; +typedef struct KiwiSolver KiwiSolver; KiwiVarRef kiwi_var_new(const char* name); void kiwi_var_del(KiwiVarRef var); @@ -66,38 +60,42 @@ double kiwi_var_value(KiwiVarRef var); void kiwi_var_set_value(KiwiVarRef var, double value); int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other); -KiwiConstraintRef -kiwi_constraint_new(KiwiExpressionConstPtr expression, enum KiwiRelOp op, double strength); +void kiwi_expression_del_vars(KiwiExpression* expr); + +KiwiConstraintRef kiwi_constraint_new( + const KiwiExpression* lhs, + const KiwiExpression* rhs, + enum KiwiRelOp op, + double strength +); void kiwi_constraint_del(KiwiConstraintRef constraint); +KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint); double kiwi_constraint_strength(KiwiConstraintRef constraint); - enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint); - bool kiwi_constraint_violated(KiwiConstraintRef constraint); -int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpressionPtr out, int out_size); +int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size); -KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint); -KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint); -bool kiwi_solver_has_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint); -KiwiErrPtr kiwi_solver_add_edit_var(KiwiSolverPtr sp, KiwiVarRef var, double strength); -KiwiErrPtr kiwi_solver_remove_edit_var(KiwiSolverPtr sp, KiwiVarRef var); -bool kiwi_solver_has_edit_var(KiwiSolverPtr sp, KiwiVarRef var); -KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr sp, KiwiVarRef var, double value); -void kiwi_solver_update_vars(KiwiSolverPtr sp); -void kiwi_solver_reset(KiwiSolverPtr sp); -void kiwi_solver_dump(KiwiSolverPtr sp); -char* kiwi_solver_dumps(KiwiSolverPtr sp); +KiwiSolver* kiwi_solver_new(); +void kiwi_solver_del(KiwiSolver* sp); -KiwiSolverPtr kiwi_solver_new(); -void kiwi_solver_del(KiwiSolverPtr sp); +const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); +const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); +bool kiwi_solver_has_constraint(const KiwiSolver* sp, KiwiConstraintRef constraint); +const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* sp, KiwiVarRef var, double strength); +const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* sp, KiwiVarRef var); +bool kiwi_solver_has_edit_var(const KiwiSolver* sp, KiwiVarRef var); +const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* sp, KiwiVarRef var, double value); +void kiwi_solver_update_vars(KiwiSolver* sp); +void kiwi_solver_reset(KiwiSolver* sp); +void kiwi_solver_dump(const KiwiSolver* sp); +char* kiwi_solver_dumps(const KiwiSolver* sp); void free(void *); ]]) local strformat = string.format -local ffi_cast, ffi_gc, ffi_istype, ffi_new, ffi_string = - ffi.cast, ffi.gc, ffi.istype, ffi.new, ffi.string +local ffi_gc, ffi_istype, ffi_new, ffi_string = ffi.gc, ffi.istype, ffi.new, ffi.string local concat = table.concat local has_table_new, new_tab = pcall(require, "table.new") @@ -161,46 +159,12 @@ kiwi.Expression = Expression local Constraint = ffi.typeof("struct KiwiConstraintRefType") --[[@as kiwi.Constraint]] kiwi.Constraint = Constraint ---- Define a constraint with expressions as `a <= b`. ----@param a kiwi.Expression|kiwi.Term|kiwi.Var|number ----@param b kiwi.Expression|kiwi.Term|kiwi.Var|number ----@param strength? number ----@return kiwi.Constraint -function kiwi.le(a, b, strength) - return Constraint(a - b, "LE", strength) -end - ---- Define a constraint with expressions as `a >= b`. ----@param a kiwi.Expression|kiwi.Term|kiwi.Var|number ----@param b kiwi.Expression|kiwi.Term|kiwi.Var|number ----@param strength? number ----@return kiwi.Constraint -function kiwi.ge(a, b, strength) - return Constraint(a - b, "GE", strength) -end - ---- Define a constraint with expressions as `a == b`. ----@param a kiwi.Expression|kiwi.Term|kiwi.Var|number ----@param b kiwi.Expression|kiwi.Term|kiwi.Var|number ----@param strength? number ----@return kiwi.Constraint -function kiwi.eq(a, b, strength) - return Constraint(a - b, "EQ", strength) -end - -local function expr_gc(expr) - local terms_ = expr.terms_ - for i = 0, expr.term_count - 1 do - ckiwi.kiwi_var_del(terms_[i].var) - end -end - ---@param expr kiwi.Expression ---@param var kiwi.Var ---@param coeff number? ---@nodiscard local function add_expr_term(expr, var, coeff) - local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), expr_gc) --[[@as kiwi.Expression]] + local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] for i = 0, expr.term_count - 1 do local st = expr.terms_[i] --[[@as kiwi.Term]] local dt = ret.terms_[i] --[[@as kiwi.Term]] @@ -219,8 +183,8 @@ end ---@param var kiwi.Var ---@param coeff number? ---@nodiscard -local function new_expr_one(constant, var, coeff) - local ret = ffi_gc(ffi_new(Expression, 1), expr_gc) --[[@as kiwi.Expression]] +local function new_expr_one_temp(constant, var, coeff) + local ret = ffi_new(Expression, 1) --[[@as kiwi.Expression]] local dt = ret.terms_[0] dt.var = ckiwi.kiwi_var_clone(var) dt.coefficient = coeff or 1.0 @@ -229,6 +193,14 @@ local function new_expr_one(constant, var, coeff) return ret end +---@param constant number +---@param var kiwi.Var +---@param coeff number? +---@nodiscard +local function new_expr_one(constant, var, coeff) + return ffi_gc(new_expr_one_temp(constant, var, coeff), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] ---@diagnostic disable-line: param-type-mismatch +end + ---@param constant number ---@param var1 kiwi.Var ---@param var2 kiwi.Var @@ -236,7 +208,7 @@ end ---@param coeff2 number? ---@nodiscard local function new_expr_pair(constant, var1, var2, coeff1, coeff2) - local ret = ffi_gc(ffi_new(Expression, 2), expr_gc) --[[@as kiwi.Expression]] + local ret = ffi_gc(ffi_new(Expression, 2), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local dt = ret.terms_[0] dt.var = ckiwi.kiwi_var_clone(var1) dt.coefficient = coeff1 or 1.0 @@ -248,6 +220,73 @@ local function new_expr_pair(constant, var1, var2, coeff1, coeff2) return ret end +local Strength = kiwi.Strength +local REQUIRED = Strength.REQUIRED + +---@param lhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param rhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param op kiwi.RelOp +---@param strength? number +local function rel(lhs, rhs, op, strength) + local function to_expr(o) + if ffi_istype(Expression, o) then + return o --[[@as kiwi.Expression]] + elseif type(o) == "number" then + if o == 0 then + return nil + end + local ret = ffi_new(Expression, 0) --[[@as kiwi.Expression]] + ret.constant = o + ret.term_count = 0 + return ret + end + local var + local coeff = 1.0 + + if ffi_istype(Var, o) then + var = o --[[@as kiwi.Var]] + elseif ffi_istype(Term, o) then + var = o.var + coeff = o.coefficient + else + error("Expected Expression|Term|Var|number, got " .. type(o) .. " instead") + end + return new_expr_one_temp(0.0, var, coeff) + end + + return ffi_gc( + ckiwi.kiwi_constraint_new(to_expr(lhs), to_expr(rhs), op, strength or REQUIRED), + ckiwi.kiwi_constraint_del + ) --[[@as kiwi.Constraint]] +end + +--- Define a constraint with expressions as `a <= b`. +---@param lhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param rhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param strength? number +---@return kiwi.Constraint +function kiwi.le(lhs, rhs, strength) + return rel(lhs, rhs, "LE", strength) +end + +--- Define a constraint with expressions as `a >= b`. +---@param lhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param rhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param strength? number +---@return kiwi.Constraint +function kiwi.ge(lhs, rhs, strength) + return rel(lhs, rhs, "GE", strength) +end + +--- Define a constraint with expressions as `a == b`. +---@param lhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param rhs kiwi.Expression|kiwi.Term|kiwi.Var|number +---@param strength? number +---@return kiwi.Constraint +function kiwi.eq(lhs, rhs, strength) + return rel(lhs, rhs, "EQ", strength) +end + --- Variables are the values the constraint solver calculates. ---@class kiwi.Var: ffi.ctype* ---@overload fun(name: string): kiwi.Var @@ -287,13 +326,20 @@ function Var_cls:toterm(coefficient) return Term(self, coefficient or 1.0) end +--- Create a term from this variable. +---@param coefficient number? +---@param constant number? +---@return kiwi.Expression +function Var_cls:toexpr(coefficient, constant) + return new_expr_one(constant or 0.0, self, coefficient) +end + ffi.metatype(Var, { __index = Var_cls, __new = function(_, name) return ffi_gc(ckiwi.kiwi_var_new(name), ckiwi.kiwi_var_del) end, - __gc = ckiwi.kiwi_var_del, __mul = function(a, b) if type(a) == "number" then @@ -426,7 +472,7 @@ do ---@param constant number ---@nodiscard local function mul_expr_constant(expr, constant) - local ret = ffi_gc(ffi_new(Expression, expr.term_count), expr_gc) --[[@as kiwi.Expression]] + local ret = ffi_gc(ffi_new(Expression, expr.term_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] for i = 0, expr.term_count - 1 do local st = expr.terms_[i] --[[@as kiwi.Term]] local dt = ret.terms_[i] --[[@as kiwi.Term]] @@ -444,7 +490,7 @@ do local function add_expr_expr(a, b) local a_count = a.term_count local b_count = b.term_count - local ret = ffi_gc(ffi_new(Expression, a_count + b_count), expr_gc) --[[@as kiwi.Expression]] + local ret = ffi_gc(ffi_new(Expression, a_count + b_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] for i = 0, a_count - 1 do local dt = ret.terms_[i] --[[@as kiwi.Term]] @@ -467,7 +513,7 @@ do ---@param constant number ---@nodiscard local function new_expr_constant(expr, constant) - local ret = ffi_gc(ffi_new(Expression, expr.term_count), expr_gc) --[[@as kiwi.Expression]] + local ret = ffi_gc(ffi_new(Expression, expr.term_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] for i = 0, expr.term_count - 1 do local dt = ret.terms_[i] --[[@as kiwi.Term]] @@ -528,7 +574,7 @@ do __index = Expression_cls, __new = function(_, terms, constant) - local e = ffi_gc(ffi_new(Expression, #terms), expr_gc) --[[@as kiwi.Expression]] + local e = ffi_gc(ffi_new(Expression, #terms), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] for i, t in ipairs(terms) do local dt = e.terms_[i - 1] --[[@as kiwi.Term]] dt.var = ckiwi.kiwi_var_clone(t.var) @@ -593,7 +639,7 @@ end --- Constraints can be built with arbitrary left and right hand expressions. But --- ultimately they all have the form `expression [op] 0`. ---@class kiwi.Constraint: ffi.ctype* ----@overload fun(expr: kiwi.Expression, op: kiwi.RelOp?, strength: number?): kiwi.Constraint +---@overload fun(lhs: kiwi.Expression, rhs: kiwi.Expression?, op: kiwi.RelOp?, strength: number?): kiwi.Constraint local Constraint_cls = { --- The strength of the constraint. ---@type fun(self: kiwi.Constraint): number @@ -619,7 +665,7 @@ function Constraint_cls:expression() expr = ffi_new(Expression, n) --[[@as kiwi.Expression]] n = ckiwi.kiwi_constraint_expression(self, expr, n) end - return ffi_gc(expr, expr_gc) --[[@as kiwi.Expression]] ---@diagnostic disable-line: param-type-mismatch + return ffi_gc(expr, ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] ---@diagnostic disable-line: param-type-mismatch end --- Create a constraint between a pair of variables with ratio. @@ -634,9 +680,20 @@ end ---@nodiscard function kiwi.new_pair_ratio_constraint(left, coeff, right, constant, op, strength) assert(ffi_istype(Var, left) and ffi_istype(Var, right)) - return Constraint(new_expr_pair(-(constant or 0.0), right, left, -coeff), op, strength) + local lhs = ffi_new(Expression, 2) --[[@as kiwi.Expression]] + local dt = lhs.terms_[0] + dt.var = left + dt.coefficient = 1.0 + dt = lhs.terms_[1] + dt.var = right + dt.coefficient = -coeff + lhs.constant = -(constant or 0.0) + lhs.term_count = 2 + return Constraint(lhs, nil, op, strength) end +local new_pair_ratio_constraint = kiwi.new_pair_ratio_constraint + --- Create a constraint between a pair of variables with ratio. --- The constraint is of the form `left [op|==] right + [constant|0.0]`. ---@param left kiwi.Var @@ -647,8 +704,7 @@ end ---@return kiwi.Constraint ---@nodiscard function kiwi.new_pair_constraint(left, right, constant, op, strength) - assert(ffi_istype(Var, left) and ffi_istype(Var, right)) - return Constraint(new_expr_pair(-(constant or 0.0), right, left, -1.0), op, strength) + return new_pair_ratio_constraint(left, 1.0, right, constant, op, strength) end --- Create a single term constraint @@ -661,18 +717,21 @@ end ---@nodiscard function kiwi.new_single_constraint(var, constant, op, strength) assert(ffi_istype(Var, var)) - return Constraint(new_expr_one(-(constant or 0.0), var), op, strength) + local lhs = ffi_new(Expression, 1) --[[@as kiwi.Expression]] + local dt = lhs.terms_[0] + dt.var = var + dt.coefficient = 1.0 + lhs.constant = -(constant or 0.0) + lhs.term_count = 1 + return Constraint(lhs, nil, op, strength) end -local Strength = kiwi.Strength -local REQUIRED = Strength.REQUIRED - ffi.metatype(Constraint, { __index = Constraint_cls, - __new = function(_, expr, op, strength) + __new = function(_, lhs, rhs, op, strength) return ffi_gc( - ckiwi.kiwi_constraint_new(expr, op or "EQ", strength or REQUIRED), + ckiwi.kiwi_constraint_new(lhs, rhs, op or "EQ", strength or REQUIRED), ckiwi.kiwi_constraint_del ) end, @@ -692,7 +751,7 @@ ffi.metatype(Constraint, { local strength = self:strength() return strformat( "%s %s 0 | %s", - self:expression(), + tostring(self:expression()), ops[tonumber(self:op())], strengths[strength] or tostring(strength) ) @@ -731,7 +790,7 @@ local C = ffi.C ---@field package must_free boolean ---@overload fun(): kiwi.KiwiErr local KiwiErr = ffi.typeof("struct KiwiErr") --[[@as kiwi.KiwiErr]] --- + ---@param f fun(solver: kiwi.Solver, item: any, ...): kiwi.KiwiErr? ---@param solver kiwi.Solver ---@param item any @@ -842,10 +901,6 @@ kiwi.Solver = ffi.metatype("struct KiwiSolver", { __new = function(_) return ffi_gc(ckiwi.kiwi_solver_new(), ckiwi.kiwi_solver_del) end, - - __tostring = function(self) - return strformat("kiwi.Solver(0x%X)", ffi_cast("intptr_t", ffi_cast("void*", self))) - end, }) --[[@as kiwi.Solver]] return kiwi