Refactors to produce slightly less garbage

This commit is contained in:
2024-02-14 10:09:49 -06:00
parent 84a01179cd
commit 3311b582a1
3 changed files with 229 additions and 153 deletions

View File

@@ -99,7 +99,7 @@ using ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
using VariableRef = SharedRef<Variable, KiwiVarRef>;
using ConstVariableRef = const SharedRef<const Variable, const KiwiVarRef>;
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<typename F>
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<typename P, typename R, typename F>
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<typename P, typename R, typename F>
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<Term> terms;
if (expression) {
terms.reserve(expression->term_count);
for (auto* t = expression->terms; t != expression->terms + expression->term_count; ++t) {
std::vector<Term> 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<RelationalOperator>(op),
strength
);
@@ -311,17 +333,17 @@ int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out
return n_terms;
}
KiwiSolverPtr kiwi_solver_new() {
return reinterpret_cast<KiwiSolverPtr>(new (std::nothrow) Solver());
KiwiSolver* kiwi_solver_new() {
return reinterpret_cast<KiwiSolver*>(new Solver());
}
void kiwi_solver_del(KiwiSolverPtr sp) {
void kiwi_solver_del(KiwiSolver* sp) {
auto* solver = reinterpret_cast<Solver*>(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<Solver*>(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<Solver*>(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<Solver*>(s);
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraintRef constraint) {
const auto* solver = reinterpret_cast<const Solver*>(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<Solver*>(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<Solver*>(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<Solver*>(s);
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVarRef var) {
const auto* solver = reinterpret_cast<const Solver*>(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<Solver*>(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<Solver*>(s);
if (solver)
solver->updateVariables();
}
void kiwi_solver_reset(KiwiSolverPtr s) {
void kiwi_solver_reset(KiwiSolver* s) {
auto* solver = reinterpret_cast<Solver*>(s);
if (solver)
solver->reset();
}
void kiwi_solver_dump(KiwiSolverPtr s) {
auto* solver = reinterpret_cast<Solver*>(s);
void kiwi_solver_dump(const KiwiSolver* s) {
auto* solver = reinterpret_cast<const Solver*>(s);
if (solver)
solver->dump();
const_cast<Solver*>(solver)->dump(); // upstream library defect
}
char* kiwi_solver_dumps(KiwiSolverPtr s) {
auto* solver = reinterpret_cast<Solver*>(s);
char* kiwi_solver_dumps(const KiwiSolver* s) {
auto* solver = reinterpret_cast<const Solver*>(s);
if (!solver)
return nullptr;
const auto str = solver->dumps();
const auto str = const_cast<Solver*>(solver)->dumps(); // upstream library defect
const auto buf_size = str.size() + 1;
auto* buf = static_cast<char*>(std::malloc(buf_size));
if (!buf)

View File

@@ -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

239
kiwi.lua
View File

@@ -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