27 Commits

Author SHA1 Message Date
579d671a77 Update rockspec 2024-02-23 13:07:02 -06:00
61ba76c5a3 Cleanup 2024-02-23 12:56:04 -06:00
8854c0edbe add build.bat 2024-02-22 23:23:07 -06:00
00a9fda814 again 2024-02-22 22:57:27 -06:00
a8c1a10cab fix MSVC warnings 2024-02-22 22:40:53 -06:00
2a71914ed8 ok 2024-02-22 20:58:47 -06:00
dea448e46b better 2024-02-22 18:23:33 -06:00
3811d82212 skin this cat 2024-02-22 16:02:31 -06:00
2baee14c5d misc Lua Version difference fixes 2024-02-22 11:15:17 -06:00
198265ee36 get compiling with c++ 2024-02-22 10:15:01 -06:00
604e3df41f just starting 2024-02-22 03:54:51 -06:00
6a99504835 Minor fixes and API change
- fix stupid spaces in Makefile
- small type annotation fix
- Modify Expression constructor API to use varargs
- Update kiwi.lua to support future C API module
2024-02-21 22:14:24 -06:00
9b00e62d43 update github workflow 2024-02-19 21:31:59 -06:00
3631704544 update LICENSE 2024-02-19 09:20:46 -06:00
17f67b8879 use our own lua gh action 2024-02-18 00:20:41 -06:00
d83cc3468c Drop LuaJIT 2.0.5 support
Primary CI target is luajit-openresty. Even 2.1.0-beta3 is ancient.
2024-02-17 22:36:28 -06:00
b3cf6136a4 Convert enum values to number for LuaJIT 2.0.5 compat 2024-02-17 22:07:01 -06:00
2ffc5a333b All kinds of things
- Add modified BSD license
- Initial test runner github action
- Add is_{type} functions
- export kiwi.Error metatable such that it appears like a class.
- Allow Expression.new to take nil for terms
- Initial set of specs
2024-02-17 21:58:13 -06:00
d85796a038 More flexible error handling, convenience methods
Allow getting solver errors returned rather than raising error.
The API allows setting a mask of which error kinds raise vs return.

Also add some convenience methods:
- `add_to` and `remove_from` constraint methods to solver.
- add and remove multiple constraints and edit/suggest variables at once
2024-02-17 01:08:45 -06:00
37833f7b2b Remove now unnecessary const_casts. 2024-02-17 01:08:45 -06:00
59465d3142 Add const to Solver dump methods and Variable equals. 2024-02-17 01:08:29 -06:00
8b57e0c441 Use global scratch space for temporary objects, better errors 2024-02-15 16:23:57 -06:00
bc948d1a61 Restyle and limit scopes for quicker navigation 2024-02-14 16:00:49 -06:00
3311b582a1 Refactors to produce slightly less garbage 2024-02-14 13:53:14 -06:00
84a01179cd Manage GC data better
Make sure that all cdata references for variables is tracked. This is
done by attaching finalizers to Expression and Term objects. Some
effort is made to avoid creating tracked temporary objects (esp Terms)
now.
2024-02-14 01:42:56 -06:00
e43272487f Guard against most egregious mistakes in calling the library
LuaJIT FFI is not inherently memory safe and there is no way
to completely guard against the caller doing something that
will trample over memory, but we can get pretty close. Biggest
issue is that an empty table will stand-in for a ref struct with
a null ref. So check for that in all the calls. In the calls that
raise errors we now have a specific error for it. In the other
functions the "nil" object is handled quietly but without a nullptr
dereference and hopefully no UB.
2024-02-13 16:58:59 -06:00
59bdeedc18 Initial actual commit, awaiting CI 2024-02-12 18:05:41 -06:00
30 changed files with 1442 additions and 973 deletions

View File

@@ -7,25 +7,13 @@ jobs:
strategy:
fail-fast: false
matrix:
lua_version:
[
"luajit-openresty",
"luajit-2.1.0-beta3",
"luajit-git",
"5.4.6",
"5.1.5",
"5.3.6",
]
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
lua_version: ["luajit-openresty", "luajit-2.1.0-beta3", "luajit-git"]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1
if: ${{ !startsWith(matrix.lua_version, 'luajit-') }}
- name: Setup lua
uses: jkl1337/gh-actions-lua@master
with:
@@ -37,24 +25,12 @@ jobs:
luarocks install busted
luarocks install luacov-coveralls
- name: Build C library
run: |
${{ matrix.os == 'ubuntu-latest' && 'FSANITIZE=1' || '' }} luarocks make --no-install
run: make
- name: Run busted tests
run: |
${{ matrix.os == 'ubuntu-latest' && 'LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.6:/usr/lib/x86_64-linux-gnu/libstdc++.so.6:/usr/lib/x86_64-linux-gnu/libubsan.so.1' || '' }} busted -c -v
run: busted -c -v
- name: Report test coverage
if: success() && !startsWith(matrix.os, 'windows-') && startsWith(matrix.lua_version, 'luajit-')
if: success()
continue-on-error: true
run: luacov-coveralls -e .luarocks -e spec
env:
COVERALLS_REPO_TOKEN: ${{ github.token }}
finish:
if: always()
needs: busted
runs-on: ubuntu-latest
steps:
- name: Close coveralls build
uses: coverallsapp/github-action@v2
with:
parallel-finished: true

7
.gitignore vendored
View File

@@ -2,14 +2,7 @@
/lua
/lua_modules
/.luarocks
/config.mk
*.pch
*.gch
*.lib
*.so
*.o
*.obj
*.exp
*.dll
.cache/
compile_commands.json

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "LuaJIT",
"runtime.version": "Lua 5.4",
"runtime.path": [
"./?/init.lua",
"./?.lua",

145
Makefile
View File

@@ -1,131 +1,56 @@
CXX := $(CROSS)g++
CP := cp
RM := rm
SRCDIR := .
CC := $(CROSS)gcc
CFLAGS := -fPIC -O3
CXXFLAGS := -I$(SRCDIR)/kiwi -fno-rtti
#F_LTO := -flto=auto
CXXFLAGS_EXTRA := -std=c++14 -Wall $(F_LTO) -fstrict-flex-arrays=3
CFLAGS_EXTRA := -pedantic -std=c99 -Wall $(F_LTO)
LIBFLAG := -shared
LIB_EXT := $(if $(filter Windows_NT,$(OS)),dll,so)
LIB_EXT := so
LUA_INCDIR := /usr/include
SRCDIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
ifeq ($(OS),Windows_NT)
is_clang = $(filter %clang++,$(CXX))
is_gcc = $(filter %g++,$(CXX))
else
uname_s := $(shell uname -s)
ifeq ($(uname_s),Darwin)
CC := env MACOSX_DEPLOYMENT_TARGET=11.0 gcc
CXX := env MACOSX_DEPLOYMENT_TARGET=11.0 g++
LIBFLAG := -bundle -undefined dynamic_lookup
is_clang = 1
is_gcc =
else
is_clang = $(filter %clang++,$(CXX))
is_gcc = $(filter %g++,$(CXX))
ifeq ($(findstring gcc,$(CC)),gcc)
CXX := $(subst gcc,g++,$(CC))
CXXFLAGS += -std=c++14
ifneq ($(SANITIZE),)
CFLAGS += -fsanitize=undefined -fsanitize=address
endif
endif
OPTFLAG := -O2
SANITIZE_FLAGS := -fsanitize=undefined -fsanitize=address -fsanitize=alignment -fsanitize=bounds-strict \
-fsanitize=shift -fsanitize=unreachable -fsanitize=bool \
-fsanitize=enum
LTO_FLAGS := -flto=auto
-include config.mk
ifeq ($(origin LUAROCKS), command line)
CCFLAGS := $(CFLAGS)
override CFLAGS := -std=c99 $(CCFLAGS)
ifneq ($(filter %gcc,$(CC)),)
CXX := $(patsubst %gcc,%g++,$(CC))
else
ifneq ($(filter %clang,$(CC)),)
CXX := $(patsubst %clang,%clang++,$(CC))
endif
else
ifeq ($(CC),clang)
CXX := clang++
CXXFLAGS += -std=c++14
ifneq ($(SANITIZE),)
CFLAGS += -fsanitize=undefined -fsanitize=address
endif
else
CCFLAGS += -fPIC $(OPTFLAG)
override CFLAGS += -std=c99 $(CCFLAGS)
endif
CCFLAGS += -Wall -fvisibility=hidden -Wformat=2 -Wconversion -Wimplicit-fallthrough
ifdef FSANITIZE
CCFLAGS += $(SANITIZE_FLAGS)
endif
ifndef FNOLTO
CCFLAGS += $(LTO_FLAGS)
endif
ifneq ($(is_gcc),)
#PCH := ljkiwi.hpp.gch
else
ifneq ($(is_clang),)
override CXXFLAGS += -pedantic -Wno-c99-extensions
#PCH := ljkiwi.hpp.pch
endif
endif
override CPPFLAGS += -I$(SRCDIR) -I$(SRCDIR)/kiwi -I"$(LUA_INCDIR)"
override CXXFLAGS += -std=c++14 -fno-rtti $(CCFLAGS)
ifeq ($(OS),Windows_NT)
override CPPFLAGS += -DLUA_BUILD_AS_DLL
override LIBFLAG += "$(LUA_LIBDIR)/$(LUALIB)"
endif
ifdef LUA
LUA_VERSION ?= $(lastword $(shell "$(LUA)" -e "print(_VERSION)"))
endif
ifndef LUA_VERSION
LJKIWI_CKIWI := 1
else
ifeq ($(LUA_VERSION),5.1)
LJKIWI_CKIWI := 1
CXX := $(CC)
endif
endif
kiwi_lib_srcs := AssocVector.h constraint.h debug.h errors.h expression.h kiwi.h maptype.h \
row.h shareddata.h solver.h solverimpl.h strength.h symbol.h symbolics.h term.h \
util.h variable.h version.h
OBJS := ckiwi.o luakiwi.o
objs := luakiwi.o
ifdef LJKIWI_CKIWI
objs += ckiwi.o
endif
VPATH = $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
vpath %.cpp $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
vpath %.h $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi $(SRCDIR)/kiwi/kiwi
all: ljkiwi.$(LIB_EXT)
all: ckiwi.$(LIB_EXT)
install:
$(CP) -f ljkiwi.$(LIB_EXT) $(INST_LIBDIR)/ljkiwi.$(LIB_EXT)
$(CP) -f kiwi.lua $(INST_LUADIR)/kiwi.lua
cp -f ckiwi.$(LIB_EXT) $(INST_LIBDIR)/ckiwi.$(LIB_EXT)
cp -f kiwi.lua $(INST_LUADIR)/kiwi.lua
mostlyclean:
$(RM) -f ljkiwi.$(LIB_EXT) $(objs)
clean:
rm -f ckiwi.$(LIB_EXT) $(OBJS)
clean: mostlyclean
$(RM) -f $(PCH)
ckiwi.o: $(PCH) ckiwi.cpp ckiwi.h $(kiwi_lib_srcs)
luakiwi.o: $(PCH) luakiwi-int.h luacompat.h $(kiwi_lib_srcs)
$(PCH): $(kiwi_lib_srcs)
ckiwi.$(LIB_EXT): $(OBJS)
$(CXX) $(CXXFLAGS) $(CFLAGS) $(CFLAGS_EXTRA) -I$(SRCDIR)/kiwi $(LIBFLAG) -o $@ $^
ljkiwi.$(LIB_EXT): $(objs)
$(CXX) $(CCFLAGS) $(LIBFLAG) -o $@ $(objs)
ckiwi.o: ckiwi.h
luakiwi.o: ckiwi.h luakiwi-int.h luacompat.h
%.hpp.gch: %.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
%.hpp.pch: %.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -x c++-header $<
%.o: %.c
$(CC) -I$(LUA_INCDIR) $(CFLAGS) $(CFLAGS_EXTRA) -c -o $@ $<
%.o: %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
$(CXX) -I$(LUA_INCDIR) $(CXXFLAGS) $(CFLAGS) $(CXXFLAGS_EXTRA) -c -o $@ $<
.PHONY: all install clean mostlyclean
.PHONY: all install clean

View File

@@ -1,21 +0,0 @@
T= ljkiwi
OBJS= luakiwi.obj
lib: $T.dll
{luakiwi\}.cpp.obj:
$(CC) $(CFLAGS) /W4 /wd4200 /c /D_CRT_SECURE_NO_DEPRECATE /I. /I kiwi /I"$(LUA_INCDIR)" /EHs /Fo$@ $(CFLAGS) $<
$T.dll: $(OBJS)
link $(LIBFLAG) /out:$T.dll "$(LUA_LIBDIR)\$(LUALIB)" $(OBJS)
IF EXIST $T.dll.manifest mt -manifest $T.dll.manifest -outputresource:$T.dll;2
install: $T.dll
IF NOT EXIST "$(INST_LIBDIR)" mkdir "$(INST_LIBDIR)"
copy $T.dll "$(INST_LIBDIR)"
copy kiwi.lua "$(INST_LUADIR)"
clean:
del $T.dll $(OBJS) $T.lib $T.exp
IF EXIST $T.dll.manifest del $T.dll.manifest

View File

@@ -1,8 +1,8 @@
ljkiwi - Free LuaJIT FFI and Lua C API kiwi (Cassowary derived) constraint solver.
ljkiwi - Free LuaJIT FFI kiwi (Cassowary derived) constraint solver.
[![CI](https://github.com/jkl1337/ljkiwi/actions/workflows/busted.yml/badge.svg)](https://github.com/jkl1337/ljkiwi/actions/workflows/busted.yml)
[![Coverage Status](https://coveralls.io/repos/github/jkl1337/ljkiwi/badge.svg?branch=master)](https://coveralls.io/github/jkl1337/ljkiwi?branch=master)
[![luarocks](https://img.shields.io/luarocks/v/jkl/kiwi)](https://luarocks.org/modules/jkl/kiwi)
[![luarocks](https://img.shields.io/luarocks/v/jkl/ljkiwi)](https://luarocks.org/modules/jkl/ljkiwi)
# Introduction
@@ -10,8 +10,8 @@ Kiwi is a reasonably efficient C++ implementation of the Cassowary constraint so
Cassowary constraint solving is a technique that is particularly well suited to user interface layout. It is the algorithm Apple uses for iOS and OS X Auto Layout.
There are a few Lua implementations or attempts. The SILE typesetting system has a pure Lua implementation of the original Cassowary code, which appears to be correct but is quite slow. There are two extant Lua ports of Kiwi, one that is based on a C rewrite of Kiwi. However testing of these was not encouraging with either segfaults or incorrect results.
Since the C++ Kiwi library is well tested and widely used it was simpler to provide a LuaJIT FFI wrapper. There is also a Lua C API binding with support for 5.1 through 5.4.
This package has no dependencies other than a supported C++14 compiler to compile the included Kiwi library and a small C wrapper.
Since the C++ Kiwi library is well tested and widely used it was simpler to provide a LuaJIT FFI wrapper and use that.
This package has no dependencies other than a C++14 toolchain to compile the included Kiwi library and a small C wrapper.
The Lua API has a pure Lua expression builder. There is of course some overhead to this, however in most cases expression building is infrequent and the underlying structures can be reused.

2
build.bat Normal file
View File

@@ -0,0 +1,2 @@
cl /nologo /O2 /W4 /wd4200 /c /D_CRT_SECURE_NO_DEPRECATE /I "C:\Program Files\luarocks\include" /EHs /I kiwi luakiwi\luakiwi.cpp
link /DLL /out:ckiwi.dll /def:luakiwi.def /LIBPATH:"C:\Program Files\luarocks\lib" luakiwi.obj lua54.lib

View File

@@ -2,22 +2,102 @@
#include <kiwi/kiwi.h>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <string>
#if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
#define lk_likely(x) (__builtin_expect(((x) != 0), 1))
#define lk_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define lk_likely(x) (x)
#define lk_unlikely(x) (x)
#endif
using namespace kiwi;
namespace {
using namespace kiwi;
template<typename T, typename R, typename... Args>
inline R make_cref(Args&&... args) {
static_assert(
sizeof(R) >= sizeof(T), //NOLINT(bugprone-sizeof-expression)
"to_cref: R too small for T"
);
static_assert(alignof(R) >= alignof(T), "to_cref: R alignment too small for T");
R cref;
new (&cref) T(std::forward<Args>(args)...);
return cref;
}
template<typename... Args>
inline decltype(auto) make_var_cref(Args&&... args) {
return make_cref<Variable, KiwiVarRef>(std::forward<Args>(args)...);
}
template<typename... Args>
inline decltype(auto) make_constraint_cref(Args&&... args) {
return make_cref<Constraint, KiwiConstraintRef>(std::forward<Args>(args)...);
}
template<class T, class R>
class SharedRef {
private:
R& cref_;
public:
explicit SharedRef<T, R>(R& cref) : cref_(cref) {}
static_assert(
sizeof(R) >= sizeof(T), //NOLINT(bugprone-sizeof-expression)
"SharedRef<T,CS> CS too small for T"
);
R clone() const {
return make_cref<T, R>(cref());
}
void release() {
if (cref_) {
ptr()->~T();
cref_ = nullptr;
}
}
T* ptr() {
T* p;
void* s = &cref_;
std::memcpy(&p, &s, sizeof p); //NOLINT(bugprone-sizeof-expression)
return p;
}
const T* const_ptr() const {
const T* p;
const void* s = &cref_;
std::memcpy(&p, &s, sizeof p); //NOLINT(bugprone-sizeof-expression)
return p;
}
const T& cref() const {
return *const_ptr();
}
T* operator&() const {
return ptr();
}
T* operator->() {
return ptr();
}
const T* operator->() const {
return const_ptr();
}
operator const T&() const {
return cref();
}
explicit operator bool() const {
return cref_;
}
};
using ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
using VariableRef = SharedRef<Variable, KiwiVarRef>;
using ConstVariableRef = const SharedRef<const Variable, const KiwiVarRef>;
const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) {
if (!std::strcmp(ex.what(), base->message))
@@ -51,7 +131,7 @@ static const constexpr KiwiErr kKiwiErrNullObjectArg1 {
};
template<typename F>
const KiwiErr* wrap_err(F&& f) {
inline const KiwiErr* wrap_err(F&& f) {
try {
f();
} catch (const UnsatisfiableConstraint& ex) {
@@ -113,102 +193,78 @@ const KiwiErr* wrap_err(F&& f) {
}
template<typename P, typename R, typename F>
const KiwiErr* wrap_err(P self, F&& f) {
if (lk_unlikely(!self)) {
inline const KiwiErr* wrap_err(P ptr, F&& f) {
if (!ptr) {
return &kKiwiErrNullObjectArg0;
}
return wrap_err([&]() { f(self->solver); });
return wrap_err([&]() { f(ptr); });
}
template<typename P, typename R, typename F>
const KiwiErr* wrap_err(P* self, R* item, F&& f) {
if (lk_unlikely(!self)) {
inline const KiwiErr* wrap_err(P ptr, R ref, F&& f) {
if (!ptr) {
return &kKiwiErrNullObjectArg0;
} else if (lk_unlikely(!item)) {
} else if (!ref) {
return &kKiwiErrNullObjectArg1;
}
return wrap_err([&]() { f(self->solver, item); });
}
template<typename T, typename... Args>
T* make_unmanaged(Args... args) {
auto* o = new T(std::forward<Args>(args)...);
o->m_refcount = 1;
return o;
}
template<typename T>
void release_unmanaged(T* p) {
if (lk_likely(p)) {
if (--p->m_refcount == 0)
delete p;
}
}
template<typename T>
T* retain_unmanaged(T* p) {
if (lk_likely(p))
p->m_refcount++;
return p;
return wrap_err([&]() { f(ptr, ref); });
}
} // namespace
extern "C" {
KiwiVar* kiwi_var_construct(const char* name) {
return make_unmanaged<VariableData>(lk_likely(name) ? name : "");
KiwiVarRef kiwi_var_new(const char* name) {
return make_var_cref(name ? name : "");
}
void kiwi_var_release(KiwiVar* var) {
release_unmanaged(var);
void kiwi_var_del(KiwiVarRef var) {
VariableRef(var).release();
}
void kiwi_var_retain(KiwiVar* var) {
retain_unmanaged(var);
KiwiVarRef kiwi_var_clone(KiwiVarRef var) {
return VariableRef(var).clone();
}
const char* kiwi_var_name(const KiwiVar* var) {
return lk_likely(var) ? var->name().c_str() : "(<null>)";
const char* kiwi_var_name(KiwiVarRef var) {
const VariableRef self(var);
return self ? self->name().c_str() : "(<null>)";
}
void kiwi_var_set_name(KiwiVar* var, const char* name) {
if (lk_likely(var && name))
var->setName(name);
void kiwi_var_set_name(KiwiVarRef var, const char* name) {
VariableRef self(var);
if (self)
self->setName(name);
}
double kiwi_var_value(const KiwiVar* var) {
return lk_likely(var) ? var->value() : std::numeric_limits<double>::quiet_NaN();
double kiwi_var_value(KiwiVarRef var) {
const VariableRef self(var);
return self ? self->value() : std::numeric_limits<double>::quiet_NaN();
}
void kiwi_var_set_value(KiwiVar* var, double value) {
if (lk_likely(var))
var->setValue(value);
void kiwi_var_set_value(KiwiVarRef var, double value) {
VariableRef self(var);
if (self)
self->setValue(value);
}
void kiwi_expression_retain(KiwiExpression* expr) {
if (lk_unlikely(!expr))
return;
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
retain_unmanaged(t->var);
}
expr->owner = expr;
bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other) {
ConstVariableRef self(var); // const defect in upstream
const VariableRef other_ref(other);
return self && other_ref && self->equals(other_ref);
}
void kiwi_expression_destroy(KiwiExpression* expr) {
if (lk_unlikely(!expr || !expr->owner))
void kiwi_expression_del_vars(KiwiExpression* expr) {
if (!expr)
return;
if (expr->owner == expr) {
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
release_unmanaged(t->var);
}
} else {
release_unmanaged(static_cast<ConstraintData*>(expr->owner));
for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) {
VariableRef(t->var).release();
}
}
KiwiConstraint* kiwi_constraint_construct(
KiwiConstraintRef kiwi_constraint_new(
const KiwiExpression* lhs,
const KiwiExpression* rhs,
enum KiwiRelOp op,
@@ -219,73 +275,72 @@ KiwiConstraint* kiwi_constraint_construct(
}
std::vector<Term> terms;
terms.reserve(static_cast<decltype(terms)::size_type>(
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
));
terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0));
if (lhs) {
for (int i = 0; i < lhs->term_count; ++i) {
const auto& t = lhs->terms_[i];
if (t.var)
terms.emplace_back(Variable(t.var), t.coefficient);
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 (int i = 0; i < rhs->term_count; ++i) {
const auto& t = rhs->terms_[i];
if (t.var)
terms.emplace_back(Variable(t.var), -t.coefficient);
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_unmanaged<ConstraintData>(
return make_constraint_cref(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op),
strength
);
}
void kiwi_constraint_release(KiwiConstraint* c) {
release_unmanaged(c);
void kiwi_constraint_del(KiwiConstraintRef constraint) {
ConstraintRef(constraint).release();
}
void kiwi_constraint_retain(KiwiConstraint* c) {
retain_unmanaged(c);
KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint) {
return ConstraintRef(constraint).clone();
}
double kiwi_constraint_strength(const KiwiConstraint* c) {
return lk_likely(c) ? c->strength() : std::numeric_limits<double>::quiet_NaN();
double kiwi_constraint_strength(KiwiConstraintRef constraint) {
const ConstraintRef self(constraint);
return self ? self->strength() : std::numeric_limits<double>::quiet_NaN();
}
enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c) {
return lk_likely(c) ? static_cast<KiwiRelOp>(c->op()) : KiwiRelOp::KIWI_OP_EQ;
enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint) {
const ConstraintRef self(constraint);
return self ? static_cast<KiwiRelOp>(self->op()) : KiwiRelOp::KIWI_OP_EQ;
}
bool kiwi_constraint_violated(const KiwiConstraint* c) {
return lk_likely(c) ? c->violated() : false;
bool kiwi_constraint_violated(KiwiConstraintRef constraint) {
const ConstraintRef self(constraint);
return self ? self->violated() : 0;
}
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size) {
if (lk_unlikely(!c))
int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size) {
const ConstraintRef self(constraint);
if (!self)
return 0;
const auto& expr = c->expression();
const auto& expr = self->expression();
const auto& terms = expr.terms();
int n = terms.size() < INT_MAX ? static_cast<int>(terms.size()) : INT_MAX;
if (!out || out_size < n)
return n;
const int n_terms = terms.size();
if (!out || out_size < n_terms)
return n_terms;
for (int i = 0; i < n; ++i) {
const auto& t = terms[static_cast<std::size_t>(i)];
out->terms_[i].var = const_cast<Variable&>(t.variable()).ptr();
out->terms_[i].coefficient = t.coefficient();
auto* p = out->terms;
for (const auto& t : terms) {
*p = KiwiTerm {make_var_cref(t.variable()), t.coefficient()};
++p;
}
out->term_count = p - out->terms;
out->constant = expr.constant();
out->term_count = n;
out->owner = retain_unmanaged(c);
return n;
return n_terms;
}
struct KiwiSolver {
@@ -293,78 +348,79 @@ struct KiwiSolver {
Solver solver;
};
KiwiSolver* kiwi_solver_construct(unsigned error_mask) {
KiwiSolver* kiwi_solver_new(unsigned error_mask) {
return new KiwiSolver {error_mask};
}
void kiwi_solver_destroy(KiwiSolver* s) {
if (lk_likely(s))
void kiwi_solver_del(KiwiSolver* s) {
if (s)
delete s;
}
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s) {
return lk_likely(s) ? s->error_mask : 0;
}
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask) {
if (lk_likely(s))
s->error_mask = mask;
}
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint) {
return wrap_err(s, constraint, [](auto&& s, auto&& c) { s.addConstraint(Constraint(c)); });
}
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint) {
return wrap_err(s, constraint, [](auto&& s, auto&& c) { s.removeConstraint(Constraint(c)); });
}
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint) {
if (lk_unlikely(!s || !constraint))
return 0;
return s->solver.hasConstraint(Constraint(constraint));
}
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength) {
return wrap_err(s, var, [strength](auto& s, auto&& v) {
s.addEditVariable(Variable(v), strength);
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraintRef constraint) {
return wrap_err(s, ConstraintRef(constraint), [](auto* s, const auto c) {
s->solver.addConstraint(c);
});
}
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var) {
return wrap_err(s, var, [](auto&& s, auto&& v) { s.removeEditVariable(Variable(v)); });
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraintRef constraint) {
return wrap_err(s, ConstraintRef(constraint), [](auto* s, const auto c) {
s->solver.removeConstraint(c);
});
}
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var) {
if (lk_unlikely(!s || !var))
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraintRef constraint) {
ConstraintRef c(constraint);
if (!s || !c)
return 0;
return s->solver.hasEditVariable(Variable(var));
return s->solver.hasConstraint(c);
}
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value) {
return wrap_err(s, var, [value](auto&& s, auto&& v) { s.suggestValue(Variable(v), value); });
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVarRef var, double strength) {
return wrap_err(s, VariableRef(var), [strength](auto* s, const auto v) {
s->solver.addEditVariable(v, strength);
});
}
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVarRef var) {
return wrap_err(s, VariableRef(var), [](auto* s, const auto v) {
s->solver.removeEditVariable(v);
});
}
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVarRef var) {
VariableRef v(var);
if (!s || !v)
return 0;
return s->solver.hasEditVariable(v);
}
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVarRef var, double value) {
return wrap_err(s, VariableRef(var), [value](auto* s, const auto v) {
s->solver.suggestValue(v, value);
});
}
void kiwi_solver_update_vars(KiwiSolver* s) {
if (lk_likely(s))
if (s)
s->solver.updateVariables();
}
void kiwi_solver_reset(KiwiSolver* s) {
if (lk_likely(s))
if (s)
s->solver.reset();
}
void kiwi_solver_dump(const KiwiSolver* s) {
if (lk_likely(s))
if (s)
s->solver.dump();
}
char* kiwi_solver_dumps(const KiwiSolver* s) {
if (lk_unlikely(!s))
if (!s)
return nullptr;
const auto& str = s->solver.dumps();
const auto str = s->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

@@ -1,35 +1,13 @@
#ifndef LJKIWI_CKIWI_H_
#define LJKIWI_CKIWI_H_
#if !defined(_MSC_VER) || _MSC_VER >= 1900
#undef LJKIWI_USE_FAM_1
#else
#define LJKIWI_USE_FAM_1
#endif
#ifndef KIWI_CKIWI_H_
#define KIWI_CKIWI_H_
#ifdef __cplusplus
namespace kiwi {
class VariableData;
class ConstraintData;
} // namespace kiwi
typedef kiwi::VariableData KiwiVar;
typedef kiwi::ConstraintData KiwiConstraint;
extern "C" {
#else
typedef struct KiwiVar KiwiVar;
typedef struct KiwiConstraint KiwiConstraint;
#include <stdbool.h>
#endif
#if defined __GNUC__ && (!defined _WIN32 || defined __CYGWIN__)
#define LJKIWI_EXP __attribute__((visibility("default")))
#elif defined _WIN32
#define LJKIWI_EXP __declspec(dllexport)
#endif
#define KIWI_REF_ISNULL(ref) ((ref).impl_ == NULL)
// LuaJIT start
enum KiwiErrKind {
@@ -48,24 +26,18 @@ enum KiwiErrKind {
enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ };
typedef struct KiwiVarRefType* KiwiVarRef;
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
typedef struct KiwiTerm {
KiwiVar* var;
KiwiVarRef var;
double coefficient;
} KiwiTerm;
typedef struct KiwiExpression {
double constant;
int term_count;
void* owner;
#if defined(LJKIWI_LUAJIT_DEF)
KiwiTerm terms_[?];
#elif defined(LJKIWI_USE_FAM_1)
KiwiTerm terms_[1]; // LuaJIT: struct KiwiTerm terms_[?];
#else
KiwiTerm terms_[];
#endif
KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?];
} KiwiExpression;
typedef struct KiwiErr {
@@ -74,58 +46,56 @@ typedef struct KiwiErr {
bool must_free;
} KiwiErr;
struct KiwiSolver;
typedef struct KiwiSolver
KiwiSolver; // LuaJIT: typedef struct { unsigned error_mask; } KiwiSolver;
KiwiVarRef kiwi_var_new(const char* name);
void kiwi_var_del(KiwiVarRef var);
KiwiVarRef kiwi_var_clone(KiwiVarRef var);
LJKIWI_EXP KiwiVar* kiwi_var_construct(const char* name);
LJKIWI_EXP void kiwi_var_release(KiwiVar* var);
LJKIWI_EXP void kiwi_var_retain(KiwiVar* var);
const char* kiwi_var_name(KiwiVarRef var);
void kiwi_var_set_name(KiwiVarRef var, const char* name);
double kiwi_var_value(KiwiVarRef var);
void kiwi_var_set_value(KiwiVarRef var, double value);
bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
LJKIWI_EXP const char* kiwi_var_name(const KiwiVar* var);
LJKIWI_EXP void kiwi_var_set_name(KiwiVar* var, const char* name);
LJKIWI_EXP double kiwi_var_value(const KiwiVar* var);
LJKIWI_EXP void kiwi_var_set_value(KiwiVar* var, double value);
void kiwi_expression_del_vars(KiwiExpression* expr);
LJKIWI_EXP void kiwi_expression_retain(KiwiExpression* expr);
LJKIWI_EXP void kiwi_expression_destroy(KiwiExpression* expr);
LJKIWI_EXP KiwiConstraint* kiwi_constraint_construct(
KiwiConstraintRef kiwi_constraint_new(
const KiwiExpression* lhs,
const KiwiExpression* rhs,
enum KiwiRelOp op,
double strength
);
LJKIWI_EXP void kiwi_constraint_release(KiwiConstraint* c);
LJKIWI_EXP void kiwi_constraint_retain(KiwiConstraint* c);
void kiwi_constraint_del(KiwiConstraintRef constraint);
KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint);
LJKIWI_EXP double kiwi_constraint_strength(const KiwiConstraint* c);
LJKIWI_EXP enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
LJKIWI_EXP bool kiwi_constraint_violated(const KiwiConstraint* c);
LJKIWI_EXP int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
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, KiwiExpression* out, int out_size);
LJKIWI_EXP KiwiSolver* kiwi_solver_construct(unsigned error_mask);
LJKIWI_EXP void kiwi_solver_destroy(KiwiSolver* s);
LJKIWI_EXP unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
LJKIWI_EXP void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
KiwiSolver* kiwi_solver_new(unsigned error_mask);
void kiwi_solver_del(KiwiSolver* s);
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);
LJKIWI_EXP const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint);
LJKIWI_EXP const KiwiErr*
kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint);
LJKIWI_EXP bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint);
LJKIWI_EXP const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength);
LJKIWI_EXP const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var);
LJKIWI_EXP bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var);
LJKIWI_EXP const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value);
LJKIWI_EXP void kiwi_solver_update_vars(KiwiSolver* sp);
LJKIWI_EXP void kiwi_solver_reset(KiwiSolver* sp);
LJKIWI_EXP void kiwi_solver_dump(const KiwiSolver* sp);
LJKIWI_EXP char* kiwi_solver_dumps(const KiwiSolver* sp);
// LuaJIT end
#ifdef __cplusplus
} // extern "C"
}
#endif
// Local Variables:
// mode: c++
// End:
#endif // LJKIWI_CKIWI_H_
#endif // KIWI_CKIWI_H_

View File

@@ -21,12 +21,8 @@ dependencies = {
build = {
type = "make",
build_variables = {
LUAROCKS = "1",
LUA = "$(LUA)",
CFLAGS = "$(CFLAGS)",
LUA_INCDIR = "$(LUA_INCDIR)",
LUA_LIBDIR = "$(LUA_LIBDIR)",
LUALIB = "$(LUALIB)",
LIBFLAG = "$(LIBFLAG)",
LIB_EXT = "$(LIB_EXTENSION)",
OBJ_EXT = "$(OBJ_EXTENSION)",

223
kiwi.lua
View File

@@ -4,29 +4,21 @@ local ffi
do
local ffi_loader = package.preload["ffi"]
if ffi_loader == nil then
return require("ljkiwi")
return require("ckiwi")
end
ffi = ffi_loader() --[[@as ffilib]]
end
local kiwi = {}
local ljkiwi
local ckiwi
do
local cpath, err = package.searchpath("ljkiwi", package.cpath)
local cpath, err = package.searchpath("ckiwi", package.cpath)
if cpath == nil then
error("kiwi dynamic library 'ljkiwi' not found\n" .. err)
error("kiwi dynamic library 'ckiwi' not found\n" .. err)
end
ljkiwi = ffi.load(cpath)
ckiwi = ffi.load(cpath)
end
kiwi.ljkiwi = ljkiwi
ffi.cdef([[
void free(void *);
typedef struct KiwiVar KiwiVar;
typedef struct KiwiConstraint KiwiConstraint;
typedef struct KiwiSolver KiwiSolver;]])
ffi.cdef([[
enum KiwiErrKind {
@@ -45,16 +37,17 @@ enum KiwiErrKind {
enum KiwiRelOp { LE, GE, EQ };
typedef struct KiwiVarRefType* KiwiVarRef;
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
typedef struct KiwiTerm {
KiwiVar* var;
KiwiVarRef var;
double coefficient;
} KiwiTerm;
typedef struct KiwiExpression {
double constant;
int term_count;
void* owner;
KiwiTerm terms_[?];
} KiwiExpression;
@@ -64,50 +57,49 @@ typedef struct KiwiErr {
bool must_free;
} KiwiErr;
struct KiwiSolver;
typedef struct KiwiSolver { unsigned error_mask_; } KiwiSolver;
KiwiVar* kiwi_var_construct(const char* name);
void kiwi_var_release(KiwiVar* var);
void kiwi_var_retain(KiwiVar* var);
KiwiVarRef kiwi_var_new(const char* name);
void kiwi_var_del(KiwiVarRef var);
KiwiVarRef kiwi_var_clone(KiwiVarRef var);
const char* kiwi_var_name(const KiwiVar* var);
void kiwi_var_set_name(KiwiVar* var, const char* name);
double kiwi_var_value(const KiwiVar* var);
void kiwi_var_set_value(KiwiVar* var, double value);
const char* kiwi_var_name(KiwiVarRef var);
void kiwi_var_set_name(KiwiVarRef var, const char* name);
double kiwi_var_value(KiwiVarRef var);
void kiwi_var_set_value(KiwiVarRef var, double value);
bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
void kiwi_expression_retain(KiwiExpression* expr);
void kiwi_expression_destroy(KiwiExpression* expr);
void kiwi_expression_del_vars(KiwiExpression* expr);
KiwiConstraint* kiwi_constraint_construct(
KiwiConstraintRef kiwi_constraint_new(
const KiwiExpression* lhs,
const KiwiExpression* rhs,
enum KiwiRelOp op,
double strength
);
void kiwi_constraint_release(KiwiConstraint* c);
void kiwi_constraint_retain(KiwiConstraint* c);
void kiwi_constraint_del(KiwiConstraintRef constraint);
double kiwi_constraint_strength(const KiwiConstraint* c);
enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
bool kiwi_constraint_violated(const KiwiConstraint* c);
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
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, KiwiExpression* out, int out_size);
KiwiSolver* kiwi_solver_construct(unsigned error_mask);
void kiwi_solver_destroy(KiwiSolver* s);
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
KiwiSolver* kiwi_solver_new(unsigned error_mask);
void kiwi_solver_del(KiwiSolver* s);
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint);
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint);
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint);
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength);
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var);
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var);
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value);
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
@@ -167,7 +159,7 @@ do
end
end
local Var = ffi.typeof("struct KiwiVar") --[[@as kiwi.Var]]
local Var = ffi.typeof("struct KiwiVarRefType") --[[@as kiwi.Var]]
kiwi.Var = Var
function kiwi.is_var(o)
@@ -188,7 +180,7 @@ function kiwi.is_expression(o)
return ffi_istype(Expression, o)
end
local Constraint = ffi.typeof("struct KiwiConstraint") --[[@as kiwi.Constraint]]
local Constraint = ffi.typeof("struct KiwiConstraintRefType") --[[@as kiwi.Constraint]]
kiwi.Constraint = Constraint
function kiwi.is_constraint(o)
@@ -200,19 +192,18 @@ end
---@param coeff number?
---@nodiscard
local function add_expr_term(expr, var, coeff)
local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), ljkiwi.kiwi_expression_destroy) --[[@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]]
dt.var = st.var
dt.var = ckiwi.kiwi_var_clone(st.var)
dt.coefficient = st.coefficient
end
local dt = ret.terms_[expr.term_count]
dt.var = var
dt.var = ckiwi.kiwi_var_clone(var)
dt.coefficient = coeff or 1.0
ret.constant = expr.constant
ret.term_count = expr.term_count + 1
ljkiwi.kiwi_expression_retain(ret)
return ret
end
@@ -221,13 +212,12 @@ end
---@param coeff number?
---@nodiscard
local function new_expr_one(constant, var, coeff)
local ret = ffi_gc(ffi_new(Expression, 1), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
local ret = ffi_gc(ffi_new(Expression, 1), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]]
local dt = ret.terms_[0]
dt.var = var
dt.var = ckiwi.kiwi_var_clone(var)
dt.coefficient = coeff or 1.0
ret.constant = constant
ret.term_count = 1
ljkiwi.kiwi_expression_retain(ret)
return ret
end
@@ -238,16 +228,15 @@ end
---@param coeff2 number?
---@nodiscard
local function new_expr_pair(constant, var1, var2, coeff1, coeff2)
local ret = ffi_gc(ffi_new(Expression, 2), ljkiwi.kiwi_expression_destroy) --[[@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 = var1
dt.var = ckiwi.kiwi_var_clone(var1)
dt.coefficient = coeff1 or 1.0
dt = ret.terms_[1]
dt.var = var2
dt.var = ckiwi.kiwi_var_clone(var2)
dt.coefficient = coeff2 or 1.0
ret.constant = constant
ret.term_count = 2
ljkiwi.kiwi_expression_retain(ret)
return ret
end
@@ -322,10 +311,7 @@ local function rel(lhs, rhs, op, strength)
op_error(lhs, rhs, OP_NAMES[op])
end
return ffi_gc(
ljkiwi.kiwi_constraint_construct(el, er, op, strength or REQUIRED),
ljkiwi.kiwi_constraint_release
) --[[@as kiwi.Constraint]]
return ffi_gc(ckiwi.kiwi_constraint_new(el, er, op, strength or REQUIRED), ckiwi.kiwi_constraint_del) --[[@as kiwi.Constraint]]
end
--- Define a constraint with expressions as `a <= b`.
@@ -371,22 +357,22 @@ do
--- Change the name of the variable.
---@type fun(self: kiwi.Var, name: string)
set_name = ljkiwi.kiwi_var_set_name,
set_name = ckiwi.kiwi_var_set_name,
--- Get the current value of the variable.
---@type fun(self: kiwi.Var): number
value = ljkiwi.kiwi_var_value,
value = ckiwi.kiwi_var_value,
--- Set the value of the variable.
---@type fun(self: kiwi.Var, value: number)
set = ljkiwi.kiwi_var_set_value,
set = ckiwi.kiwi_var_set_value,
}
--- Get the name of the variable.
---@return string
---@nodiscard
function Var_cls:name()
return ffi_string(ljkiwi.kiwi_var_name(self))
return ffi_string(ckiwi.kiwi_var_name(self))
end
--- Create a term from this variable.
@@ -411,7 +397,7 @@ do
}
function Var_mt:__new(name)
return ffi_gc(ljkiwi.kiwi_var_construct(name), ljkiwi.kiwi_var_release)
return ffi_gc(ckiwi.kiwi_var_new(name), ckiwi.kiwi_var_del)
end
function Var_mt.__mul(a, b)
@@ -496,15 +482,11 @@ do
local Term_mt = { __index = Term_cls }
local function term_gc(term)
ljkiwi.kiwi_var_release(term.var)
ckiwi.kiwi_var_del(term.var)
end
function Term_mt.__new(T, var, coefficient)
local t = ffi_gc(ffi_new(T), term_gc) --[[@as kiwi.Term]]
ljkiwi.kiwi_var_retain(var)
t.var = var
t.coefficient = coefficient or 1.0
return t
return ffi_gc(ffi_new(T, ckiwi.kiwi_var_clone(var), coefficient or 1.0), term_gc)
end
function Term_mt.__mul(a, b)
@@ -560,7 +542,6 @@ do
---@class kiwi.Expression: ffi.cdata*
---@overload fun(constant: number, ...: kiwi.Term): kiwi.Expression
---@field constant number
---@field package owner ffi.cdata*
---@field package term_count number
---@field package terms_ ffi.cdata*
---@operator mul(number): kiwi.Expression
@@ -578,16 +559,15 @@ do
---@param constant number
---@nodiscard
local function mul_expr_coeff(expr, constant)
local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@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]]
dt.var = st.var
dt.var = ckiwi.kiwi_var_clone(st.var)
dt.coefficient = st.coefficient * constant
end
ret.constant = expr.constant * constant
ret.term_count = expr.term_count
ljkiwi.kiwi_expression_retain(ret)
return ret
end
@@ -597,23 +577,22 @@ 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), ljkiwi.kiwi_expression_destroy) --[[@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]]
local st = a.terms_[i] --[[@as kiwi.Term]]
dt.var = st.var
dt.var = ckiwi.kiwi_var_clone(st.var)
dt.coefficient = st.coefficient
end
for i = 0, b_count - 1 do
local dt = ret.terms_[a_count + i] --[[@as kiwi.Term]]
local st = b.terms_[i] --[[@as kiwi.Term]]
dt.var = st.var
dt.var = ckiwi.kiwi_var_clone(st.var)
dt.coefficient = st.coefficient
end
ret.constant = a.constant + b.constant
ret.term_count = a_count + b_count
ljkiwi.kiwi_expression_retain(ret)
return ret
end
@@ -621,17 +600,16 @@ do
---@param constant number
---@nodiscard
local function new_expr_constant(expr, constant)
local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@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]]
local st = expr.terms_[i] --[[@as kiwi.Term]]
dt.var = st.var
dt.var = ckiwi.kiwi_var_clone(st.var)
dt.coefficient = st.coefficient
end
ret.constant = constant
ret.term_count = expr.term_count
ljkiwi.kiwi_expression_retain(ret)
return ret
end
@@ -667,18 +645,17 @@ do
__index = Expression_cls,
}
function Expression_mt:__new(constant, ...)
function Expression_mt.__new(T, constant, ...)
local term_count = select("#", ...)
local e = ffi_gc(ffi_new(self, term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
local e = ffi_gc(ffi_new(T, term_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]]
e.term_count = term_count
e.constant = constant
for i = 1, term_count do
local t = select(i, ...)
local dt = e.terms_[i - 1] --[[@as kiwi.Term]]
dt.var = t.var
dt.var = ckiwi.kiwi_var_clone(t.var)
dt.coefficient = t.coefficient
end
ljkiwi.kiwi_expression_retain(e)
return e
end
@@ -745,29 +722,29 @@ do
local Constraint_cls = {
--- The strength of the constraint.
---@type fun(self: kiwi.Constraint): number
strength = ljkiwi.kiwi_constraint_strength,
strength = ckiwi.kiwi_constraint_strength,
--- The relational operator of the constraint.
---@type fun(self: kiwi.Constraint): kiwi.RelOp
op = ljkiwi.kiwi_constraint_op,
op = ckiwi.kiwi_constraint_op,
--- Whether the constraint is violated in the current solution.
---@type fun(self: kiwi.Constraint): boolean
violated = ljkiwi.kiwi_constraint_violated,
violated = ckiwi.kiwi_constraint_violated,
}
--- The reduced expression defining the constraint.
---@return kiwi.Expression
---@nodiscard
function Constraint_cls:expression()
local SZ = 7
local SZ = 7 -- 2**7 bytes on x64
local expr = ffi_new(Expression, SZ) --[[@as kiwi.Expression]]
local n = ljkiwi.kiwi_constraint_expression(self, expr, SZ)
local n = ckiwi.kiwi_constraint_expression(self, expr, SZ)
if n > SZ then
expr = ffi_new(Expression, n) --[[@as kiwi.Expression]]
n = ljkiwi.kiwi_constraint_expression(self, expr, n)
n = ckiwi.kiwi_constraint_expression(self, expr, n)
end
return ffi_gc(expr, ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
return ffi_gc(expr, ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]]
end
--- Add the constraint to the solver.
@@ -797,8 +774,8 @@ do
function Constraint_mt:__new(lhs, rhs, op, strength)
return ffi_gc(
ljkiwi.kiwi_constraint_construct(lhs, rhs, op or "EQ", strength or REQUIRED),
ljkiwi.kiwi_constraint_release
ckiwi.kiwi_constraint_new(lhs, rhs, op or "EQ", strength or REQUIRED),
ckiwi.kiwi_constraint_del
)
end
@@ -846,8 +823,8 @@ do
tmpexpr.term_count = 2
return ffi_gc(
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED),
ljkiwi.kiwi_constraint_release
ckiwi.kiwi_constraint_new(tmpexpr, nil, op or "EQ", strength or REQUIRED),
ckiwi.kiwi_constraint_del
) --[[@as kiwi.Constraint]]
end
@@ -881,10 +858,9 @@ do
local t = tmpexpr.terms_[0]
t.var = var
t.coefficient = 1.0
return ffi_gc(
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED),
ljkiwi.kiwi_constraint_release
ckiwi.kiwi_constraint_new(tmpexpr, nil, op or "EQ", strength or REQUIRED),
ckiwi.kiwi_constraint_del
) --[[@as kiwi.Constraint]]
end
end
@@ -971,7 +947,7 @@ do
C.free(err)
end
local errdata = new_error(kind, message, solver, item)
local error_mask = ljkiwi.kiwi_solver_get_error_mask(solver)
local error_mask = solver and solver.error_mask_ or 0
return item,
band(error_mask, lshift(1, kind --[[@as integer]])) == 0 and error(errdata)
or errdata
@@ -979,19 +955,20 @@ do
return item
end
---@class kiwi.Solver: ffi.cdata*
---@field package error_mask_ integer
---@overload fun(error_mask: (integer|(kiwi.ErrKind|integer)[] )?): kiwi.Solver
local Solver_cls = {
--- Test whether a constraint is in the solver.
---@type fun(self: kiwi.Solver, constraint: kiwi.Constraint): boolean
has_constraint = ljkiwi.kiwi_solver_has_constraint,
has_constraint = ckiwi.kiwi_solver_has_constraint,
--- Test whether an edit variable has been added to the solver.
---@type fun(self: kiwi.Solver, var: kiwi.Var): boolean
has_edit_var = ljkiwi.kiwi_solver_has_edit_var,
has_edit_var = ckiwi.kiwi_solver_has_edit_var,
--- Update the values of the external solver variables.
---@type fun(self: kiwi.Solver)
update_vars = ljkiwi.kiwi_solver_update_vars,
update_vars = ckiwi.kiwi_solver_update_vars,
--- Reset the solver to the empty starting conditions.
---
@@ -1001,11 +978,11 @@ do
--- when the entire system must change, since it can avoid unecessary
--- heap (de)allocations.
---@type fun(self: kiwi.Solver)
reset = ljkiwi.kiwi_solver_reset,
reset = ckiwi.kiwi_solver_reset,
--- Dump a representation of the solver to stdout.
---@type fun(self: kiwi.Solver)
dump = ljkiwi.kiwi_solver_dump,
dump = ckiwi.kiwi_solver_dump,
}
--- Sets the error mask for the solver.
@@ -1015,7 +992,7 @@ do
if type(mask) == "table" then
mask = kiwi.error_mask(mask, invert)
end
ljkiwi.kiwi_solver_set_error_mask(self, mask)
self.error_mask_ = mask
end
---@generic T
@@ -1040,7 +1017,7 @@ do
---@param constraint kiwi.Constraint
---@return kiwi.Constraint constraint, kiwi.Error?
function Solver_cls:add_constraint(constraint)
return try_solver(ljkiwi.kiwi_solver_add_constraint, self, constraint)
return try_solver(ckiwi.kiwi_solver_add_constraint, self, constraint)
end
--- Add constraints to the solver.
@@ -1050,7 +1027,7 @@ do
---@param constraints kiwi.Constraint[]
---@return kiwi.Constraint[] constraints, kiwi.Error?
function Solver_cls:add_constraints(constraints)
return add_remove_items(self, constraints, ljkiwi.kiwi_solver_add_constraint)
return add_remove_items(self, constraints, ckiwi.kiwi_solver_add_constraint)
end
--- Remove a constraint from the solver.
@@ -1059,7 +1036,7 @@ do
---@param constraint kiwi.Constraint
---@return kiwi.Constraint constraint, kiwi.Error?
function Solver_cls:remove_constraint(constraint)
return try_solver(ljkiwi.kiwi_solver_remove_constraint, self, constraint)
return try_solver(ckiwi.kiwi_solver_remove_constraint, self, constraint)
end
--- Remove constraints from the solver.
@@ -1068,7 +1045,7 @@ do
---@param constraints kiwi.Constraint[]
---@return kiwi.Constraint[] constraints, kiwi.Error?
function Solver_cls:remove_constraints(constraints)
return add_remove_items(self, constraints, ljkiwi.kiwi_solver_remove_constraint)
return add_remove_items(self, constraints, ckiwi.kiwi_solver_remove_constraint)
end
--- Add an edit variables to the solver.
@@ -1082,7 +1059,7 @@ do
---@param strength number the strength of the edit variable (must be less than `Strength.REQUIRED`)
---@return kiwi.Var var, kiwi.Error?
function Solver_cls:add_edit_var(var, strength)
return try_solver(ljkiwi.kiwi_solver_add_edit_var, self, var, strength)
return try_solver(ckiwi.kiwi_solver_add_edit_var, self, var, strength)
end
--- Add edit variables to the solver.
@@ -1096,7 +1073,7 @@ do
---@param strength number the strength of the edit variables (must be less than `Strength.REQUIRED`)
---@return kiwi.Var[] vars, kiwi.Error?
function Solver_cls:add_edit_vars(vars, strength)
return add_remove_items(self, vars, ljkiwi.kiwi_solver_add_edit_var, strength)
return add_remove_items(self, vars, ckiwi.kiwi_solver_add_edit_var, strength)
end
--- Remove an edit variable from the solver.
@@ -1105,7 +1082,7 @@ do
---@param var kiwi.Var the edit variable to remove
---@return kiwi.Var var, kiwi.Error?
function Solver_cls:remove_edit_var(var)
return try_solver(ljkiwi.kiwi_solver_remove_edit_var, self, var)
return try_solver(ckiwi.kiwi_solver_remove_edit_var, self, var)
end
--- Removes edit variables from the solver.
@@ -1114,7 +1091,7 @@ do
---@param vars kiwi.Var[] the edit variables to remove
---@return kiwi.Var[] vars, kiwi.Error?
function Solver_cls:remove_edit_vars(vars)
return add_remove_items(self, vars, ljkiwi.kiwi_solver_remove_edit_var)
return add_remove_items(self, vars, ckiwi.kiwi_solver_remove_edit_var)
end
--- Suggest a value for the given edit variable.
@@ -1127,7 +1104,7 @@ do
---@param value number the suggested value
---@return kiwi.Var var, kiwi.Error?
function Solver_cls:suggest_value(var, value)
return try_solver(ljkiwi.kiwi_solver_suggest_value, self, var, value)
return try_solver(ckiwi.kiwi_solver_suggest_value, self, var, value)
end
--- Suggest values for the given edit variables.
@@ -1139,7 +1116,7 @@ do
---@return kiwi.Var[] vars, number[] values, kiwi.Error?
function Solver_cls:suggest_values(vars, values)
for i, var in ipairs(vars) do
local _, err = try_solver(ljkiwi.kiwi_solver_suggest_value, self, var, values[i])
local _, err = try_solver(ckiwi.kiwi_solver_suggest_value, self, var, values[i])
if err ~= nil then
return vars, values, err
end
@@ -1151,7 +1128,7 @@ do
---@return string
---@nodiscard
function Solver_cls:dumps()
local cs = ljkiwi.kiwi_solver_dumps(self)
local cs = ckiwi.kiwi_solver_dumps(self)
local s = ffi_string(cs)
C.free(cs)
return s
@@ -1165,15 +1142,13 @@ do
if type(error_mask) == "table" then
error_mask = kiwi.error_mask(error_mask)
end
return ffi_gc(ljkiwi.kiwi_solver_construct(error_mask or 0), ljkiwi.kiwi_solver_destroy) --[[@as kiwi.Constraint]]
return ffi_gc(ckiwi.kiwi_solver_new(error_mask or 0), ckiwi.kiwi_solver_del)
end
local Solver = ffi.metatype(ffi.typeof("struct KiwiSolver"), Solver_mt) --[[@as kiwi.Solver]]
kiwi.Solver = Solver
kiwi.Solver = ffi.metatype("struct KiwiSolver", Solver_mt) --[[@as kiwi.Solver]]
function kiwi.is_solver(s)
return ffi_istype(Solver, s)
return ffi_istype(kiwi.Solver, s)
end
end

View File

@@ -26,70 +26,17 @@ enum RelationalOperator
OP_EQ
};
class Constraint;
class ConstraintData : public SharedData
{
static Expression reduce(const Expression &expr)
{
std::map<Variable, double> vars;
for (const auto & term : expr.terms())
vars[term.variable()] += term.coefficient();
std::vector<Term> terms(vars.begin(), vars.end());
return Expression(std::move(terms), expr.constant());
}
public:
ConstraintData(const Expression &expr,
RelationalOperator op,
double strength) : SharedData(),
m_expression(reduce(expr)),
m_strength(strength::clip(strength)),
m_op(op) {}
ConstraintData(const ConstraintData &other, double strength) : SharedData(),
m_expression(other.m_expression),
m_strength(strength::clip(strength)),
m_op(other.m_op) {}
~ConstraintData() = default;
const Expression &expression() const { return m_expression; }
RelationalOperator op() const { return m_op; }
double strength() const { return m_strength; }
bool violated() const
{
switch (m_op)
{
case OP_EQ: return !impl::nearZero(m_expression.value());
case OP_GE: return m_expression.value() < 0.0;
case OP_LE: return m_expression.value() > 0.0;
}
std::abort();
}
private:
Expression m_expression;
double m_strength;
RelationalOperator m_op;
ConstraintData(const ConstraintData &other) = delete;
ConstraintData &operator=(const ConstraintData &other) = delete;
};
class Constraint
{
public:
explicit Constraint(ConstraintData *p) : m_data(p) {}
public:
Constraint() = default;
Constraint(const Expression &expr,
RelationalOperator op,
double strength = strength::required) : m_data(new ConstraintData(expr, op, strength)) {}
Constraint(const Constraint &other, double strength) : m_data(new ConstraintData(*other.m_data, strength)) {}
Constraint(const Constraint &other, double strength) : m_data(new ConstraintData(other, strength)) {}
Constraint(const Constraint &) = default;
@@ -97,10 +44,32 @@ public:
~Constraint() = default;
const Expression &expression() const { return m_data->expression(); }
RelationalOperator op() const { return m_data->op(); }
double strength() const { return m_data->strength(); }
bool violated() const { return m_data->violated(); }
const Expression &expression() const
{
return m_data->m_expression;
}
RelationalOperator op() const
{
return m_data->m_op;
}
double strength() const
{
return m_data->m_strength;
}
bool violated() const
{
switch (m_data->m_op)
{
case OP_EQ: return !impl::nearZero(m_data->m_expression.value());
case OP_GE: return m_data->m_expression.value() < 0.0;
case OP_LE: return m_data->m_expression.value() > 0.0;
}
std::abort();
}
bool operator!() const
{
@@ -112,9 +81,45 @@ public:
Constraint& operator=(Constraint &&) noexcept = default;
private:
SharedDataPtr<ConstraintData> m_data;
static Expression reduce(const Expression &expr)
{
std::map<Variable, double> vars;
for (const auto & term : expr.terms())
vars[term.variable()] += term.coefficient();
public:
std::vector<Term> terms(vars.begin(), vars.end());
return Expression(std::move(terms), expr.constant());
}
class ConstraintData : public SharedData
{
public:
ConstraintData(const Expression &expr,
RelationalOperator op,
double strength) : SharedData(),
m_expression(reduce(expr)),
m_strength(strength::clip(strength)),
m_op(op) {}
ConstraintData(const Constraint &other, double strength) : SharedData(),
m_expression(other.expression()),
m_strength(strength::clip(strength)),
m_op(other.op()) {}
~ConstraintData() = default;
Expression m_expression;
double m_strength;
RelationalOperator m_op;
private:
ConstraintData(const ConstraintData &other);
ConstraintData &operator=(const ConstraintData &other);
};
SharedDataPtr<ConstraintData> m_data;
friend bool operator<(const Constraint &lhs, const Constraint &rhs)
{

View File

@@ -13,55 +13,63 @@
namespace kiwi
{
class VariableData : public SharedData
{
public:
VariableData(std::string name) : SharedData(),
m_name(std::move(name)),
m_value(0.0) {}
VariableData(const char *name) : SharedData(),
m_name(name),
m_value(0.0) {}
~VariableData() = default;
const std::string &name() const { return m_name; }
void setName(const char *name) { m_name = name; }
void setName(const std::string &name) { m_name = name; }
double value() const { return m_value; }
void setValue(double value) { m_value = value; }
private:
std::string m_name;
double m_value;
VariableData(const VariableData &other) = delete;
VariableData &operator=(const VariableData &other) = delete;
};
class Variable
{
public:
explicit Variable(VariableData *p) : m_data(p) {}
VariableData *ptr() { return m_data; }
class Context
{
public:
Context() = default;
virtual ~Context() {} // LCOV_EXCL_LINE
};
Variable() : m_data(new VariableData("")) {}
Variable(Context *context = 0) : m_data(new VariableData("", context)) {}
Variable(std::string name, Context *context = 0) : m_data(new VariableData(std::move(name), context)) {}
Variable(const char *name, Context *context = 0) : m_data(new VariableData(name, context)) {}
Variable(std::string name) : m_data(new VariableData(std::move(name))) {}
Variable(const char *name) : m_data(new VariableData(name)) {}
Variable(const Variable&) = default;
Variable(Variable&&) noexcept = default;
~Variable() = default;
const std::string &name() const { return m_data->name(); }
void setName(const char *name) { m_data->setName(name); }
void setName(const std::string &name) { m_data->setName(name); }
const std::string &name() const
{
return m_data->m_name;
}
double value() const { return m_data->value(); }
void setValue(double value) { m_data->setValue(value); }
void setName(const char *name)
{
m_data->m_name = name;
}
void setName(const std::string &name)
{
m_data->m_name = name;
}
Context *context() const
{
return m_data->m_context.get();
}
void setContext(Context *context)
{
m_data->m_context.reset(context);
}
double value() const
{
return m_data->m_value;
}
void setValue(double value)
{
m_data->m_value = value;
}
// operator== is used for symbolics
bool equals(const Variable &other) const
@@ -74,6 +82,32 @@ public:
Variable& operator=(Variable&&) noexcept = default;
private:
class VariableData : public SharedData
{
public:
VariableData(std::string name, Context *context) : SharedData(),
m_name(std::move(name)),
m_context(context),
m_value(0.0) {}
VariableData(const char *name, Context *context) : SharedData(),
m_name(name),
m_context(context),
m_value(0.0) {}
~VariableData() = default;
std::string m_name;
std::unique_ptr<Context> m_context;
double m_value;
private:
VariableData(const VariableData &other);
VariableData &operator=(const VariableData &other);
};
SharedDataPtr<VariableData> m_data;
friend bool operator<(const Variable &lhs, const Variable &rhs)

View File

@@ -1 +0,0 @@
#include <kiwi/kiwi.h>

2
luakiwi.def Normal file
View File

@@ -0,0 +1,2 @@
EXPORTS
luaopen_ckiwi

88
luakiwi/.clang-format Normal file
View File

@@ -0,0 +1,88 @@
AccessModifierOffset: -2
AlignAfterOpenBracket: BlockIndent # New in v14. For earlier clang-format versions, use AlwaysBreak instead.
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: false
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakAfterAttributes: Always
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakStringLiterals: false
ColumnLimit: 98
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: BeforeHash
IndentWidth: 3
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PointerAlignment: Left
ReferenceAlignment: Left # New in v13. int &name ==> int& name
ReflowComments: false
SeparateDefinitionBlocks: Always # New in v14.
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never

View File

@@ -1,29 +1,33 @@
#ifndef LJKIWI_LUACOMPAT_H_
#define LJKIWI_LUACOMPAT_H_
#ifndef LKIWI_LUACOMPAT_H_
#define LKIWI_LUACOMPAT_H_
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 && defined(__GNUC__)
#define LJKIWI_LJ_COMPAT_ATTR __attribute__((weak, visibility("default")))
#else
#define LJKIWI_LJ_COMPAT_ATTR static
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
#define LUA_OPADD 0
#define LUA_OPSUB 1
#define LUA_OPMUL 2
#define LUA_OPDIV 3
#define LUA_OPMOD 4
#define LUA_OPPOW 5
#define LUA_OPUNM 6
static int lua_absindex(lua_State* L, int i) {
if (i < 0 && i > LUA_REGISTRYINDEX)
i += lua_gettop(L) + 1;
return i;
}
LJKIWI_LJ_COMPAT_ATTR lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
static lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
lua_Number n = lua_tonumber(L, i);
if (isnum != NULL) {
*isnum = (n != 0 || lua_isnumber(L, i));
@@ -31,7 +35,7 @@ LJKIWI_LJ_COMPAT_ATTR lua_Number lua_tonumberx(lua_State* L, int i, int* isnum)
return n;
}
LJKIWI_LJ_COMPAT_ATTR lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
static lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
int ok = 0;
lua_Number n = lua_tonumberx(L, i, &ok);
if (ok) {
@@ -140,8 +144,4 @@ static int luaL_typeerror(lua_State* L, int arg, const char* tname) {
#define luaL_checkversion(L) ((void)0)
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // LJKIWI_LUACOMPAT_H_
#endif // LKIWI_LUACOMPAT_H_

View File

@@ -8,7 +8,7 @@
#include "luacompat.h"
#if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
#if defined(__GNUC__) && !defined(LKIWI_NO_BUILTIN)
#define lk_likely(x) (__builtin_expect(((x) != 0), 1))
#define lk_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
@@ -23,9 +23,8 @@ using namespace kiwi;
// Lua 5.1 compatibility for missing lua_arith.
inline void compat_arith_unm(lua_State* L) {
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
int isnum;
lua_Number n = lua_tonumberx(L, -1, &isnum);
if (isnum) {
lua_Number n = lua_tonumber(L, -1);
if (n != 0 || lua_isnumber(L, -1)) {
lua_pop(L, 1);
lua_pushnumber(L, -n);
} else {
@@ -59,7 +58,7 @@ constexpr int array_count(T (&)[N]) {
return static_cast<int>(N);
}
inline void newlib(lua_State* L, const luaL_Reg* l) {
void newlib(lua_State* L, const luaL_Reg* l) {
lua_newtable(L);
setfuncs(L, l, 0);
}
@@ -79,28 +78,26 @@ enum KiwiErrKind {
};
struct KiwiTerm {
VariableData* var;
Variable* var;
double coefficient;
};
struct KiwiExpression {
double constant;
int term_count;
ConstraintData* owner;
Constraint* owner;
#if !defined(_MSC_VER) || _MSC_VER >= 1900
KiwiTerm terms[];
static constexpr std::size_t sz(int count) {
return sizeof(KiwiExpression)
+ sizeof(KiwiTerm) * static_cast<std::size_t>(count > 0 ? count : 0);
return sizeof(KiwiExpression) + sizeof(KiwiTerm) * (count > 0 ? count : 0);
}
#else
KiwiTerm terms[1];
static constexpr std::size_t sz(int count) {
return sizeof(KiwiExpression)
+ sizeof(KiwiTerm) * static_cast<std::size_t>(count > 0 ? count : 0);
return sizeof(KiwiExpression) + sizeof(KiwiTerm) * (count > 1 ? count - 1 : 0);
}
#endif
@@ -141,57 +138,49 @@ template<typename F>
inline const KiwiErr* wrap_err(F&& f) {
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
KiwiErrUnknown,
"An unhandled C++ exception occurred."
};
"An unhandled C++ exception occurred."};
try {
f();
} catch (const UnsatisfiableConstraint&) {
static const constexpr KiwiErr err {
KiwiErrUnsatisfiableConstraint,
"The constraint cannot be satisfied."
};
"The constraint cannot be satisfied."};
return &err;
} catch (const UnknownConstraint&) {
static const constexpr KiwiErr err {
KiwiErrUnknownConstraint,
"The constraint has not been added to the solver."
};
"The constraint has not been added to the solver."};
return &err;
} catch (const DuplicateConstraint&) {
static const constexpr KiwiErr err {
KiwiErrDuplicateConstraint,
"The constraint has already been added to the solver."
};
"The constraint has already been added to the solver."};
return &err;
} catch (const UnknownEditVariable&) {
static const constexpr KiwiErr err {
KiwiErrUnknownEditVariable,
"The edit variable has not been added to the solver."
};
"The edit variable has not been added to the solver."};
return &err;
} catch (const DuplicateEditVariable&) {
static const constexpr KiwiErr err {
KiwiErrDuplicateEditVariable,
"The edit variable has already been added to the solver."
};
"The edit variable has already been added to the solver."};
return &err;
} catch (const BadRequiredStrength&) {
static const constexpr KiwiErr err {
KiwiErrBadRequiredStrength,
"A required strength cannot be used in this context."
};
"A required strength cannot be used in this context."};
return &err;
} catch (const InternalSolverError& ex) {
static const constexpr KiwiErr base {
KiwiErrInternalSolverError,
"An internal solver error occurred."
};
"An internal solver error occurred."};
return new_error(&base, ex);
} catch (std::bad_alloc&) {
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
@@ -205,108 +194,81 @@ inline const KiwiErr* wrap_err(F&& f) {
}
template<typename P, typename R, typename F>
inline const KiwiErr* wrap_err(P&& s, F&& f) {
inline const KiwiErr* wrap_err(P& s, F&& f) {
return wrap_err([&]() { f(s); });
}
template<typename P, typename R, typename F>
inline const KiwiErr* wrap_err(P&& s, R&& ref, F&& f) {
inline const KiwiErr* wrap_err(P& s, R& ref, F&& f) {
return wrap_err([&]() { f(s, ref); });
}
template<typename T, typename... Args>
inline T* make_unmanaged(Args... args) {
auto* p = new (std::nothrow) T(std::forward<Args>(args)...);
if (lk_unlikely(!p))
return nullptr;
p->m_refcount = 1;
return p;
inline Variable* kiwi_var_retain(Variable* var) {
alignas(Variable) unsigned char buf[sizeof(Variable)];
new (buf) Variable(*var);
return var;
}
template<typename T>
inline void release_unmanaged(T* p) {
if (lk_likely(p)) {
if (--p->m_refcount == 0)
delete p;
}
inline Constraint* kiwi_constraint_retain(Constraint* c) {
alignas(Constraint) unsigned char buf[sizeof(Constraint)];
new (buf) Constraint(*c);
return c;
}
template<typename T>
inline T* retain_unmanaged(T* p) {
if (lk_likely(p))
p->m_refcount++;
return p;
}
inline ConstraintData* kiwi_constraint_new(
inline Constraint* kiwi_constraint_new(
const KiwiExpression* lhs,
const KiwiExpression* rhs,
RelationalOperator op,
double strength
double strength,
void* mem
) {
if (strength < 0.0) {
strength = kiwi::strength::required;
}
try {
std::vector<Term> terms;
std::vector<Term> terms;
terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0));
terms.reserve(static_cast<decltype(terms)::size_type>(
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
));
if (lhs) {
for (int i = 0; i < lhs->term_count; ++i) {
const auto& t = lhs->terms[i];
terms.emplace_back(Variable(t.var), t.coefficient);
}
if (lhs) {
for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) {
terms.emplace_back(*t->var, t->coefficient);
}
if (rhs) {
for (int i = 0; i < rhs->term_count; ++i) {
const auto& t = rhs->terms[i];
terms.emplace_back(Variable(t.var), -t.coefficient);
}
}
return make_unmanaged<ConstraintData>(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op),
strength
);
} catch (...) {
return nullptr;
}
if (rhs) {
for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) {
terms.emplace_back(*t->var, -t->coefficient);
}
}
return new (mem) Constraint(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op),
strength
);
}
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, ConstraintData* constraint) {
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
solver.addConstraint(Constraint(c));
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, const Constraint& constraint) {
return wrap_err(s, constraint, [](auto& solver, const auto& c) { solver.addConstraint(c); });
}
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, const Constraint& constraint) {
return wrap_err(s, constraint, [](auto& solver, const auto& c) {
solver.removeConstraint(c);
});
}
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, ConstraintData* constraint) {
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
solver.removeConstraint(Constraint(c));
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, const Variable& var, double strength) {
return wrap_err(s, var, [strength](auto& solver, const auto& v) {
solver.addEditVariable(v, strength);
});
}
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, VariableData* var, double strength) {
return wrap_err(s, var, [strength](auto&& solver, auto&& v) {
solver.addEditVariable(Variable(v), strength);
});
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, const Variable& var) {
return wrap_err(s, var, [](auto& solver, const auto& v) { solver.removeEditVariable(v); });
}
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, VariableData* var) {
return wrap_err(s, var, [](auto&& solver, auto&& v) {
solver.removeEditVariable(Variable(v));
});
}
inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, VariableData* var, double value) {
return wrap_err(s, var, [value](auto&& solver, auto&& v) {
solver.suggestValue(Variable(v), value);
inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, const Variable& var, double value) {
return wrap_err(s, var, [value](auto& solver, const auto& v) {
solver.suggestValue(v, value);
});
}

View File

@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include "luacompat.h"
#include "luakiwi-int.h"
namespace {
@@ -11,7 +12,9 @@ namespace {
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
enum { ERR_KIND_TAB = NUMBER + 1, VAR_SUB_FN, MEM_ERR_MSG, CONTEXT_TAB_MAX };
const int ERR_KIND_TAB = NUMBER + 1;
const int VAR_SUB_FN = ERR_KIND_TAB + 1;
const int CONTEXT_TAB_MAX = VAR_SUB_FN + 1;
constexpr const char* const lkiwi_error_kinds[] = {
"KiwiErrNone",
@@ -142,8 +145,8 @@ inline void* try_type(lua_State* L, int idx, TypeId type_id) {
return lua_rawequal(L, -1, -2) ? p : 0;
}
inline VariableData* try_var(lua_State* L, int idx) {
return *static_cast<VariableData**>(try_type(L, idx, VAR));
inline Variable* try_var(lua_State* L, int idx) {
return static_cast<Variable*>(try_type(L, idx, VAR));
}
inline KiwiTerm* try_term(lua_State* L, int idx) {
@@ -187,8 +190,8 @@ inline void* try_arg(lua_State* L, int idx, TypeId* type_id, double* num) {
return 0;
}
inline VariableData* get_var(lua_State* L, int idx) {
return *static_cast<VariableData**>(check_arg(L, idx, VAR));
inline Variable* get_var(lua_State* L, int idx) {
return static_cast<Variable*>(check_arg(L, idx, VAR));
}
inline KiwiTerm* get_term(lua_State* L, int idx) {
@@ -206,30 +209,24 @@ inline KiwiExpression* get_expr_opt(lua_State* L, int idx) {
return static_cast<KiwiExpression*>(check_arg(L, idx, EXPR));
}
inline ConstraintData* get_constraint(lua_State* L, int idx) {
return *static_cast<ConstraintData**>(check_arg(L, idx, CONSTRAINT));
inline Constraint* get_constraint(lua_State* L, int idx) {
return static_cast<Constraint*>(check_arg(L, idx, CONSTRAINT));
}
inline KiwiSolver* get_solver(lua_State* L, int idx) {
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
}
VariableData** var_new(lua_State* L) {
auto** varp = static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*)));
// note this expects the 2nd upvalue to have the variable weak table
template<typename... Args>
inline Variable* var_new(lua_State* L, Args&&... args) {
auto* var = new (lua_newuserdata(L, sizeof(Variable))) Variable(std::forward<Args>(args)...);
push_type(L, VAR);
lua_setmetatable(L, -2);
return varp;
}
// note this expects the 2nd upvalue to have the variable weak table
VariableData* var_register(lua_State* L, VariableData* var) {
if (lk_unlikely(!var)) {
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
lua_error(L);
}
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
// a true compatibility shim has performance implications here
lua_pushlightuserdata(L, var);
lua_pushlightuserdata(L, p);
lua_pushvalue(L, -2);
lua_rawset(L, lua_upvalueindex(2));
#else
@@ -239,7 +236,7 @@ VariableData* var_register(lua_State* L, VariableData* var) {
return var;
}
KiwiTerm* term_new(lua_State* L) {
inline KiwiTerm* term_new(lua_State* L) {
auto* term = static_cast<KiwiTerm*>(lua_newuserdata(L, sizeof(KiwiTerm)));
push_type(L, TERM);
lua_setmetatable(L, -2);
@@ -254,22 +251,18 @@ inline KiwiExpression* expr_new(lua_State* L, int nterms) {
return expr;
}
inline ConstraintData* constraint_new(
inline Constraint* constraint_new(
lua_State* L,
const KiwiExpression* lhs,
const KiwiExpression* rhs,
kiwi::RelationalOperator op,
double strength
) {
auto** c = static_cast<ConstraintData**>(lua_newuserdata(L, sizeof(ConstraintData*)));
auto* c = kiwi_constraint_new(lhs, rhs, op, strength, lua_newuserdata(L, sizeof(Constraint)));
push_type(L, CONSTRAINT);
lua_setmetatable(L, -2);
if (lk_unlikely(!(*c = kiwi_constraint_new(lhs, rhs, op, strength)))) {
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
lua_error(L);
}
return *c;
return c;
}
// stack disposition: dirty
@@ -294,7 +287,7 @@ KiwiExpression* toexpr(lua_State* L, int idx, KiwiExpression* temp) {
temp->term_count = 1;
push_type(L, VAR);
if (lua_rawequal(L, -1, -3)) {
temp->terms[0].var = *static_cast<VariableData**>(ud);
temp->terms[0].var = static_cast<Variable*>(ud);
temp->terms[0].coefficient = 1.0;
return temp;
}
@@ -338,7 +331,7 @@ inline int push_expr_one(lua_State* L, double constant, const KiwiTerm* term) {
expr->constant = constant;
expr->term_count = 1;
expr->terms[0].coefficient = term->coefficient;
expr->terms[0].var = retain_unmanaged(term->var);
expr->terms[0].var = kiwi_var_retain(term->var);
return 1;
}
@@ -347,21 +340,20 @@ inline int push_expr_pair(lua_State* L, double constant, const KiwiTerm* ta, con
e->constant = constant;
e->term_count = 2;
e->terms[0].coefficient = ta->coefficient;
e->terms[0].var = retain_unmanaged(ta->var);
e->terms[0].var = kiwi_var_retain(ta->var);
e->terms[1].coefficient = tb->coefficient;
e->terms[1].var = retain_unmanaged(tb->var);
e->terms[1].var = kiwi_var_retain(tb->var);
return 1;
}
inline int
push_expr_var_term(lua_State* L, double constant, VariableData* var, const KiwiTerm* t) {
inline int push_expr_var_term(lua_State* L, double constant, Variable* var, const KiwiTerm* t) {
auto* e = expr_new(L, 2);
e->constant = constant;
e->term_count = 2;
e->terms[0].coefficient = 1.0;
e->terms[0].var = retain_unmanaged(var);
e->terms[0].var = kiwi_var_retain(var);
e->terms[1].coefficient = t->coefficient;
e->terms[1].var = retain_unmanaged(t->var);
e->terms[1].var = kiwi_var_retain(t->var);
return 1;
}
@@ -372,23 +364,23 @@ int push_add_expr_term(lua_State* L, const KiwiExpression* expr, const KiwiTerm*
int i = 0;
for (; i < expr->term_count; ++i) {
e->terms[i].coefficient = expr->terms[i].coefficient;
e->terms[i].var = retain_unmanaged(expr->terms[i].var);
e->terms[i].var = kiwi_var_retain(expr->terms[i].var);
}
e->terms[i].coefficient = t->coefficient;
e->terms[i].var = retain_unmanaged(t->var);
e->terms[i].var = kiwi_var_retain(t->var);
return 1;
}
int lkiwi_var_m_add(lua_State* L) {
TypeId type_id_b;
double num = 0.0;
double num;
void* arg_b = try_arg(L, 2, &type_id_b, &num);
if (type_id_b == VAR) {
int isnum_a;
num = lua_tonumberx(L, 1, &isnum_a);
if (isnum_a) {
const KiwiTerm t {*static_cast<VariableData**>(arg_b), 1.0};
const KiwiTerm t {static_cast<Variable*>(arg_b), 1.0};
return push_expr_one(L, num, &t);
}
}
@@ -397,7 +389,7 @@ int lkiwi_var_m_add(lua_State* L) {
if (var_a) {
switch (type_id_b) {
case VAR: {
const KiwiTerm ta {var_a, 1.0}, tb {*static_cast<VariableData**>(arg_b), 1.0};
const KiwiTerm ta {var_a, 1.0}, tb {static_cast<Variable*>(arg_b), 1.0};
return push_expr_pair(L, 0.0, &ta, &tb);
}
case TERM:
@@ -443,7 +435,7 @@ int lkiwi_var_m_mul(lua_State* L) {
auto* var = try_var(L, varidx);
if (var) {
auto* term = term_new(L);
term->var = retain_unmanaged(var);
term->var = kiwi_var_retain(var);
term->coefficient = num;
return 1;
}
@@ -459,20 +451,20 @@ int lkiwi_var_m_div(lua_State* L) {
return op_error(L, "/", 1, 2);
}
auto* term = term_new(L);
term->var = retain_unmanaged(var);
term->var = kiwi_var_retain(var);
term->coefficient = 1.0 / num;
return 1;
}
int lkiwi_var_m_unm(lua_State* L) {
auto* term = term_new(L);
term->var = retain_unmanaged(get_var(L, 1));
term->var = kiwi_var_retain(get_var(L, 1));
term->coefficient = -1.0;
return 1;
}
int lkiwi_var_m_eq(lua_State* L) {
lua_pushboolean(L, get_var(L, 1) == get_var(L, 2));
lua_pushboolean(L, get_var(L, 1)->equals(*get_var(L, 2)));
return 1;
}
@@ -483,7 +475,7 @@ int lkiwi_var_m_tostring(lua_State* L) {
}
int lkiwi_var_m_gc(lua_State* L) {
release_unmanaged(get_var(L, 1));
get_var(L, 1)->~Variable();
return 0;
}
@@ -516,7 +508,7 @@ int lkiwi_var_toterm(lua_State* L) {
double coefficient = luaL_optnumber(L, 2, 1.0);
auto* term = term_new(L);
term->var = retain_unmanaged(var);
term->var = kiwi_var_retain(var);
term->coefficient = coefficient;
return 1;
@@ -545,21 +537,59 @@ constexpr const struct luaL_Reg kiwi_var_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}
};
{0, 0}};
int lkiwi_var_new(lua_State* L) {
const char* name = luaL_optstring(L, 1, "");
var_new(L, name);
return 1;
}
auto* varp = var_new(L);
var_register(L, *varp = make_unmanaged<VariableData>(name));
int lkiwi_term_m_mul(lua_State* L) {
int isnum, termidx = 2;
double num = lua_tonumberx(L, 1, &isnum);
if (!isnum) {
termidx = 1;
num = lua_tonumberx(L, 2, &isnum);
}
if (isnum) {
const auto* term = try_term(L, termidx);
if (term) {
auto* ret = term_new(L);
ret->var = kiwi_var_retain(term->var);
ret->coefficient = term->coefficient * num;
return 1;
}
}
return op_error(L, "*", 1, 2);
}
int lkiwi_term_m_div(lua_State* L) {
const KiwiTerm* term = try_term(L, 1);
int isnum;
double num = lua_tonumberx(L, 2, &isnum);
if (!term || !isnum) {
return op_error(L, "/", 1, 2);
}
auto* ret = term_new(L);
ret->var = kiwi_var_retain(term->var);
ret->coefficient = term->coefficient / num;
return 1;
}
int lkiwi_term_m_unm(lua_State* L) {
const auto* term = get_term(L, 1);
auto* ret = term_new(L);
ret->var = kiwi_var_retain(term->var);
ret->coefficient = -term->coefficient;
return 1;
}
int lkiwi_term_m_add(lua_State* L) {
TypeId type_id_b;
double num = 0.0;
double num;
void* arg_b = try_arg(L, 2, &type_id_b, &num);
if (type_id_b == TERM) {
@@ -576,7 +606,7 @@ int lkiwi_term_m_add(lua_State* L) {
case TERM:
return push_expr_pair(L, 0.0, term_a, static_cast<KiwiTerm*>(arg_b));
case VAR: {
const KiwiTerm term_b {*static_cast<VariableData**>(arg_b), 1.0};
const KiwiTerm term_b {static_cast<Variable*>(arg_b), 1.0};
return push_expr_pair(L, 0.0, term_a, &term_b);
}
case EXPR:
@@ -597,48 +627,6 @@ int lkiwi_term_m_sub(lua_State* L) {
return 1;
}
int lkiwi_term_m_mul(lua_State* L) {
int isnum, termidx = 2;
double num = lua_tonumberx(L, 1, &isnum);
if (!isnum) {
termidx = 1;
num = lua_tonumberx(L, 2, &isnum);
}
if (isnum) {
const auto* term = try_term(L, termidx);
if (term) {
auto* ret = term_new(L);
ret->var = retain_unmanaged(term->var);
ret->coefficient = term->coefficient * num;
return 1;
}
}
return op_error(L, "*", 1, 2);
}
int lkiwi_term_m_div(lua_State* L) {
const KiwiTerm* term = try_term(L, 1);
int isnum;
double num = lua_tonumberx(L, 2, &isnum);
if (!term || !isnum) {
return op_error(L, "/", 1, 2);
}
auto* ret = term_new(L);
ret->var = retain_unmanaged(term->var);
ret->coefficient = term->coefficient / num;
return 1;
}
int lkiwi_term_m_unm(lua_State* L) {
const auto* term = get_term(L, 1);
auto* ret = term_new(L);
ret->var = retain_unmanaged(term->var);
ret->coefficient = -term->coefficient;
return 1;
}
int lkiwi_term_toexpr(lua_State* L) {
return push_expr_one(L, 0.0, get_term(L, 1));
}
@@ -656,7 +644,7 @@ int lkiwi_term_m_tostring(lua_State* L) {
}
int lkiwi_term_m_gc(lua_State* L) {
release_unmanaged(get_term(L, 1)->var);
get_term(L, 1)->var->~Variable();
return 0;
}
@@ -671,10 +659,8 @@ int lkiwi_term_m_index(lua_State* L) {
#else
lua_rawgetp(L, lua_upvalueindex(2), term->var);
#endif
if (lua_isnil(L, -1)) {
auto* varp = var_new(L);
var_register(L, *varp = retain_unmanaged(term->var));
}
if (lua_isnil(L, -1))
var_new(L, *term->var);
return 1;
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
lua_pushnumber(L, term->coefficient);
@@ -703,14 +689,13 @@ constexpr const struct luaL_Reg kiwi_term_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}
};
{0, 0}};
int lkiwi_term_new(lua_State* L) {
auto* var = get_var(L, 1);
double coefficient = luaL_optnumber(L, 2, 1.0);
auto* term = term_new(L);
term->var = retain_unmanaged(var);
term->var = kiwi_var_retain(var);
term->coefficient = coefficient;
return 1;
}
@@ -718,7 +703,7 @@ int lkiwi_term_new(lua_State* L) {
int push_expr_constant(lua_State* L, const KiwiExpression* expr, double constant) {
auto* ne = expr_new(L, expr->term_count);
for (int i = 0; i < expr->term_count; i++) {
ne->terms[i].var = retain_unmanaged(expr->terms[i].var);
ne->terms[i].var = kiwi_var_retain(expr->terms[i].var);
ne->terms[i].coefficient = expr->terms[i].coefficient;
}
ne->constant = constant;
@@ -731,7 +716,7 @@ int push_mul_expr_coeff(lua_State* L, const KiwiExpression* expr, double coeff)
ne->constant = expr->constant * coeff;
ne->term_count = expr->term_count;
for (int i = 0; i < expr->term_count; i++) {
ne->terms[i].var = retain_unmanaged(expr->terms[i].var);
ne->terms[i].var = kiwi_var_retain(expr->terms[i].var);
ne->terms[i].coefficient = expr->terms[i].coefficient * coeff;
}
return 1;
@@ -745,57 +730,16 @@ int push_add_expr_expr(lua_State* L, const KiwiExpression* a, const KiwiExpressi
ne->term_count = na + nb;
for (int i = 0; i < na; i++) {
ne->terms[i].var = retain_unmanaged(a->terms[i].var);
ne->terms[i].var = kiwi_var_retain(a->terms[i].var);
ne->terms[i].coefficient = a->terms[i].coefficient;
}
for (int i = 0; i < nb; i++) {
ne->terms[i + na].var = retain_unmanaged(b->terms[i].var);
ne->terms[i + na].var = kiwi_var_retain(b->terms[i].var);
ne->terms[i + na].coefficient = b->terms[i].coefficient;
}
return 1;
}
int lkiwi_expr_m_add(lua_State* L) {
TypeId type_id_b;
double num = 0.0;
void* arg_b = try_arg(L, 2, &type_id_b, &num);
if (type_id_b == EXPR) {
int isnum_a;
num = lua_tonumberx(L, 1, &isnum_a);
if (isnum_a) {
auto* expr_b = static_cast<const KiwiExpression*>(arg_b);
return push_expr_constant(L, expr_b, num + expr_b->constant);
}
}
const auto* expr_a = try_expr(L, 1);
if (expr_a) {
switch (type_id_b) {
case EXPR:
return push_add_expr_expr(L, expr_a, static_cast<KiwiExpression*>(arg_b));
case TERM:
return push_add_expr_term(L, expr_a, static_cast<KiwiTerm*>(arg_b));
case VAR: {
const KiwiTerm term_b {*static_cast<VariableData**>(arg_b), 1.0};
return push_add_expr_term(L, expr_a, &term_b);
}
case NUMBER:
return push_expr_constant(L, expr_a, num + expr_a->constant);
default:
break;
}
}
return op_error(L, "+", 1, 2);
}
int lkiwi_expr_m_sub(lua_State* L) {
lua_settop(L, 2);
compat_arith_unm(L);
lkiwi_expr_m_add(L);
return 1;
}
int lkiwi_expr_m_mul(lua_State* L) {
int isnum, expridx = 2;
double num = lua_tonumberx(L, 1, &isnum);
@@ -828,6 +772,47 @@ int lkiwi_expr_m_unm(lua_State* L) {
return push_mul_expr_coeff(L, expr, -1.0);
}
int lkiwi_expr_m_add(lua_State* L) {
TypeId type_id_b;
double num;
void* arg_b = try_arg(L, 2, &type_id_b, &num);
if (type_id_b == EXPR) {
int isnum_a;
num = lua_tonumberx(L, 1, &isnum_a);
if (isnum_a) {
auto* expr_b = static_cast<const KiwiExpression*>(arg_b);
return push_expr_constant(L, expr_b, num + expr_b->constant);
}
}
const auto* expr_a = try_expr(L, 1);
if (expr_a) {
switch (type_id_b) {
case EXPR:
return push_add_expr_expr(L, expr_a, static_cast<KiwiExpression*>(arg_b));
case TERM:
return push_add_expr_term(L, expr_a, static_cast<KiwiTerm*>(arg_b));
case VAR: {
const KiwiTerm term_b {static_cast<Variable*>(arg_b), 1.0};
return push_add_expr_term(L, expr_a, &term_b);
}
case NUMBER:
return push_expr_constant(L, expr_a, num + expr_a->constant);
default:
break;
}
}
return op_error(L, "+", 1, 2);
}
int lkiwi_expr_m_sub(lua_State* L) {
lua_settop(L, 2);
compat_arith_unm(L);
lkiwi_expr_m_add(L);
return 1;
}
int lkiwi_expr_value(lua_State* L) {
const auto* expr = get_expr(L, 1);
double sum = expr->constant;
@@ -845,7 +830,7 @@ int lkiwi_expr_terms(lua_State* L) {
for (int i = 0; i < expr->term_count; i++) {
const auto* t = &expr->terms[i];
auto* new_term = term_new(L);
new_term->var = retain_unmanaged(t->var);
new_term->var = kiwi_var_retain(t->var);
new_term->coefficient = t->coefficient;
lua_rawseti(L, -2, i + 1);
}
@@ -879,10 +864,10 @@ int lkiwi_expr_m_tostring(lua_State* L) {
int lkiwi_expr_m_gc(lua_State* L) {
const auto* expr = get_expr(L, 1);
if (expr->owner) {
release_unmanaged(expr->owner);
expr->owner->~Constraint();
} else {
for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) {
release_unmanaged(t->var);
t->var->~Variable();
}
}
return 0;
@@ -920,8 +905,7 @@ constexpr const struct luaL_Reg kiwi_expr_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}
};
{0, 0}};
int lkiwi_expr_new(lua_State* L) {
int nterms = lua_gettop(L) - 1;
@@ -933,7 +917,7 @@ int lkiwi_expr_new(lua_State* L) {
for (int i = 0; i < nterms; i++) {
const auto* term = get_term(L, i + 2);
expr->terms[i].var = retain_unmanaged(term->var);
expr->terms[i].var = kiwi_var_retain(term->var);
expr->terms[i].coefficient = term->coefficient;
}
return 1;
@@ -971,17 +955,16 @@ int lkiwi_constraint_expression(lua_State* L) {
auto* c = get_constraint(L, 1);
const auto& expr = c->expression();
const auto& terms = expr.terms();
const auto term_count = static_cast<int>(terms.size() > INT_MAX ? INT_MAX : terms.size());
const int term_count = terms.size();
auto* ne = expr_new(L, term_count);
ne->owner = retain_unmanaged(c);
ne->owner = kiwi_constraint_retain(c);
ne->constant = expr.constant();
ne->term_count = term_count;
for (int i = 0; i < term_count; ++i) {
const auto& t = terms[static_cast<std::size_t>(i)];
ne->terms[i].var = const_cast<Variable&>(t.variable()).ptr();
ne->terms[i].coefficient = t.coefficient();
ne->terms[i].var = const_cast<Variable*>(&terms[i].variable());
ne->terms[i].coefficient = terms[i].coefficient();
}
return 1;
}
@@ -1041,7 +1024,7 @@ int lkiwi_constraint_m_tostring(lua_State* L) {
}
int lkiwi_constraint_m_gc(lua_State* L) {
release_unmanaged(get_constraint(L, 1));
get_constraint(L, 1)->~Constraint();
return 0;
}
@@ -1073,8 +1056,7 @@ constexpr const struct luaL_Reg kiwi_constraint_m[] = {
{"expression", lkiwi_constraint_expression},
{"add_to", lkiwi_constraint_add_to},
{"remove_from", lkiwi_constraint_remove_from},
{0, 0}
};
{0, 0}};
int lkiwi_constraint_new(lua_State* L) {
const auto* lhs = get_expr_opt(L, 1);
@@ -1088,9 +1070,9 @@ int lkiwi_constraint_new(lua_State* L) {
int push_pair_constraint(
lua_State* L,
VariableData* left,
Variable* left,
double coeff,
VariableData* right,
Variable* right,
double constant,
kiwi::RelationalOperator op,
double strength
@@ -1147,8 +1129,7 @@ constexpr const struct luaL_Reg lkiwi_constraints[] = {
{"pair_ratio", lkiwi_constraints_pair_ratio},
{"pair", lkiwi_constraints_pair},
{"single", lkiwi_constraints_single},
{0, 0}
};
{0, 0}};
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
luaL_newlibtable(L, lkiwi_constraints);
@@ -1199,7 +1180,10 @@ int lkiwi_error_m_tostring(lua_State* L) {
luaL_tolstring(L, -1, 0);
lua_remove(L, -2); // remove item
luaL_addvalue(&buf);
luaL_addstring(&buf, ")");
luaL_addstring(&buf, ")\n");
luaL_traceback(L, L, nullptr, 2);
luaL_addvalue(&buf);
luaL_pushresult(&buf);
return 1;
@@ -1207,8 +1191,7 @@ int lkiwi_error_m_tostring(lua_State* L) {
constexpr const struct luaL_Reg lkiwi_error_m[] = {
{"__tostring", lkiwi_error_m_tostring},
{0, 0}
};
{0, 0}};
int lkiwi_error_mask(lua_State* L) {
int invert = lua_toboolean(L, 2);
@@ -1254,21 +1237,21 @@ int lkiwi_solver_handle_err(lua_State* L, const KiwiErr* err, const KiwiSolver*
int lkiwi_solver_add_constraint(lua_State* L) {
auto* self = get_solver(L, 1);
auto* c = get_constraint(L, 2);
const auto& c = *get_constraint(L, 2);
auto* err = kiwi_solver_add_constraint(self->solver, c);
return lkiwi_solver_handle_err(L, err, self);
}
int lkiwi_solver_remove_constraint(lua_State* L) {
auto* self = get_solver(L, 1);
auto* c = get_constraint(L, 2);
const auto& c = *get_constraint(L, 2);
auto* err = kiwi_solver_remove_constraint(self->solver, c);
return lkiwi_solver_handle_err(L, err, self);
}
int lkiwi_solver_add_edit_var(lua_State* L) {
auto* self = get_solver(L, 1);
auto* var = get_var(L, 2);
const auto& var = *get_var(L, 2);
double strength = luaL_checknumber(L, 3);
auto* err = kiwi_solver_add_edit_var(self->solver, var, strength);
return lkiwi_solver_handle_err(L, err, self);
@@ -1276,14 +1259,14 @@ int lkiwi_solver_add_edit_var(lua_State* L) {
int lkiwi_solver_remove_edit_var(lua_State* L) {
auto* self = get_solver(L, 1);
auto* var = get_var(L, 2);
const auto& var = *get_var(L, 2);
auto* err = kiwi_solver_remove_edit_var(self->solver, var);
return lkiwi_solver_handle_err(L, err, self);
}
int lkiwi_solver_suggest_value(lua_State* L) {
auto* self = get_solver(L, 1);
auto* var = get_var(L, 2);
const auto& var = *get_var(L, 2);
double value = luaL_checknumber(L, 3);
auto* err = kiwi_solver_suggest_value(self->solver, var, value);
return lkiwi_solver_handle_err(L, err, self);
@@ -1301,15 +1284,15 @@ int lkiwi_solver_reset(lua_State* L) {
int lkiwi_solver_has_constraint(lua_State* L) {
auto* s = get_solver(L, 1);
auto* c = get_constraint(L, 2);
lua_pushboolean(L, s->solver.hasConstraint(Constraint(c)));
const auto& c = *get_constraint(L, 2);
lua_pushboolean(L, s->solver.hasConstraint(c));
return 1;
}
int lkiwi_solver_has_edit_var(lua_State* L) {
auto* s = get_solver(L, 1);
auto* var = get_var(L, 2);
lua_pushboolean(L, s->solver.hasEditVariable(Variable(var)));
const auto& var = *get_var(L, 2);
lua_pushboolean(L, s->solver.hasEditVariable(var));
return 1;
}
@@ -1354,26 +1337,26 @@ int lkiwi_add_remove_tab(lua_State* L, F&& fn) {
int lkiwi_solver_add_constraints(lua_State* L) {
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
return kiwi_solver_add_constraint(s->solver, get_constraint(L, -1));
return kiwi_solver_add_constraint(s->solver, *get_constraint(L, -1));
});
}
int lkiwi_solver_remove_constraints(lua_State* L) {
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
return kiwi_solver_add_constraint(s->solver, get_constraint(L, -1));
return kiwi_solver_add_constraint(s->solver, *get_constraint(L, -1));
});
}
int lkiwi_solver_add_edit_vars(lua_State* L) {
double strength = luaL_checknumber(L, 3);
return lkiwi_add_remove_tab(L, [strength](lua_State* L, KiwiSolver* s) {
return kiwi_solver_add_edit_var(s->solver, get_var(L, -1), strength);
return kiwi_solver_add_edit_var(s->solver, *get_var(L, -1), strength);
});
}
int lkiwi_solver_remove_edit_vars(lua_State* L) {
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
return kiwi_solver_remove_edit_var(s->solver, get_var(L, -1));
return kiwi_solver_remove_edit_var(s->solver, *get_var(L, -1));
});
}
@@ -1381,10 +1364,11 @@ int lkiwi_solver_suggest_values(lua_State* L) {
auto* self = get_solver(L, 1);
int narg = lua_gettop(L);
// catch this obnoxious case which is always a bug
// block this particularly obnoxious case which is always a bug
if (lua_type(L, 2) == LUA_TSTRING) {
luaL_typeerror(L, 2, "indexable");
}
// block this particularly obnoxious case which is always a bug
if (lua_type(L, 3) == LUA_TSTRING) {
luaL_typeerror(L, 3, "indexable");
}
@@ -1395,7 +1379,7 @@ int lkiwi_solver_suggest_values(lua_State* L) {
lua_geti(L, 3, i);
double value = luaL_checknumber(L, -1);
const KiwiErr* err = kiwi_solver_suggest_value(self->solver, var, value);
const KiwiErr* err = kiwi_solver_suggest_value(self->solver, *var, value);
if (err) {
error_new(L, err, 1, narg + 1 /* item_absi */);
unsigned error_mask = self->error_mask;
@@ -1460,8 +1444,7 @@ constexpr const struct luaL_Reg kiwi_solver_m[] = {
{"set_error_mask", lkiwi_solver_set_error_mask},
{"__tostring", lkiwi_solver_m_tostring},
{"__gc", lkiwi_solver_m_gc},
{0, 0}
};
{0, 0}};
int lkiwi_solver_new(lua_State* L) {
lua_Integer error_mask;
@@ -1558,8 +1541,7 @@ constexpr const struct luaL_Reg lkiwi[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}
};
{0, 0}};
int no_member_mt_index(lua_State* L) {
luaL_error(L, "attempt to access non-existent member '%s'", lua_tostring(L, 2));
@@ -1580,7 +1562,7 @@ void register_type_n(
const luaL_Reg* m,
size_t mcnt
) {
lua_createtable(L, 0, static_cast<int>(mcnt + 2));
lua_createtable(L, 0, mcnt + 2);
lua_pushvalue(L, -2); // no_member_mt
lua_setmetatable(L, -2);
lua_pushstring(L, name);
@@ -1623,13 +1605,7 @@ void compat_init(lua_State*, int) {}
} // namespace
#if defined __GNUC__ && (!defined _WIN32 || defined __CYGWIN__)
#define LJKIWI_EXPORT __attribute__((__visibility__("default")))
#elif defined _WIN32
#define LJKIWI_EXPORT __declspec(dllexport)
#endif
extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
extern "C" int luaopen_ckiwi(lua_State* L) {
luaL_checkversion(L);
/* context table */
@@ -1637,8 +1613,6 @@ extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
int ctx_i = lua_gettop(L);
compat_init(L, ctx_i);
lua_pushliteral(L, "kiwi library memory allocation error");
lua_rawseti(L, ctx_i, MEM_ERR_MSG);
no_member_mt_new(L);
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);

View File

@@ -1,40 +0,0 @@
rockspec_format = "3.0"
package = "kiwi"
version = "0.1.0-1"
source = {
url = "git+https://github.com/jkl1337/ljkiwi",
tag = "v0.1.0",
}
description = {
summary = "LuaJIT FFI and Lua binding for the Kiwi constraint solver.",
detailed = [[
kiwi is a LuaJIT FFI and Lua binding for the Kiwi constraint solver. Kiwi is a fast
implementation of the Cassowary constraint solving algorithm. kiwi provides
reasonably efficient bindings using the LuaJIT FFI and convential Lua C bindings.]],
license = "MIT",
issues_url = "https://github.com/jkl1337/ljkiwi/issues",
maintainer = "John Luebs",
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "make",
build_variables = {
LUAROCKS = "1",
LUA = "$(LUA)",
CFLAGS = "$(CFLAGS)",
LUA_INCDIR = "$(LUA_INCDIR)",
LUA_LIBDIR = "$(LUA_LIBDIR)",
LUALIB = "$(LUALIB)",
LIBFLAG = "$(LIBFLAG)",
LIB_EXT = "$(LIB_EXTENSION)",
OBJ_EXT = "$(OBJ_EXTENSION)",
},
install_variables = {
INST_LIBDIR = "$(LIBDIR)",
INST_LUADIR = "$(LUADIR)",
LIB_EXT = "$(LIB_EXTENSION)",
},
}

32
t.lua Normal file
View File

@@ -0,0 +1,32 @@
local kiwi = require("kiwi")
local c
--debug.getupvalue
do
local v1 = kiwi.Var("v1")
local v2 = kiwi.Var("v2")
local v3 = kiwi.Var("v3")
local v4 = kiwi.Var("v4")
local v5 = kiwi.Var("v5")
local c1 = (3 * v1 + 4 * v2 + 6 * v3):eq(0)
local c2 = (6 * v4 + 4 * v5 + 2 * v1 + 1.4 * v1 / 0.3):le(1000)
local e = c1:expression()
local s = kiwi.Solver()
s:add_constraints({ c1, c2, c1 })
print(s:dumps())
end
-- c = (3 * v1 + 4 * v2 + 6 * v3):eq(0)
-- local t = c:expression():terms()
-- print(t[2].var)
-- for k, v in ipairs(t) do
-- print(k, v.var, v.coefficient)
-- end
-- for k, v in pairs(kiwi.ErrKind) do
-- print(k, v)
-- end

204
tmp/enaml.lua Normal file
View File

@@ -0,0 +1,204 @@
local kiwi = require("kiwi")
local Var = kiwi.Var
---@param solver kiwi.Solver
local function build_solver(solver)
-- create custom strength
--
local width = Var("width")
local height = Var("height")
local mmedium = kiwi.strength.create(0.0, 1.0, 0.0, 1.25)
local smedium = kiwi.strength.create(0.0, 100, 0.0)
local left = Var("left")
local top = Var("top")
local contents_top = Var("contents_top")
local contents_bottom = Var("contents_bottom")
local contents_left = Var("contents_left")
local contents_right = Var("contents_right")
local midline = Var("midline")
local ctleft = Var("ctleft")
local ctheight = Var("ctheight")
local cttop = Var("cttop")
local ctwidth = Var("ctwidth")
local lb1left = Var("lb1left")
local lb1height = Var("lb1height")
local lb1top = Var("lb1top")
local lb1width = Var("lb1width")
local lb2left = Var("lb2left")
local lb2height = Var("lb2height")
local lb2top = Var("lb2top")
local lb2width = Var("lb2width")
local lb3left = Var("lb3left")
local lb3height = Var("lb3height")
local lb3top = Var("lb3top")
local lb3width = Var("lb3width")
local fl1left = Var("fl1left")
local fl1height = Var("fl1height")
local fl1top = Var("fl1top")
local fl1width = Var("fl1width")
local fl2left = Var("fl2left")
local fl2height = Var("fl2height")
local fl2top = Var("fl2top")
local fl2width = Var("fl2width")
local fl3left = Var("fl3left")
local fl3height = Var("fl3height")
local fl3top = Var("fl3top")
local fl3width = Var("fl3width")
solver:add_edit_var(width, kiwi.strength.STRONG)
solver:add_edit_var(height, kiwi.strength.STRONG)
local strong, medium, weak = kiwi.strength.STRONG, kiwi.strength.MEDIUM, kiwi.strength.WEAK
local constraints = {
(left + -0):ge(0),
(height + 0):eq(0, medium),
(top + -0):ge(0),
(width + -0):ge(0),
(height + -0):ge(0),
(-top + contents_top + -10):eq(0),
(lb3height + -16):eq(0, strong),
(lb3height + -16):ge(0, strong),
(ctleft + -0):ge(0),
(cttop + -0):ge(0),
(ctwidth + -0):ge(0),
(ctheight + -0):ge(0),
(fl3left + -0):ge(0),
(ctheight + -24):ge(0, smedium),
(ctwidth + -1.67772e+07):le(0, smedium),
(ctheight + -24):le(0, smedium),
(fl3top + -0):ge(0),
(fl3width + -0):ge(0),
(fl3height + -0):ge(0),
(lb1width + -67):eq(0, weak),
(lb2width + -0):ge(0),
(lb2height + -0):ge(0),
(fl2height + -0):ge(0),
(lb3left + -0):ge(0),
(fl2width + -125):ge(0, strong),
(fl2height + -21):eq(0, strong),
(fl2height + -21):ge(0, strong),
(lb3top + -0):ge(0),
(lb3width + -0):ge(0),
(fl1left + -0):ge(0),
(fl1width + -0):ge(0),
(lb1width + -67):ge(0, strong),
(fl2left + -0):ge(0),
(lb2width + -66):eq(0, weak),
(lb2width + -66):ge(0, strong),
(lb2height + -16):eq(0, strong),
(fl1height + -0):ge(0),
(fl1top + -0):ge(0),
(lb2top + -0):ge(0),
(-lb2top + lb3top + -lb2height + -10):eq(0, mmedium),
(-lb3top + -lb3height + fl3top + -10):ge(0),
(-lb3top + -lb3height + fl3top + -10):eq(0, mmedium),
(contents_bottom + -fl3height + -fl3top + -0):eq(0, mmedium),
(fl1top + -contents_top + 0):ge(0),
(fl1top + -contents_top + 0):eq(0, mmedium),
(contents_bottom + -fl3height + -fl3top + -0):ge(0),
(-left + -width + contents_right + 10):eq(0),
(-top + -height + contents_bottom + 10):eq(0),
(-left + contents_left + -10):eq(0),
(lb3left + -contents_left + 0):eq(0, mmedium),
(fl1left + -midline + 0):eq(0, strong),
(fl2left + -midline + 0):eq(0, strong),
(ctleft + -midline + 0):eq(0, strong),
(fl1top + 0.5 * fl1height + -lb1top + -0.5 * lb1height + 0):eq(0, strong),
(lb1left + -contents_left + 0):ge(0),
(lb1left + -contents_left + 0):eq(0, mmedium),
(-lb1left + fl1left + -lb1width + -10):ge(0),
(-lb1left + fl1left + -lb1width + -10):eq(0, mmedium),
(-fl1left + contents_right + -fl1width + -0):ge(0),
(width + 0):eq(0, medium),
(-fl1top + fl2top + -fl1height + -10):ge(0),
(-fl1top + fl2top + -fl1height + -10):eq(0, mmedium),
(cttop + -fl2top + -fl2height + -10):ge(0),
(-ctheight + -cttop + fl3top + -10):ge(0),
(contents_bottom + -fl3height + -fl3top + -0):ge(0),
(cttop + -fl2top + -fl2height + -10):eq(0, mmedium),
(-fl1left + contents_right + -fl1width + -0):eq(0, mmedium),
(-lb2top + -0.5 * lb2height + fl2top + 0.5 * fl2height + 0):eq(0, strong),
(-contents_left + lb2left + 0):ge(0),
(-contents_left + lb2left + 0):eq(0, mmedium),
(fl2left + -lb2width + -lb2left + -10):ge(0),
(-ctheight + -cttop + fl3top + -10):eq(0, mmedium),
(contents_bottom + -fl3height + -fl3top + -0):eq(0, mmedium),
(lb1top + -0):ge(0),
(lb1width + -0):ge(0),
(lb1height + -0):ge(0),
(fl2left + -lb2width + -lb2left + -10):eq(0, mmedium),
(-fl2left + -fl2width + contents_right + -0):eq(0, mmedium),
(-fl2left + -fl2width + contents_right + -0):ge(0),
(lb3left + -contents_left + 0):ge(0),
(lb1left + -0):ge(0),
(0.5 * ctheight + cttop + -lb3top + -0.5 * lb3height + 0):eq(0, strong),
(ctleft + -lb3left + -lb3width + -10):ge(0),
(-ctwidth + -ctleft + contents_right + -0):ge(0),
(ctleft + -lb3left + -lb3width + -10):eq(0, mmedium),
(fl3left + -contents_left + 0):ge(0),
(fl3left + -contents_left + 0):eq(0, mmedium),
(-ctwidth + -ctleft + contents_right + -0):eq(0, mmedium),
(-fl3left + contents_right + -fl3width + -0):eq(0, mmedium),
(-contents_top + lb1top + 0):ge(0),
(-contents_top + lb1top + 0):eq(0, mmedium),
(-fl3left + contents_right + -fl3width + -0):ge(0),
(lb2top + -lb1top + -lb1height + -10):ge(0),
(-lb2top + lb3top + -lb2height + -10):ge(0),
(lb2top + -lb1top + -lb1height + -10):eq(0, mmedium),
(fl1height + -21):eq(0, strong),
(fl1height + -21):ge(0, strong),
(lb2left + -0):ge(0),
(lb2height + -16):ge(0, strong),
(fl2top + -0):ge(0),
(fl2width + -0):ge(0),
(lb1height + -16):ge(0, strong),
(lb1height + -16):eq(0, strong),
(fl3width + -125):ge(0, strong),
(fl3height + -21):eq(0, strong),
(fl3height + -21):ge(0, strong),
(lb3height + -0):ge(0),
(ctwidth + -119):ge(0, smedium),
(lb3width + -24):eq(0, weak),
(lb3width + -24):ge(0, strong),
(fl1width + -125):ge(0, strong),
}
for _, c in ipairs(constraints) do
print(c)
solver:add_constraint(c)
end
return width, height
end
local function main()
local sizes = {
{ w = 400, h = 600 },
{ w = 600, h = 400 },
{ w = 800, h = 1200 },
{ w = 1200, h = 800 },
{ w = 400, h = 800 },
{ w = 800, h = 400 },
}
for i = 1, 1 do
local solver = kiwi.Solver()
local width, height = build_solver(solver)
for _, size in ipairs(sizes) do
solver:suggest_value(width, size.w)
solver:suggest_value(height, size.h)
solver:update_vars()
print(width:value(), height:value())
end
end
end
main()
collectgarbage("collect")
collectgarbage("collect")
collectgarbage("collect")
collectgarbage("collect")

90
tmp/ex.lua Normal file
View File

@@ -0,0 +1,90 @@
local kiwi = require("kiwi")
local Var = kiwi.Var
local Button = setmetatable({}, {
__call = function(_, identifier)
return setmetatable({
left = Var(identifier .. " left"),
width = Var(identifier .. " width"),
}, {
__tostring = function(self)
return "Button(" .. self.left:value() .. ", " .. self.width:value() .. ")"
end,
})
end,
})
local b1 = Button("b1")
local b2 = Button("b2")
local left_edge = Var("left")
local right_edge = Var("width")
local STRONG = kiwi.Strength.STRONG
local constraints
for i = 1, 200 do
q = kiwi.Term(left_edge, 2.0)
end
for i = 1, 50, 1 do
-- stylua: ignore start
constraints = {
left_edge :eq(0.0),
-- two buttons are the same width
b1.width :eq(b2.width),
-- button1 starts 50 for the left margin
(1.00 * b1.left) :eq(left_edge + 50),
-- button2 ends 50 from the right margin
right_edge :eq(b2.left + b2.width + 50),
-- button2 starts at least 100 from the end of button1. This is the "elastic" constraint
b2.left :ge(b1.left + b1.width + 100),
-- button1 has a minimum width of 87
b1.width :ge(87),
-- button1 has a preferred width of 87
b1.width :eq(87, STRONG),
-- button2 has minimum width of 113
b2.width :ge(113),
-- button2 has a preferred width of 113
b2.width :eq(113, STRONG),
}
-- stylua: ignore end
--
k = kiwi.Strength.create(0.0, 1.0, 0.0, 1.25)
end
local solver = kiwi.Solver()
for _, c in ipairs(constraints) do
print(c)
solver:add_constraint(c)
end
solver:update_vars()
print(b1) -- Button(50, 113)
print(b2) -- Button(263, 113)
print(left_edge:value()) -- 0
print(right_edge:value()) -- 426
solver:add_edit_var(right_edge, STRONG)
solver:suggest_value(right_edge, 500)
solver:update_vars()
-- solver:dump()
-- print(b1) -- Button(50, 113)
-- print(b2) -- Button(337, 113)
-- print(right_edge:value()) -- 500
-- print(solver:dumps())
-- solver = kiwi.Solver()
-- local trailing = Var("trailing")
-- local leading = Var("leading")
-- solver:add_constraint(kiwi.new_single_constraint(trailing, 86))
-- solver:add_constraint(kiwi.new_pair_constraint(trailing, leading, 8.0))
-- print(solver:dumps())
-- solver:update_vars()
-- print(trailing:value()) -- 86
-- print(leading:value()) -- 76

28
tmp/ex2.lua Normal file
View File

@@ -0,0 +1,28 @@
local kiwi = require("kiwi")
local Var = kiwi.Var
local function doit(v, i)
v:set(i)
end
local v = Var("fuckit")
local s = kiwi.Solver()
local e
for i = 1, 1000 do
e = kiwi.Expression(5.0, kiwi.Term(v, 3.0), kiwi.Term(v, 2.0))
end
--local e = (v + 1)
s:add_constraint(e:eq(100))
print(v:value())
s:update_vars()
v:set_name("fuck")
print(v:value())
s:dump()

49
tmp/ex3.lua Normal file
View File

@@ -0,0 +1,49 @@
local kiwi = require("kiwi")
local ffi = require("ffi")
local t
local e
local strformat = string.format
local Strength = kiwi.Strength
--local bad_str = "-1 v8 + -1 v4 + -1 v2 + -4 v9 + -2 v3 + -1 v6 + -1 v5 + -1 v1 + -2 v7 + -5"
-- local b = ""
function s(self)
local ops = {
[0] = "<=",
">=",
"==",
}
local strengths = {
[Strength.REQUIRED] = "required",
[Strength.STRONG] = "strong",
[Strength.MEDIUM] = "medium",
[Strength.WEAK] = "weak",
}
local strength = self:strength()
--local e = self:expression()
local s = strformat("%s %s %s", "aaa", tostring(self:expression()), tonumber(self:op()))
return s
end
for i = 1, 2000 do
local v1 = kiwi.Var("v1")
local v2 = kiwi.Var("v2")
local v3 = kiwi.Var("v3")
local v4 = kiwi.Var("v4")
local v5 = kiwi.Var("v5")
local v6 = kiwi.Var("v6")
local v7 = kiwi.Var("v7")
local v8 = kiwi.Var("v8")
local v9 = kiwi.Var("v9")
local v10 = kiwi.Var("v9")
do
--
local c = ((-(v1 + v2 + 2 * v3 + v4 + 3)):eq(v5 + v6 + 2 * v7 + v8 + 4 * v9 + 3))
s(c)
end
end
print(t)

37
tmp/ex4.lua Normal file
View File

@@ -0,0 +1,37 @@
local kiwi = require("kiwi")
local ffi = require("ffi")
local Var = kiwi.Var
local v1 = Var("v1")
local v2 = Var("v2")
local v3 = Var("v3")
local v4 = Var("v4")
local v5 = Var("v5")
local v6 = Var("v6")
local f1 = kiwi.f1
local f2 = kiwi.f2
local single = kiwi.constraints.single
local function execute_times(f, times)
local begin = os.clock()
for _ = 1, times do
f()
end
local finish = os.clock()
return finish - begin
end
local t = execute_times(function()
return kiwi.constraints.pair_ratio(v1, 2.0, v2, 3.0)
end, 6000000)
print(t)
-- local t = execute_times(function()
-- return pair_ratio2(v1, 2.0, v2, 3.0)
-- end, 2000000)
-- print(t)

41
tmp/ex5.lua Normal file
View File

@@ -0,0 +1,41 @@
local kiwi = require("kiwi")
local Var = kiwi.Var
x1 = Var("x1")
x2 = Var("x2")
xm = Var("xm")
local constraints = { x1:ge(0), x2:le(100), x2:ge(x1 + 10), xm:eq((x1 + x2) / 2) }
local solver = kiwi.Solver()
for _, c in ipairs(constraints) do
solver:add_constraint(c)
end
local c = kiwi.constraints.single(x1, 40, "EQ", kiwi.Strength.WEAK)
solver:add_constraint(c)
constraints[#constraints + 1] = c
solver:add_edit_var(xm, kiwi.Strength.STRONG)
solver:suggest_value(xm, 60)
solver:update_vars()
print(xm:value(), x1:value(), x2:value())
for _, c in ipairs(constraints) do
print(c, c:violated())
end
solver:suggest_value(xm, 90)
solver:update_vars()
print(xm:value(), x1:value(), x2:value())
for _, c in ipairs(constraints) do
print(c, c:violated())
end

26
tmp/t.lua Normal file
View File

@@ -0,0 +1,26 @@
local k = require("kiwi")
s = k.Solver()
--s:set_error_mask({ "KiwiErrUnknownEditVariable" }, false)
local v1 = k.Var("v1")
local v2 = k.Var("v2")
local v3 = k.Var("v3")
local v4 = k.Var("v4")
print((2 * v1 - 5 * v2))
os.exit()
local c1 = k.constraints.pair_ratio(v1, -4, v2, 22, "GE", 1000)
local c2 = k.constraints.pair_ratio(v3, -6, v4, 29, "LE", 1000)
local vo = c1:expression():terms()[1].var
print(v1 == vo)
-- print(v1:name())
-- print(v1 * 5)
local e = k.Expression(6, k.Term(v1, 2), k.Term(v2, 3))
--print(e)
--print(k.strength.MEDIUM)

66
tmp/test.lua Normal file
View File

@@ -0,0 +1,66 @@
local kiwi = require("kiwi")
local Var = kiwi.Var
local Strength = kiwi.Strength
local Button_mt = {}
---@class Button
---@field left kiwi.Var
---@field width kiwi.Var
---@overload fun(identifier: string): Button
local Button = setmetatable({}, Button_mt)
function Button_mt.__call(_, identifier)
return setmetatable({
left = Var("left" .. identifier),
width = Var("width" .. identifier),
}, {
__index = Button,
__tostring = function(self)
return "Button(" .. self.left:value() .. ", " .. self.width:value() .. ")"
end,
})
end
local b1 = Button("b1")
local b2 = Button("b2")
local left_limit = kiwi.Var("left")
local right_limit = kiwi.Var("width")
-- stylua: ignore start
local constraints = {
left_limit :eq( 0 ),
b1.width :eq( b2.width ),
b1.left :eq( left_limit + 50),
right_limit:eq( b2.left + b2.width + 50),
b2.left :ge( b1.left + b1.width + 100),
b1.width :ge( 87 ),
b1.width :eq( 87, Strength.STRONG),
b2.width :ge( 113 ),
b2.width :eq( 113, Strength.STRONG),
}
-- stylua: ignore end
local s = kiwi.Solver()
for _, c in ipairs(constraints) do
s:add_constraint(c)
end
print(constraints[4]:expression())
print(constraints[5]:expression())
kiwi.Constraint(b1.left + 1, "EQ")
s:update_vars()
print(b1)
print(b2)
print(left_limit:value())
print(right_limit:value())
print(s:dumps())