[
  {
    "path": ".github/workflows/test.yml",
    "content": "# This is a basic workflow to help you get started with Actions\n\nname: CI\n\n# Controls when the action will run. \non:\n  # Triggers the workflow on push or pull request events but only for the master branch\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\n# A workflow run is made up of one or more jobs that can run sequentially or in parallel\njobs:\n  test_ubuntu:\n    if: \"! contains(toJSON(github.event.commits.*.message), '[skip ci]')\"\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [\"ubuntu-latest\"]\n    steps:\n      - name: Install LCov\n        run: |\n          sudo apt install libperlio-gzip-perl libjson-perl libcapture-tiny-perl libdatetime-perl\n          wget \"https://github.com/linux-test-project/lcov/releases/download/v1.16/lcov-1.16.tar.gz\"\n          tar zxf \"lcov-1.16.tar.gz\"\n          cd \"lcov-1.16\"\n          sudo make install\n      - uses: actions/checkout@master\n      - name: Run Tests\n        run: |\n          gcc -o test -O test.c --coverage && ./test\n      - name: Run LCov\n        run: |\n          mkdir -p coverage\n          lcov -c -d . -o coverage/lcov.info.all\n          lcov -r coverage/lcov.info.all '/usr/*' -o coverage/lcov.info\n      - name: Coveralls\n        uses: coverallsapp/github-action@master\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          parallel: true\n  finish:\n    needs: test_ubuntu\n    runs-on: ubuntu-latest\n    steps:\n    - name: Coveralls Finished\n      uses: coverallsapp/github-action@master\n      with:\n        github-token: ${{ secrets.github_token }}\n        parallel-finished: true\n"
  },
  {
    "path": ".gitignore",
    "content": "*.dll\n*.exp\n*.ilk\n*.lib\n*.pdb\n*.exe\n\n*.so\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: c\n\ncompiler:\n    - gcc\n\nbefore_install:\n    - pip install --user urllib3[secure] cpp-coveralls\n\n    # Work around https://github.com/eddyxu/cpp-coveralls/issues/108 by manually\n    # installing the pyOpenSSL module and injecting it into urllib3 as per\n    # https://urllib3.readthedocs.io/en/latest/user-guide.html#ssl-py2\n    - sed -i -e '/^import sys$/a import urllib3.contrib.pyopenssl\\nurllib3.contrib.pyopenssl.inject_into_urllib3()' `which coveralls`\n\n\ninstall:\n    - gcc -shared -Wall -O3 -Wextra -pedantic -std=c89 -xc amoeba.h -o amoeba.so\n    - gcc -Wall -fprofile-arcs -ftest-coverage -O0 -Wextra -pedantic -std=c89 test.c -o test\n\nscript:\n    - ./test\n\nafter_success:\n    - coveralls\n\nnotifications:\n    email:\n        on_success: change\n        on_failure: always\n\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Xavier Wang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Amoeba -- the constraint solving algorithm in pure C\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/starwing/amoeba/test.yml?branch=master)](https://github.com/starwing/amoeba/actions?query=branch%3Amaster)[![Coverage Status](https://img.shields.io/coveralls/github/starwing/amoeba)](https://coveralls.io/github/starwing/amoeba?branch=master)\n\nAmoeba is a pure C implement of Cassowary algorithm. It:\n- Uses Clean C, which is the cross set of ANSI C89 and C++, like the Lua language.\n- Is a single-file library. For more single-file library, see the stb project [here][1].\n- Largely impressed by [kiwi][2], the C++ implement of Cassowary algorithm, and the algorithm [paper][3].\n- With a Lua binding.\n- With the same license as the [Lua language][4].\n\n[1]: https://github.com/nothings/stb\n[2]: https://github.com/nucleic/kiwi\n[3]: http://constraints.cs.washington.edu/solvers/uist97.html\n[4]: https://www.lua.org/license.html\n\nImprovement relative to Kiwi:\n- Single header library that easy to distribution.\n- Lua binding vs. Python binding.\n- Suggest cache makes suggest performance much better than kiwi.\n- Dump/load support improves the performance of building solver.\n\n## How To Use\n\nThis libary export a constraint solver interface, to solve a constraint problem, you should use it in steps:\n\n- create a new solver: `am_newsolver()`\n- create some variables: `am_newvariable()` binding the `am_Num` values.\n- create some constraints that may use variables: `am_newconstraint()`.\n- make constraints by construct equation using:\n  - `am_addterm()` add a $a \\times variable$ term to constraint equation items.\n  - `am_setrelation()` to specify the equal/greater/less sign in center of equation.\n  - `am_addconstant()` to add a number without variable.\n  - `am_setstrength()` to specify the priority of this constraint in all constraints.\n- after make up a constraint, you could add it into solver by `am_add()`.\n- and you can read out the result of each variable from the binding `am_Num`.\n- or you can manually specify a new value to variable with `am_sugguest()`.\n- after done, use `am_delsolver()` to free al memory.\n\nbelow is a tiny example to demonstrate the steps:\n\n```c\n#define AM_IMPLEMENTATION // include implementations of library\n#include \"amoeba.h\"       // and interface\n\nint main(void) {\n    // first, create a solver:\n    am_Solver *S = am_newsolver(NULL, NULL);\n    int ret;\n\n    // create some variable:\n    am_Num l, m, r;\n    am_Id vl = am_newvariable(S, &l);\n    am_Id vm = am_newvariable(S, &m);\n    am_Id vr = am_newvariable(S, &r);\n\n    // create the constraint: \n    am_Constraint *c1 = am_newconstraint(S, AM_REQUIRED);\n    am_Constraint *c2 = am_newconstraint(S, AM_REQUIRED);\n\n    // c1: m is in middle of l and r:\n    //     i.e. m = (l + r) / 2, or 2*m = l + r\n    am_addterm(c1, vm, 2.f);\n    am_setrelation(c1, AM_EQUAL);\n    am_addterm(c1, vl, 1.f);\n    am_addterm(c1, vr, 1.f);\n    // apply c1\n    ret = am_add(c1);\n    assert(ret == AM_OK);\n\n    // c2: r - l >= 100\n    am_addterm(c2, vr, 1.f);\n    am_addterm(c2, vl, -1.f);\n    am_setrelation(c2, AM_GREATEQUAL);\n    am_addconstant(c2, 100.f);\n    // apply c2\n    ret = am_add(c2);\n    assert(ret == AM_OK);\n\n    // now we set variable l to 20\n    am_suggest(S, vl, 20.f);\n\n    // and see the value of m and r:\n    am_updatevars(S);\n\n    // r should by 20 + 100 == 120:\n    assert(r == 120.f);\n\n    // and m should in middle of l and r:\n    assert(m == 70.f);\n    \n    // done with solver\n    am_delsolver(S);\n    return 0;\n}\n```\n\n\n## Reference\n\n### Return value\n\nAll functions below that returns `int` are returning error codes:\n- `AM_OK`: Operations success.\n- `AM_FAILED`: The operation is failed, most caused by invalid arguments.\n- `AM_UNSATISFIED`: Specific constraints added cannot satisfied.\n- `AM_UNBOUND`: Failed to add constraints because there are unbound variables in it\n\n### Dump & Load\n\nAmoeba supports to store whole state of solver into bianry data and to load\nback in new solver. Notice that the state of loaded solver will be earsed. \n\nTo perform a dump or load operations, you should prepare `am_Dumper` or\n`am_Loader` to control the behavior in operations.\n\n- `struct am_Dumper`\n\n  A user-defined dump behavior control type.  The user must provide `reader`\n  and `var_name` function pointers in it. there is a example:\n\n  ```c\n  /* dumps into a string buffer */\n  typedef struct MyDumper {\n      am_Dumper base;\n      char buf[MY_BUF_SIZE]; /* buffer */\n      size_t len;            /* and length */\n  } MyDumper;\n\n  static const char *dump_varname(am_Dumper *d, unsigned idx, am_Id var, am_Num *value) {\n      /* you should returns a var name here, or NULL if you do not want a name\n       * for this variable.\n       * A common implement retrives variable context from value pointer, and\n       * get name from it.  */\n      MyContext *ctx = (MyContext*)value;\n      return ctx->name;\n  }\n\n  static const char *dump_consname(am_Dumper *d, unsigned idx, am_Constraint *cons) {\n      /* Same as `var_name`, but retrieve constraint's name */\n      MyContext *ctx = *(MyContext**)cons;\n      return ctx->name;\n  }\n\n  static int dump_writer(am_Dumper *d, const void *buf, size_t len) {\n      /* Write the actually data */\n      MyDumper *myd = (MyDumper*)d;\n      if (myd->len + len > MY_BUF_SIZE)\n          return AM_FAILED; /* data out of buffer, errors returned */\n      memcpy(myd->buf + myd->len, buf, len);\n      myd->len += len;\n      /* All return value other than `AM_OK` will be returned directly by\n       * `am_dump()`. */\n      return AM_OK;\n  }\n\n  /* do actually dump */\n  MyDumper d;\n  d.base.var_name  = dump_varname;\n  d.base.cons_name = dump_consname;\n  d.base.writer    = dump_writer;\n  d.len = 0;\n\n  int ret = am_dump(solver, &d.base);\n  assert(ret == AM_OK);\n  /* now uses the d.buf & d.len */\n  ```\n\n- `struct am_Loader`\n\n  A user-defined dump behavior control type.  The user must provide `reader`\n  and `load_var` function pointers in it. there is a example:\n\n  ```c\n  /* loads from a string buffer */\n  typedef struct MyLoader {\n      am_Loader base;\n      VarMap    vars;\n      ConsMap   cons;\n      const char *buf; /* the buffer to read */\n      size_t remain;   /* remain bytes */\n  } MyLoader;\n\n  static am_Num *load_var(am_Loader *l, const char *name, unsigned idx, am_Id var) {\n      /* you should returns a binding for variable named `name`, or `AM_FAILED`\n       * will be returned by `am_load()`.\n       * A common implement creates variable context into some map, returns it.\n       * Notice that `name` can be `NULL` if no name avaialable.  */\n      MyLoader *myl = (MyLoader*)l;\n      MyContext *ctx = my_create_ctx_by_idx_and_name(&myl->vars, idx, name);\n      return &ctx->value;\n  }\n\n  static void load_cons(am_Loader *l, const char *name, unsigned idx, am_Constraint *cons) {\n      /* same as `var_name`, but can be NULL in `am_Loader*` means do not\n       * retrieve any constraints from previous state.\n       * Notice that `name` can be `NULL`.  */\n      MyLoader *myl = (MyLoader*)l;\n      MyContext *ctx = save_cons_and_create_context(&myl->cons, idx, name, cons);\n      *(MyContext**)cons = ctx;\n  }\n\n  static const char *load_reader(am_Loader *l, size_t *plen) {\n      /* Get the data to reads, if returns `NULL` or store `0` into `plen`,\n       * stops reading */\n      MyLoader *myl = (MyLoader*)l;\n      const char *buf = myl->buf;\n      *plen = myl->len;\n      if (buf) {\n          my->buf = NULL;\n          my->len = 0;\n      }\n      return buf;\n  }\n\n  /* do actually load */\n  MyLoader l;\n  l.base.load_var  = load_var;\n  l.base.load_cons = load_cons; /* optional */\n  l.base.reader    = load_reader;\n  d.buf = get_some_data(&d.len);\n\n  am_Solver *solver = am_newsolver(NULL, NULL);\n  int ret = am_load(solver, &d.base);\n  assert(ret == AM_OK);\n  /* now uses the solver */\n  ```\n\n### Routines\n\n- `am_Solver *am_newsolver(am_Allocf *allocf, void *ud);`\n\n  Create a new solver with custom memory alloculator, pass NULL for use default ones.\n\n- `void am_resetsolver(am_Solver *solver);`\n\n  Remove all constraints from solver, all variables and constraints created are keeped.\n\n- `void am_delsolver(am_Solver *solver);`\n\n  Delete a solver and frees all memory it used, after that, all variables/constraints created from this solver are all freed.\n\n- `void am_updatevars(am_Solver *solver);`\n\n  Refresh variables' value into it's constrainted value.\n  You could use `am_autoupdate()` to update all variables automatically when solver changes.\n\n- `void am_autoupdate(am_Solver *solver, int auto_update);`\n\n  Set the auto update flags.\n  If setted, all variables will be updated to its latest values right after any changes done to the `solver`.\n\n- `int am_dump(am_Solver *solver, am_Dumper *d);`\n\n  Dump whole solver state (without edits) into binary data.\n\n- `int am_load(am_Solver *solver, am_Loader *l);`\n\n  Loads binary data back into solver. Note that all previous states are\n  discarded, but previous vars & constraints are keeped. i.e. the\n  `am_resetsolver()` called before loaded.\n\n- `am_Id am_newvariable(am_Solver *solver, am_Num *pvalue);`\n\n  Create a new variable with a value `pvalue` binding to it. Returns `0` on\n  error.\n\n  Notice that the pvalue pointer can be retrived in `am_dump()` or\n  `am_varvalue()`, so you could put context data here:\n\n  ```c\n  typedef struct MyVarContext {\n      am_Num value;\n      const char *name;\n      /* etc.. */\n  } MyVarContext;\n  MyVarContext *ctx = new_context();\n  am_Id var = am_newvariable(solver, &ctx->value);\n  /* retrive the context: */\n  MyVarContext *ctx = (MyVarContext*)am_varvalue(var, NULL);\n  ```\n\n- `void am_delvariable(am_Solver *solver, am_Id var);`\n\n  Remove this variable, after that, it cannot be added into further constraints.\n\n- `void am_refcount(am_Solver *solver, am_Id var);`\n\n  Retrieve the refcount of the variable.\n\n- `void am_varvalue(am_Solver *solver, am_Id var, am_Num *newvalue);`\n\n  Retrieve or reset the value binding of the variable. if `newvalue` is `NULL`,\n  Only retrieve the old value, otherwise the `newvalue` will be setted into\n  `var` and original value returned.\n\n- `int am_add(am_Constraint *cons);`\n\n  Add constraint into the solver it's created from.\n\n- `int am_hasconstraint(am_Constraint *cons);`\n\n  Check whether a constraint has been added into the solver or not.\n\n- `void am_remove(am_Constraint *cons);`\n\n  Remove added constraint.\n\n- `int am_clearedits(am_Solver *solver);`\n\n  Remove all variable suggests from `solver`.\n\n- `int am_hasedit(am_Solver *solver, am_Id var);`\n\n  Check whether a variable has suggested value in the `solver`.\n\n- `int am_addedit(am_Solver *solver, am_Id var, am_Num strength);`\n\n  Prepare to change the value of variables or the `strength` value if the variable is in edit now.\n\n- `void am_suggest(am_Solver *solver, am_Id var, am_Num value);`\n\n  Actually change the value of the variable `var`.\n\n  After changed, other variable may changed due to the constraints in solver.\n  If you do not want change the strength of suggest (default is `AM_MEDIUM`), you may call `am_suggest()` directly without `am_addedit()`.\n\n- `void am_deledit(am_Solver *solver, am_Id var);`\n\n  Cancel the modify of variable.\n  The value binding by `var` will restore to the referred value according the solver.\n\n- `am_Constraint *am_newconstraint(am_Solver *solver, am_Num strength);`\n\n  Create a new constraints. Notice that where are one pointer's space can be\n  used to store context data in the head of `am_Constraint`, example:\n\n  ```c\n  am_Constraint *cons = am_newconstraint(solver, AM_REQUIRED);\n  MyContext *ctx = new_my_context();\n  ctx->mydata = 1;\n  *(MyContext**)cons = ctx;\n  ```\n\n- `am_Constraint *am_cloneconstraint(am_Constraint *other, am_Num strength);`\n\n  Clone a new constraint from the existing one with new `strength`.\n\n- `void am_resetconstraint(am_Constraint *cons);`\n\n  Remove all terms and variables from the constraint, restore it to blank state.\n\n- `void am_delconstraint(am_Constraint *cons);`\n\n  Free the constraint. if it's added into solver, remove it first.\n\n- `int am_addterm(am_Constraint *cons, am_Id var, am_Num multiplier);`\n\n  Add a term into constraint.\n\n  Example: a constraint like `2*m == x+y`, has terms `2*m`, `1*x` and `1*y`.\n  So to makeup this constraint, you could:\n\n  ```c\n  // 2*m == x + y\n  am_addterm(c, m, 2.0); // 2*m\n  am_setrelation(c, AM_EQUAL); // =\n  am_addterm(c, x, 1.0); // x\n  am_addterm(c, y, 1.0); // y\n  ```\n\n- `int am_setrelation(am_Constraint *cons, int relation);`\n\n  Set the relations of constraint, could be one of these:\n\n  - `AM_EQUAL`\n  - `AM_GREATEQUAL`\n  - `AM_LESSEQUAL`\n\n  The terms added before `am_setrelation()` become the left hand terms of constraints, and the terms adds after call will become the right hand terms of constraints.\n\n- `int am_addconstant(am_Constraint *cons, am_Num constant);`\n\n  Add a constant without variable into constraint as a term.\n\n- `int am_setstrength(am_Constraint *cons, am_Num strength);`\n\n  Set the strength of a constraint.\n\n- `int am_mergeconstraint(am_Constraint *cons, const am_Constraint *other, am_Num multiplier);`\n\n  Merge other constraints into `cons`, with a multiplier multiples with `other`.\n\n"
  },
  {
    "path": "amoeba.h",
    "content": "#ifndef amoeba_h\n#define amoeba_h\n\n#ifndef AM_NS_BEGIN\n# ifdef __cplusplus\n#   define AM_NS_BEGIN extern \"C\" {\n#   define AM_NS_END   }\n# else\n#   define AM_NS_BEGIN\n#   define AM_NS_END\n# endif\n#endif /* AM_NS_BEGIN */\n\n#ifndef AM_STATIC\n# ifdef __GNUC__\n#   define AM_STATIC static __attribute((unused))\n# else\n#   define AM_STATIC static\n# endif\n#endif\n\n#ifdef AM_STATIC_API\n# ifndef AM_IMPLEMENTATION\n#  define AM_IMPLEMENTATION\n# endif\n# define AM_API AM_STATIC\n#endif\n\n#if !defined(AM_API) && defined(_WIN32)\n# ifdef AM_IMPLEMENTATION\n#  define AM_API __declspec(dllexport)\n# else\n#  define AM_API __declspec(dllimport)\n# endif\n#endif\n\n#ifndef AM_API\n# define AM_API extern\n#endif\n\n#define AM_OK           (0)\n#define AM_FAILED       (-1)\n#define AM_UNSATISFIED  (-2)\n#define AM_UNBOUND      (-3)\n\n#define AM_LESSEQUAL    (1)\n#define AM_EQUAL        (2)\n#define AM_GREATEQUAL   (3)\n\n#define AM_REQUIRED     ((am_Num)1 / 0.0) /* INFINITY */\n#define AM_STRONG       ((am_Num)1000000)\n#define AM_MEDIUM       ((am_Num)1000)\n#define AM_WEAK         ((am_Num)1)\n\n#include <stddef.h>\n\nAM_NS_BEGIN\n\n\n#ifdef AM_NUM\ntypedef AM_NUM am_Num;\n#elif defined(AM_USE_DOUBLE)\ntypedef double am_Num;\n#else\ntypedef float  am_Num;\n#endif\n\ntypedef struct am_Solver     am_Solver;\ntypedef struct am_Constraint am_Constraint;\n\ntypedef unsigned am_Id;\n\ntypedef enum am_AllocType {\n    am_AllocSolver = 1,\n    am_AllocConstraint,\n    am_AllocSuggest,\n    am_AllocHash,\n    am_AllocDump\n} am_AllocType;\n\ntypedef void *am_Allocf (void **pud, void *ptr,\n        size_t nsize, size_t osize, am_AllocType ty);\n\nAM_API am_Solver *am_newsolver   (am_Allocf *allocf, void *ud);\nAM_API void       am_resetsolver (am_Solver *S);\nAM_API void       am_delsolver   (am_Solver *S);\n\nAM_API void am_updatevars (am_Solver *S);\nAM_API void am_autoupdate (am_Solver *S, int auto_update);\n\nAM_API int  am_add    (am_Constraint *cons);\nAM_API void am_remove (am_Constraint *cons);\n\nAM_API void am_clearedits (am_Solver *S);\nAM_API int  am_hasedit (am_Solver *S, am_Id var);\nAM_API int  am_addedit (am_Solver *S, am_Id var, am_Num strength);\nAM_API void am_suggest (am_Solver *S, am_Id var, am_Num value);\nAM_API void am_deledit (am_Solver *S, am_Id var);\n\nAM_API am_Id am_newvariable (am_Solver *S, am_Num *pvalue);\nAM_API void  am_delvariable (am_Solver *S, am_Id var);\n\nAM_API am_Num *am_varvalue (am_Solver *S, am_Id var, am_Num *newvalue);\n\nAM_API int am_refcount      (am_Solver *S, am_Id var);\nAM_API int am_hasconstraint (am_Constraint *cons);\n\nAM_API am_Constraint *am_newconstraint   (am_Solver *S, am_Num strength);\nAM_API am_Constraint *am_cloneconstraint (am_Constraint *other, am_Num strength);\n\nAM_API void am_resetconstraint (am_Constraint *cons);\nAM_API void am_delconstraint   (am_Constraint *cons);\n\nAM_API int am_addterm     (am_Constraint *cons, am_Id var, am_Num multiplier);\nAM_API int am_setrelation (am_Constraint *cons, int relation);\nAM_API int am_addconstant (am_Constraint *cons, am_Num constant);\nAM_API int am_setstrength (am_Constraint *cons, am_Num strength);\n\nAM_API int am_mergeconstraint (am_Constraint *cons, const am_Constraint *other, am_Num multiplier);\n\n/* dump & load */\n\ntypedef struct am_Dumper am_Dumper;\ntypedef struct am_Loader am_Loader;\n\nAM_API int am_dump (am_Solver *S, am_Dumper *dumper);\nAM_API int am_load (am_Solver *S, am_Loader *loader);\n\nstruct am_Dumper {\n    const char *(*var_name)  (am_Dumper *D, unsigned idx, am_Id var, am_Num *value);\n    const char *(*cons_name) (am_Dumper *D, unsigned idx, am_Constraint *cons);\n\n    int (*writer) (am_Dumper *D, const void *buf, size_t len);\n};\n\nstruct am_Loader {\n    am_Num *(*load_var)  (am_Loader *L, const char *name, unsigned idx, am_Id var);\n    void    (*load_cons) (am_Loader *L, const char *name, unsigned idx, am_Constraint *cons);\n\n    const char *(*reader) (am_Loader *L, size_t *plen);\n};\n\n\nAM_NS_END\n\n#endif /* amoeba_h */\n\n#if defined(AM_IMPLEMENTATION) && !defined(am_implemented)\n#define am_implemented\n\n#include <assert.h>\n#include <float.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#define AM_EXTERNAL     (0)\n#define AM_SLACK        (1)\n#define AM_ERROR        (2)\n#define AM_DUMMY        (3)\n\n#define am_isexternal(key)   ((key).type == AM_EXTERNAL)\n#define am_isslack(key)      ((key).type == AM_SLACK)\n#define am_iserror(key)      ((key).type == AM_ERROR)\n#define am_isdummy(key)      ((key).type == AM_DUMMY)\n#define am_ispivotable(key)  (am_isslack(key) || am_iserror(key))\n\n#define am_alloc(T,S,ty) ((T*)(S)->allocf((void**)(S),NULL,sizeof(T),0,(ty)))\n#define am_free(S,p,ty)  ((S)->allocf((void**)(S),(p),0,sizeof(*(p)),(ty)))\n\n#define AM_MIN_HASHSIZE  8\n#define AM_POOL_SIZE     4096\n#define AM_UNSIGNED_BITS (sizeof(unsigned)*CHAR_BIT)\n\n#ifdef AM_USE_DOUBLE\n# define AM_NUM_MAX DBL_MAX\n# define AM_NUM_EPS 1e-6\n#else\n# define AM_NUM_MAX FLT_MAX\n# define AM_NUM_EPS 1e-4\n#endif\n\n#ifndef AM_DEBUG\n# ifdef __GNUC__\n#   pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n# endif\n# define AM_DEBUG(...) ((void)0)\n#endif /* AM_DEBUG */\n\n#define amC(cond) do { if (!(cond)) return \\\n    AM_DEBUG(__FILE__ \":%d: \" #cond \"\\n\", __LINE__), AM_FAILED; } while (0)\n#define amE(cond) amC((cond) == AM_OK)\n\nAM_NS_BEGIN\n\n\ntypedef unsigned am_Size;\n\ntypedef struct am_Symbol {\n    unsigned id   : AM_UNSIGNED_BITS - 2;\n    unsigned type : 2;\n} am_Symbol;\n\ntypedef struct am_MemPool {\n    size_t size;\n    void  *freed;\n    void  *pages;\n} am_MemPool;\n\ntypedef struct am_Key {\n    int       next;\n    am_Symbol sym;\n} am_Key;\n\ntypedef struct am_Arena {\n    size_t size;\n    void  *buf;\n} am_Arena;\n\ntypedef struct am_Table {\n    am_Size size;\n    am_Size count;\n    am_Size value_size : AM_UNSIGNED_BITS - 1;\n    am_Size arena      : 1;\n    am_Size last_free;\n    am_Key *keys;\n    void   *vals;\n} am_Table;\n\ntypedef struct am_Iterator {\n    const am_Table *t;\n    am_Symbol key;\n    am_Size   offset;\n} am_Iterator;\n\ntypedef struct am_Row {\n    am_Symbol infeasible_next;\n    am_Num    constant;\n    am_Table  terms;\n} am_Row;\n\ntypedef struct am_Var {\n    am_Symbol next;\n    unsigned  refcount;\n    am_Num   *pvalue;\n} am_Var;\n\nstruct am_Constraint {\n    void      *ud;\n    am_Row     expression;\n    am_Symbol  sym; /* AM_EXTERNAL for pool cons, AM_DUMMY for suggest */\n    am_Symbol  marker;\n    am_Symbol  other;\n    unsigned   marker_id;\n    unsigned   other_id : AM_UNSIGNED_BITS - 2;\n    unsigned   relation : 2;\n    am_Num     strength;\n    am_Solver *S;\n};\n\ntypedef struct am_Suggest {\n    am_Size       age;\n    am_Num        edit_value;\n    am_Constraint constraint;\n    am_Table      dirtyset;\n} am_Suggest;\n\nstruct am_Solver {\n    void      *ud; /* must be the first for `am_default_allocf` */\n    am_Allocf *allocf;\n    am_Row     objective;\n    am_Table   vars;            /* symbol -> am_Var */\n    am_Table   constraints;     /* symbol -> am_Constraint* */\n    am_Table   suggests;        /* symbol -> am_Suggest* */\n    am_Table   rows;            /* symbol -> am_Row */\n    am_Arena   arena;\n    am_Size    auto_update;\n    am_Size    age; /* counts of rows changed */\n    am_Size    current_id;\n    am_Size    current_cons;\n    am_Symbol  infeasible_rows;\n    am_Symbol  dirty_vars;\n    am_MemPool conspool;\n};\n\n/* utils */\n\nstatic int am_approx(am_Num a, am_Num b)\n{ return a > b ? a - b < AM_NUM_EPS : b - a < AM_NUM_EPS; }\n\nstatic int am_nearzero(am_Num a)\n{ return am_approx(a, 0.0f); }\n\nstatic am_Symbol am_null(void)\n{ am_Symbol null = { 0, 0 }; return null; }\n\nstatic void am_poolfree(am_MemPool *pool, void *obj)\n{ *(void**)obj = pool->freed, pool->freed = obj; }\n\nstatic void am_freearena(am_Solver *S, am_Arena *a) {\n    if (!a->buf) return;\n    S->allocf(&S->ud, a->buf, 0, a->size, am_AllocDump);\n    a->buf = NULL, a->size = 0;\n}\n\nstatic void am_allocarena(am_Solver *S, am_Arena *a, size_t size) {\n    assert(a->buf == NULL);\n    a->buf = S->allocf(&S->ud, NULL, size, 0, am_AllocDump);\n    if (a->buf) a->size = size;\n}\n\nstatic void am_initpool(am_MemPool *pool, size_t size) {\n    pool->size  = size;\n    pool->freed = pool->pages = NULL;\n    assert(size > sizeof(void*) && size < AM_POOL_SIZE/4);\n}\n\nstatic void am_freepool(am_MemPool *pool) {\n    const size_t offset = AM_POOL_SIZE - sizeof(void*);\n    while (pool->pages != NULL) {\n        void *next = *(void**)((char*)pool->pages + offset);\n        free(pool->pages);\n        pool->pages = next;\n    }\n}\n\nstatic void *am_poolalloc(am_MemPool *pool) {\n    void *obj = pool->freed;\n    if (obj == NULL) {\n        const size_t offset = AM_POOL_SIZE - sizeof(void*);\n        void *end, *ptr = malloc(AM_POOL_SIZE);\n        if (ptr == NULL) abort();\n        *(void**)((char*)ptr + offset) = pool->pages;\n        pool->pages = ptr;\n        end = (char*)ptr + (offset/pool->size-1)*pool->size;\n        while (end != ptr) {\n            *(void**)end = pool->freed;\n            pool->freed = (void**)end;\n            end = (char*)end - pool->size;\n        }\n        return end;\n    }\n    pool->freed = *(void**)obj;\n    return obj;\n}\n\nstatic void *am_default_allocf(void **pud, void *ptr, size_t nsize, size_t osize, am_AllocType ty) {\n    void *newptr;\n    (void)osize;\n    if (ty == am_AllocConstraint) {\n        am_Solver *S = (am_Solver*)pud;\n        if (nsize) return am_poolalloc(&S->conspool);\n        return am_poolfree(&S->conspool, ptr), (void*)NULL;\n    }\n    if (nsize == 0) return free(ptr), (void*)NULL;\n    if ((newptr = realloc(ptr, nsize)) == NULL) abort();\n    return newptr;\n}\n\nstatic am_Symbol am_newsymbol(am_Solver *S, int type) {\n    am_Symbol sym;\n    unsigned id = ++S->current_id;\n    if (id > 0x3FFFFFFF) abort(); /* id runs out */\n    assert(type >= AM_EXTERNAL && type <= AM_DUMMY);\n    sym.id   = id;\n    sym.type = type;\n    return sym;\n}\n\n/* hash table */\n\n#define amH_val(t,i)  ((char*)(t)->vals+(i)*(t)->value_size)\n#define am_val(T,it)  ((T*)amH_val((it).t,(it).offset-1))\n\nstatic am_Iterator am_itertable(const am_Table *t)\n{ am_Iterator it; it.t = t, it.key = am_null(), it.offset = 0; return it; }\n\nstatic void am_inittable(am_Table *t, size_t value_size)\n{ memset(t, 0, sizeof(*t)), t->value_size = (am_Size)value_size; }\n\nstatic void am_resettable(am_Table *t)\n{ t->last_free = t->count = 0; memset(t->keys, 0, t->size * sizeof(am_Key)); }\n\nstatic am_Size am_calcsize(am_Size request)\n{ am_Size s = AM_MIN_HASHSIZE; while (s < request) s <<= 1; return s; }\n\nstatic int am_nextentry(am_Iterator *it) {\n    am_Size cur = it->offset, size = it->t->size;\n    am_Symbol key;\n    while (cur < size && (key = it->t->keys[cur].sym).id == 0) cur++;\n    if (cur == size) return it->key = am_null(), it->offset = 0;\n    return it->key = key, it->offset = cur + 1;\n}\n\nstatic void am_freetable(const am_Solver *S, am_Table *t) {\n    size_t hsize = t->size * (sizeof(am_Key) + t->value_size);\n    if (hsize == 0) return;\n    if (!t->arena) S->allocf((void**)S, t->keys, 0, hsize, am_AllocHash);\n    am_inittable(t, t->value_size);\n}\n\nstatic const void *am_gettable(const am_Table *t, unsigned key) {\n    am_Size cur;\n    if (t->size == 0) return NULL;\n    cur = key & (t->size - 1);\n    for (; t->keys[cur].sym.id != key; cur += t->keys[cur].next)\n        if (t->keys[cur].next == 0) return NULL;\n    return amH_val(t, cur);\n}\n\nstatic int amH_alloc(am_Table *t, const am_Solver *S, size_t request, size_t vsize, void **arena) {\n    size_t size, hsize;\n    am_Key *keys;\n    amC(request <= (~(am_Size)0 >> 1));\n    size = am_calcsize((am_Size)request);\n    hsize = size * (sizeof(am_Key) + vsize);\n    if (arena) keys = (am_Key*)*arena, *arena = (char*)*arena + hsize;\n    else keys = (am_Key*)S->allocf((void**)S, NULL, hsize, 0, am_AllocHash);\n    amC(keys != NULL);\n    t->size = (am_Size)size;\n    t->value_size = vsize;\n    t->arena = (arena != NULL);\n    t->keys = keys;\n    t->vals = keys + t->size;\n    return am_resettable(t), AM_OK;\n}\n\nstatic void *amH_rawset(am_Table *t, am_Symbol key) {\n    int mp = key.id & (t->size - 1);\n    am_Key *ks = t->keys;\n    if (ks[mp].sym.id) {\n        int f = t->last_free, othern;\n        while (f < (int)t->size && (ks[f].sym.id || ks[f].next))\n            f += 1;\n        if (f == (int)t->size) return NULL;\n        t->last_free = f + 1;\n        if ((othern = ks[mp].sym.id & (t->size - 1)) == mp) {\n            if (ks[mp].next) ks[f].next = (mp + ks[mp].next) - f;\n            ks[mp].next = (f - mp), mp = f;\n        } else {\n            assert(ks[othern].next != 0);\n            while (othern + ks[othern].next != mp)\n                othern += ks[othern].next;\n            ks[othern].next = (f - othern);\n            ks[f] = ks[mp], memcpy(amH_val(t,f), amH_val(t,mp), t->value_size);\n            if (ks[mp].next) ks[f].next += (mp - f), ks[mp].next = 0;\n        }\n    }\n    return ks[mp].sym = key, amH_val(t, mp);\n}\n\nstatic int amH_resize(const am_Solver *S, am_Table *t, size_t request, void **arena) {\n    am_Table nt;\n    am_Size i;\n    amE(amH_alloc(&nt, S, request, t->value_size, arena));\n    for (i = 0; i < t->size; ++i) {\n        void *value;\n        if (!t->keys[i].sym.id) continue;\n        value = amH_rawset(&nt, t->keys[i].sym);\n        assert(value != NULL), memcpy(value, amH_val(t, i), t->value_size);\n    }\n    nt.count = t->count;\n    if (!t->arena) am_freetable(S, t);\n    return (*t = nt), AM_OK;\n}\n\nstatic int am_growtable(const am_Solver *S, am_Table *t, size_t grow, void **a)\n{ return (grow += t->count)*2 < t->size ? AM_OK : amH_resize(S, t, grow, a); }\n\nstatic void *am_settable(const am_Solver *S, am_Table *t, am_Symbol key) {\n    void *value;\n    assert(key.id != 0 && am_gettable(t, key.id) == NULL);\n    if (!t->size && amH_resize(S, t, 0, NULL) != AM_OK) return NULL;\n    if ((value = amH_rawset(t, key)) == NULL) {\n        if (amH_resize(S, t, t->count*2, NULL) != AM_OK) return NULL;\n        value = amH_rawset(t, key); /* retry and must success */\n    }\n    return t->count += 1, assert(value != NULL), value;\n}\n\nstatic void am_deltable(am_Table *t, const void *value) {\n    size_t offset = ((char*)value - (char*)t->vals)/t->value_size;\n    t->count -= 1, t->keys[offset].sym = am_null();\n}\n\n/* expression (row) */\n\nstatic void am_freerow(am_Solver *S, am_Row *row)\n{ am_freetable(S, &row->terms); }\n\nstatic void am_resetrow(am_Row *row)\n{ row->constant = 0.0f; am_resettable(&row->terms); }\n\nstatic void am_initrow(am_Row *row) {\n    row->infeasible_next = am_null();\n    row->constant = 0.0f;\n    am_inittable(&row->terms, sizeof(am_Num));\n}\n\nstatic void am_multiply(am_Row *row, am_Num multiplier) {\n    am_Iterator it = am_itertable(&row->terms);\n    row->constant *= multiplier;\n    while (am_nextentry(&it))\n        *am_val(am_Num,it) *= multiplier;\n}\n\nstatic void am_addvar(am_Solver *S, am_Row *row, am_Symbol sym, am_Num value) {\n    am_Num *term;\n    if (sym.id == 0 || am_nearzero(value)) return;\n    term = (am_Num*)am_gettable(&row->terms, sym.id);\n    if (term == NULL) {\n        term = (am_Num*)am_settable(S, &row->terms, sym);\n        assert(term != NULL);\n        *term = value;\n    } else if (am_nearzero(*term += value))\n        am_deltable(&row->terms, term);\n}\n\nstatic void am_addrow(am_Solver *S, am_Row *row, const am_Row *other, am_Num multiplier) {\n    am_Iterator it = am_itertable(&other->terms);\n    row->constant += other->constant*multiplier;\n    while (am_nextentry(&it))\n        am_addvar(S, row, it.key, *am_val(am_Num,it)*multiplier);\n}\n\n/* variables & constraints */\n\nAM_API int am_hasconstraint(am_Constraint *cons)\n{ return cons != NULL && cons->marker.id != 0; }\n\nAM_API void am_autoupdate(am_Solver *S, int auto_update)\n{ S->auto_update = auto_update; }\n\nstatic am_Var *am_id2var(am_Solver *S, am_Id var)\n{ return S ? (am_Var*)am_gettable(&S->vars, var) : NULL; }\n\nAM_API am_Id am_newvariable(am_Solver *S, am_Num *pvalue) {\n    am_Symbol sym;\n    am_Var *ve;\n    if (S == NULL || pvalue == NULL) return 0;\n    sym = am_newsymbol(S, AM_EXTERNAL);\n    ve = (am_Var*)am_settable(S, &S->vars, sym);\n    if (ve == NULL) return 0;\n    memset(ve, 0, sizeof(*ve));\n    ve->pvalue   = pvalue;\n    ve->refcount = 1;\n    return sym.id;\n}\n\nAM_API void am_delvariable(am_Solver *S, am_Id var) {\n    am_Var *ve = am_id2var(S, var);\n    if (ve && --ve->refcount == 0) {\n        assert(!am_isdummy(ve->next));\n        am_deledit(S, var);\n        am_deltable(&S->vars, ve);\n    }\n}\n\nAM_API int am_refcount(am_Solver *S, am_Id var) {\n    am_Var *ve = am_id2var(S, var);\n    return ve ? ve->refcount : 0;\n}\n\nAM_API am_Num *am_varvalue(am_Solver *S, am_Id var, am_Num *newvalue) {\n    am_Var *ve = am_id2var(S, var);\n    am_Num *pvalue = ve ? ve->pvalue : NULL;\n    if (ve && newvalue) ve->pvalue = newvalue;\n    return pvalue;\n}\n\nAM_API void am_updatevars(am_Solver *S) {\n    while (S->dirty_vars.id != 0) {\n        am_Symbol var = S->dirty_vars;\n        am_Var *ve = (am_Var*)am_gettable(&S->vars, var.id);\n        assert(ve != NULL);\n        S->dirty_vars = ve->next;\n        ve->next = am_null();\n        if (ve->refcount == 1) {\n            am_deledit(S, var.id);\n            am_deltable(&S->vars, ve);\n        } else {\n            am_Row *row = (am_Row*)am_gettable(&S->rows, var.id);\n            *ve->pvalue = row ? row->constant : 0.0f;\n            ve->refcount -= 1;\n        }\n    }\n}\n\nstatic int am_initconstraint(am_Solver *S, am_Symbol sym, am_Num strength, am_Constraint *cons) {\n    am_Constraint **ce = (am_Constraint**)am_settable(S, &S->constraints, sym);\n    amC(ce != NULL);\n    *ce = cons;\n    memset(cons, 0, sizeof(*cons));\n    cons->S        = S;\n    cons->sym      = sym;\n    cons->strength = am_nearzero(strength) ? AM_REQUIRED : strength;\n    am_initrow(&(*ce)->expression);\n    return AM_OK;\n}\n\nAM_API am_Constraint *am_newconstraint(am_Solver *S, am_Num strength) {\n    am_Symbol sym = {0, AM_EXTERNAL}; /* AM_EXTERNAL for pool cons */\n    am_Constraint *cons;\n    int ret;\n    if (S == NULL || strength < 0.f) return NULL;\n    cons = am_alloc(am_Constraint, S, am_AllocConstraint);\n    if (cons == NULL) return NULL;\n    sym.id = ++S->current_cons;\n    ret = am_initconstraint(S, sym, strength, cons);\n    if (ret != AM_OK) am_free(S, cons, am_AllocConstraint), cons = NULL;\n    return cons;\n}\n\nAM_API void am_delconstraint(am_Constraint *cons) {\n    am_Solver *S = cons ? cons->S : NULL;\n    am_Iterator it;\n    am_Constraint **ce;\n    if (S == NULL) return;\n    am_remove(cons);\n    ce = (am_Constraint**)am_gettable(&S->constraints, cons->sym.id);\n    assert(ce != NULL && *ce == cons);\n    am_deltable(&S->constraints, ce);\n    it = am_itertable(&cons->expression.terms);\n    while (am_nextentry(&it))\n        am_delvariable(cons->S, it.key.id);\n    am_freerow(S, &cons->expression);\n    if (am_isexternal(cons->sym)) am_free(S, cons, am_AllocConstraint);\n}\n\nAM_API am_Constraint *am_cloneconstraint(am_Constraint *other, am_Num strength) {\n    am_Constraint *cons;\n    if (other == NULL) return NULL;\n    cons = am_newconstraint(other->S,\n            am_nearzero(strength) ? other->strength : strength);\n    am_mergeconstraint(cons, other, 1.0f);\n    cons->relation = other->relation;\n    return cons;\n}\n\nAM_API int am_mergeconstraint(am_Constraint *cons, const am_Constraint *other, am_Num multiplier) {\n    am_Iterator it;\n    amC(cons && other && cons->marker.id == 0 && cons->S == other->S);\n    if (cons->relation == AM_GREATEQUAL) multiplier = -multiplier;\n    cons->expression.constant += other->expression.constant*multiplier;\n    it = am_itertable(&other->expression.terms);\n    while (am_nextentry(&it)) {\n        am_Var *ve = (am_Var*)am_gettable(&cons->S->vars, it.key.id);\n        assert(ve != NULL);\n        ve->refcount += 1;\n        am_addvar(cons->S, &cons->expression, it.key,\n                *am_val(am_Num,it)*multiplier);\n    }\n    return AM_OK;\n}\n\nAM_API void am_resetconstraint(am_Constraint *cons) {\n    am_Iterator it;\n    if (cons == NULL) return;\n    am_remove(cons);\n    cons->relation = 0;\n    it = am_itertable(&cons->expression.terms);\n    while (am_nextentry(&it))\n        am_delvariable(cons->S, it.key.id);\n    am_resetrow(&cons->expression);\n}\n\nAM_API int am_addterm(am_Constraint *cons, am_Id var, am_Num multiplier) {\n    am_Symbol sym = {0, AM_EXTERNAL};\n    am_Var *ve;\n    amC(cons && var && cons->marker.id == 0);\n    if (cons->relation == AM_GREATEQUAL) multiplier = -multiplier;\n    amC(ve = (am_Var*)am_gettable(&cons->S->vars, var));\n    am_addvar(cons->S, &cons->expression, (sym.id=var, sym), multiplier);\n    return ve->refcount += 1, AM_OK;\n}\n\nAM_API int am_addconstant(am_Constraint *cons, am_Num constant) {\n    amC(cons && cons->marker.id == 0);\n    cons->expression.constant +=\n        cons->relation == AM_GREATEQUAL ? -constant : constant;\n    return AM_OK;\n}\n\nAM_API int am_setrelation(am_Constraint *cons, int relation) {\n    assert(relation >= AM_LESSEQUAL && relation <= AM_GREATEQUAL);\n    amC(cons && cons->marker.id == 0 && cons->relation == 0);\n    if (relation != AM_GREATEQUAL) am_multiply(&cons->expression, -1.0f);\n    cons->relation = relation;\n    return AM_OK;\n}\n\nAM_API int am_hasedit(am_Solver *S, am_Id var) {\n    if (S == NULL) return 0;\n    return am_gettable(&S->suggests, var) != NULL;\n}\n\nstatic am_Suggest *am_newedit(am_Solver *S, am_Id var, am_Num strength) {\n    am_Symbol sym = {0, AM_EXTERNAL};\n    am_Suggest **s;\n    am_Var *ve;\n    int ret;\n    if (S == NULL || var == 0 || strength < 0.f) return NULL;\n    if ((ve = (am_Var*)am_gettable(&S->vars, var)) == NULL) return NULL;\n    s = (am_Suggest**)am_settable(S, &S->suggests, (sym.id=var, sym));\n    if (s == NULL) return NULL;\n    *s = am_alloc(am_Suggest, S, am_AllocSuggest);\n    if (*s == NULL) return am_deltable(&S->suggests, s), (am_Suggest*)NULL;\n    memset(*s, 0, sizeof(**s));\n    sym.id = ++S->current_cons, sym.type = AM_DUMMY; /* local cons */\n    am_initconstraint(S, sym, strength, &(*s)->constraint);\n    am_setrelation(&(*s)->constraint, AM_EQUAL);\n    am_addterm(&(*s)->constraint, var, 1.0f); /* var must have positive signture */\n    am_addconstant(&(*s)->constraint, -*ve->pvalue);\n    ret = am_add(&(*s)->constraint);\n    assert(ret == AM_OK), (void)ret;\n    (*s)->edit_value = *ve->pvalue;\n    return *s;\n}\n\nAM_API int am_addedit(am_Solver *S, am_Id var, am_Num strength) {\n    am_Suggest **s;\n    amC(S && var);\n    if (strength >= AM_STRONG) strength = AM_STRONG;\n    s = (am_Suggest**)am_gettable(&S->suggests, var);\n    if (s) return assert(*s), am_setstrength(&(*s)->constraint, strength);\n    amC(am_newedit(S, var, strength));\n    return AM_OK;\n}\n\nAM_API void am_deledit(am_Solver *S, am_Id var) {\n    am_Suggest **ps, *s;\n    if (S == NULL || var == 0) return;\n    ps = (am_Suggest**)am_gettable(&S->suggests, var);\n    if (ps == NULL) return;\n    s = *ps, am_deltable(&S->suggests, ps);\n    am_freetable(S, &s->dirtyset);\n    am_delconstraint(&(s->constraint));\n    am_free(S, s, am_AllocSuggest);\n}\n\nAM_API void am_clearedits(am_Solver *S) {\n    am_Iterator it;\n    if (S == NULL) return;\n    it = am_itertable(&S->suggests);\n    if (!S->auto_update) am_updatevars(S);\n    while (am_nextentry(&it)) {\n        am_Suggest *s = *am_val(am_Suggest*,it);\n        am_freetable(S, &s->dirtyset);\n        am_delconstraint(&s->constraint);\n        am_free(S, s, am_AllocSuggest);\n    }\n    am_resettable(&S->suggests);\n}\n\n/* Cassowary algorithm */\n\nstatic int am_takerow(am_Solver *S, am_Symbol sym, am_Row *dst) {\n    am_Row *row = (am_Row*)am_gettable(&S->rows, sym.id);\n    if (row == NULL) return AM_FAILED;\n    am_deltable(&S->rows, row);\n    dst->constant = row->constant;\n    dst->terms    = row->terms;\n    return AM_OK;\n}\n\nstatic void am_solvefor(am_Solver *S, am_Row *row, am_Symbol enter, am_Symbol leave) {\n    am_Num *term = (am_Num*)am_gettable(&row->terms, enter.id);\n    am_Num reciprocal = 1.0f / *term;\n    assert(enter.id != leave.id && !am_nearzero(*term));\n    am_deltable(&row->terms, term);\n    if (!am_approx(reciprocal, -1.0f)) am_multiply(row, -reciprocal);\n    if (leave.id != 0) am_addvar(S, row, leave, reciprocal);\n}\n\nstatic void am_eliminate(am_Solver *S, am_Row *dst, am_Symbol out, const am_Row *row) {\n    am_Num *term = (am_Num*)am_gettable(&dst->terms, out.id);\n    if (!term) return;\n    am_deltable(&dst->terms, term);\n    am_addrow(S, dst, row, *term);\n}\n\nstatic void am_markdirty(am_Solver *S, am_Symbol var) {\n    am_Var *ve = (am_Var*)am_gettable(&S->vars, var.id);\n    assert(ve != NULL);\n    if (am_isdummy(ve->next)) return;\n    ve->next.id = S->dirty_vars.id;\n    ve->next.type = AM_DUMMY;\n    S->dirty_vars = var;\n    ve->refcount += 1;\n}\n\nstatic void am_infeasible(am_Solver *S, am_Symbol sym, am_Row *row) {\n    if (row->constant < 0.0f && !am_isdummy(row->infeasible_next)) {\n        row->infeasible_next.id = S->infeasible_rows.id;\n        row->infeasible_next.type = AM_DUMMY;\n        S->infeasible_rows = sym;\n    }\n}\n\nstatic void am_substitute_rows(am_Solver *S, am_Symbol var, am_Row *expr) {\n    am_Iterator it = am_itertable(&S->rows);\n    while (am_nextentry(&it)) {\n        am_Row *row = am_val(am_Row,it);\n        am_eliminate(S, row, var, expr);\n        if (am_isexternal(it.key))\n            am_markdirty(S, it.key);\n        else\n            am_infeasible(S, it.key, row);\n    }\n    am_eliminate(S, &S->objective, var, expr);\n}\n\nstatic int am_putrow(am_Solver *S, am_Symbol sym, const am_Row *src) {\n    am_Row *row;\n    assert(am_gettable(&S->rows, sym.id) == NULL);\n    row = (am_Row*)am_settable(S, &S->rows, sym);\n    assert(row != NULL);\n    row->infeasible_next = am_null();\n    row->constant = src->constant;\n    row->terms    = src->terms;\n    return AM_OK;\n}\n\nstatic void am_optimize(am_Solver *S, am_Row *objective) {\n    for (;;) {\n        am_Symbol enter = am_null(), leave = am_null();\n        am_Num r, min_ratio = AM_NUM_MAX, *term;\n        am_Iterator it = am_itertable(&objective->terms);\n        am_Row tmp;\n\n        assert(S->infeasible_rows.id == 0);\n        while (am_nextentry(&it))\n            if (!am_isdummy(it.key) && *am_val(am_Num,it) < 0.0f)\n            { enter = it.key; break; }\n        if (enter.id == 0) return;\n\n        it = am_itertable(&S->rows);\n        while (am_nextentry(&it)) {\n            am_Row *row = am_val(am_Row,it);\n            if (am_isexternal(it.key)) continue;\n            term = (am_Num*)am_gettable(&row->terms, enter.id);\n            if (term == NULL || *term > 0.0f) continue;\n            r = -row->constant / *term;\n            if (r < min_ratio || (am_approx(r, min_ratio)\n                        && it.key.id < leave.id))\n                min_ratio = r, leave = it.key;\n        }\n        assert(leave.id != 0);\n        am_takerow(S, leave, &tmp);\n        am_solvefor(S, &tmp, enter, leave);\n        am_substitute_rows(S, enter, &tmp);\n        if (objective != &S->objective)\n            am_eliminate(S, objective, enter, &tmp);\n        am_putrow(S, enter, &tmp);\n    }\n}\n\nstatic void am_mergerow(am_Solver *S, am_Row *row, am_Symbol var, am_Num multiplier) {\n    am_Row *oldrow = (am_Row*)am_gettable(&S->rows, var.id);\n    if (oldrow) am_addrow(S, row, oldrow, multiplier);\n    else        am_addvar(S, row, var, multiplier);\n}\n\nstatic am_Row am_makerow(am_Solver *S, am_Constraint *cons) {\n    am_Iterator it = am_itertable(&cons->expression.terms);\n    am_Row row;\n    am_initrow(&row);\n    row.constant = cons->expression.constant;\n    while (am_nextentry(&it)) {\n        am_markdirty(S, it.key);\n        am_mergerow(S, &row, it.key, *am_val(am_Num,it));\n    }\n    if (cons->relation != AM_EQUAL) {\n        cons->marker.id = cons->marker_id, cons->marker.type = AM_SLACK;\n        am_addvar(S, &row, cons->marker, -1.0f);\n        if (cons->strength < AM_REQUIRED) {\n            cons->other.id = cons->other_id, cons->other.type = AM_ERROR;\n            am_addvar(S, &row, cons->other, 1.0f);\n            am_addvar(S, &S->objective, cons->other, cons->strength);\n        }\n    } else if (cons->strength >= AM_REQUIRED) {\n        cons->marker.id = cons->marker_id, cons->marker.type = AM_DUMMY;\n        am_addvar(S, &row, cons->marker, 1.0f);\n    } else {\n        cons->marker.id = cons->marker_id, cons->marker.type = AM_ERROR;\n        cons->other.id = cons->other_id, cons->other.type = AM_ERROR;\n        am_addvar(S, &row, cons->marker, -1.0f);\n        am_addvar(S, &row, cons->other,   1.0f);\n        am_addvar(S, &S->objective, cons->marker, cons->strength);\n        am_addvar(S, &S->objective, cons->other,  cons->strength);\n    }\n    if (row.constant < 0.0f) am_multiply(&row, -1.0f);\n    return row;\n}\n\nstatic int am_add_with_artificial(am_Solver *S, am_Row *row, am_Constraint *cons) {\n    am_Symbol a = am_newsymbol(S, AM_SLACK);\n    am_Iterator it;\n    am_Row tmp;\n    am_Num *term;\n    int ret;\n    --S->current_id; /* artificial variable will be removed */\n    am_initrow(&tmp);\n    am_addrow(S, &tmp, row, 1.0f);\n    am_putrow(S, a, row);\n    am_optimize(S, &tmp);\n    ret = am_nearzero(tmp.constant) ? AM_OK : AM_UNBOUND;\n    am_freerow(S, &tmp);\n    if (am_takerow(S, a, &tmp) == AM_OK) {\n        am_Symbol enter = am_null();\n        if (tmp.terms.count == 0) return am_freerow(S, &tmp), ret;\n        it = am_itertable(&tmp.terms);\n        while (am_nextentry(&it))\n            if (am_ispivotable(it.key)) { enter = it.key; break; }\n        if (enter.id == 0) return am_freerow(S, &tmp), AM_UNBOUND;\n        am_solvefor(S, &tmp, enter, a);\n        am_substitute_rows(S, enter, &tmp);\n        am_putrow(S, enter, &tmp);\n    }\n    it = am_itertable(&S->rows);\n    while (am_nextentry(&it)) {\n        am_Row *row = am_val(am_Row,it);\n        am_Num *term = (am_Num*)am_gettable(&row->terms, a.id);\n        if (term) am_deltable(&row->terms, term);\n    }\n    term = (am_Num*)am_gettable(&S->objective.terms, a.id);\n    if (term) am_deltable(&S->objective.terms, term);\n    if (ret != AM_OK) am_remove(cons);\n    return ret;\n}\n\nstatic int am_try_addrow(am_Solver *S, am_Row *row, am_Constraint *cons) {\n    am_Symbol subject = am_null();\n    am_Iterator it = am_itertable(&row->terms);\n    while (am_nextentry(&it))\n        if (am_isexternal(it.key)) { subject = it.key; break; }\n    if (subject.id == 0 && am_ispivotable(cons->marker)) {\n        am_Num *term = (am_Num*)am_gettable(&row->terms, cons->marker.id);\n        if (*term < 0.0f) subject = cons->marker;\n    }\n    if (subject.id == 0 && am_ispivotable(cons->other)) {\n        am_Num *term = (am_Num*)am_gettable(&row->terms, cons->other.id);\n        if (*term < 0.0f) subject = cons->other;\n    }\n    if (subject.id == 0) {\n        it = am_itertable(&row->terms);\n        while (am_nextentry(&it))\n            if (!am_isdummy(it.key)) break;\n        if (it.offset == 0) {\n            if (am_nearzero(row->constant))\n                subject = cons->marker;\n            else {\n                am_freerow(S, row);\n                return AM_UNSATISFIED;\n            }\n        }\n    }\n    if (subject.id == 0) return am_add_with_artificial(S, row, cons);\n    am_solvefor(S, row, subject, am_null());\n    am_substitute_rows(S, subject, row);\n    am_putrow(S, subject, row);\n    return AM_OK;\n}\n\nstatic void am_remove_errors(am_Solver *S, am_Constraint *cons) {\n    if (am_iserror(cons->marker))\n        am_mergerow(S, &S->objective, cons->marker, -cons->strength);\n    if (am_iserror(cons->other))\n        am_mergerow(S, &S->objective, cons->other, -cons->strength);\n    if (S->objective.terms.count == 0)\n        S->objective.constant = 0.0f;\n    cons->marker = cons->other = am_null();\n}\n\nAM_API int am_add(am_Constraint *cons) {\n    am_Solver *S = cons ? cons->S : NULL;\n    am_Symbol sym;\n    am_Row row;\n    int ret;\n    amC(S && cons->marker.id == 0);\n    if (cons->marker_id == 0)\n        cons->marker_id = ((sym = am_newsymbol(S, 0)), sym.id);\n    if (cons->other_id == 0 && cons->strength < AM_REQUIRED)\n        cons->other_id = ((sym = am_newsymbol(S, 0)), sym.id);\n    row = am_makerow(S, cons);\n    if ((ret = am_try_addrow(S, &row, cons)) != AM_OK)\n        am_remove_errors(S, cons);\n    else {\n        am_optimize(S, &S->objective);\n        if (S->auto_update) am_updatevars(S);\n        S->age += 1;\n    }\n    assert(S->infeasible_rows.id == 0);\n    return ret;\n}\n\nstatic am_Symbol am_get_leaving_row(am_Solver *S, am_Symbol marker) {\n    am_Symbol first = am_null(), second = am_null(), third = am_null();\n    am_Num r1 = AM_NUM_MAX, r2 = AM_NUM_MAX;\n    am_Iterator it = am_itertable(&S->rows);\n    while (am_nextentry(&it)) {\n        am_Row *row = am_val(am_Row,it);\n        am_Num *term = (am_Num*)am_gettable(&row->terms, marker.id);\n        if (term == NULL) continue;\n        if (am_isexternal(it.key))\n            third = it.key;\n        else if (*term < 0.0f) {\n            am_Num r = -row->constant / *term;\n            if (r < r1) r1 = r, first = it.key;\n        } else {\n            am_Num r = row->constant / *term;\n            if (r < r2) r2 = r, second = it.key;\n        }\n    }\n    return first.id ? first : second.id ? second : third;\n}\n\nAM_API void am_remove(am_Constraint *cons) {\n    am_Solver *S;\n    am_Symbol marker;\n    am_Row tmp;\n    if (cons == NULL || cons->marker.id == 0) return;\n    S = cons->S, marker = cons->marker;\n    am_remove_errors(S, cons);\n    if (am_takerow(S, marker, &tmp) != AM_OK) {\n        am_Symbol leave = am_get_leaving_row(S, marker);\n        assert(leave.id != 0);\n        am_takerow(S, leave, &tmp);\n        am_solvefor(S, &tmp, marker, leave);\n        am_substitute_rows(S, marker, &tmp);\n    }\n    am_freerow(S, &tmp);\n    am_optimize(S, &S->objective);\n    S->age += 1;\n    if (S->auto_update) am_updatevars(S);\n}\n\nAM_API int am_setstrength(am_Constraint *cons, am_Num strength) {\n    amC(cons);\n    strength = am_nearzero(strength) ? AM_REQUIRED : strength;\n    if (cons->strength == strength) return AM_OK;\n    if (cons->strength >= AM_REQUIRED || strength >= AM_REQUIRED)\n        return am_remove(cons), cons->strength = strength, am_add(cons);\n    if (cons->marker.id != 0) {\n        am_Solver *S = cons->S;\n        am_Num diff = strength - cons->strength;\n        am_mergerow(S, &S->objective, cons->marker, diff);\n        am_mergerow(S, &S->objective, cons->other,  diff);\n        am_optimize(S, &S->objective);\n        if (S->auto_update) am_updatevars(S);\n        S->age += 1;\n    }\n    cons->strength = strength;\n    return AM_OK;\n}\n\nstatic void am_cached_sugggest(am_Solver *S, am_Suggest *s, am_Num delta) {\n    am_Iterator it = am_itertable(&s->dirtyset);\n    am_Symbol marker = s->constraint.marker;\n    int pure = 1;\n    while (am_nextentry(&it)) {\n        am_Row *row = (am_Row*)am_gettable(&S->rows, it.key.id);\n        am_Num *term;\n        assert(row != NULL);\n        term = (am_Num*)am_gettable(&row->terms, marker.id);\n        assert(term != NULL);\n        row->constant += *term * delta;\n        if (am_isexternal(it.key))\n            am_markdirty(S, it.key);\n        else if (!am_nearzero(row->constant) && row->constant < 0.0f)\n            pure = 0, am_infeasible(S, it.key, row);\n    }\n    if (!pure) s->age = 0;\n}\n\nstatic void am_delta_edit_constant(am_Solver *S, am_Suggest *s, am_Num delta) {\n    am_Iterator it = am_itertable(&S->rows);\n    am_Constraint *cons = &s->constraint;\n    am_Row *row;\n    int pure = 1;\n    if ((row = (am_Row*)am_gettable(&S->rows, cons->marker.id)) != NULL)\n    { row->constant -= delta, am_infeasible(S, cons->marker, row); return; }\n    if ((row = (am_Row*)am_gettable(&S->rows, cons->other.id)) != NULL)\n    { row->constant += delta, am_infeasible(S, cons->other, row); return; }\n    if (s->age == S->age) { am_cached_sugggest(S, s, delta); return; }\n    am_resettable(&s->dirtyset);\n    while (am_nextentry(&it)) {\n        am_Row *row = am_val(am_Row,it);\n        am_Num *term = (am_Num*)am_gettable(&row->terms, cons->marker.id);\n        if (term == NULL) continue;\n        row->constant += *term*delta;\n        am_settable(S, &s->dirtyset, it.key);\n        if (am_isexternal(it.key))\n            am_markdirty(S, it.key);\n        else if (!am_nearzero(row->constant) && row->constant < 0.0f)\n            pure = 0, am_infeasible(S, it.key, row);\n    }\n    if (pure) s->age = S->age;\n}\n\nstatic void am_dual_optimize(am_Solver *S) {\n    while (S->infeasible_rows.id != 0) {\n        am_Symbol enter = am_null(), leave;\n        am_Num r, min_ratio = AM_NUM_MAX;\n        am_Iterator it;\n        am_Row tmp, *row =\n            (am_Row*)am_gettable(&S->rows, S->infeasible_rows.id);\n        assert(row != NULL);\n        leave = S->infeasible_rows;\n        S->infeasible_rows = row->infeasible_next;\n        row->infeasible_next = am_null();\n        if (am_nearzero(row->constant) || row->constant >= 0.0f) continue;\n        it = am_itertable(&row->terms);\n        while (am_nextentry(&it)) {\n            am_Num term = *am_val(am_Num,it);\n            am_Num *objterm;\n            if (am_isdummy(it.key) || term <= 0.0f) continue;\n            objterm = (am_Num*)am_gettable(&S->objective.terms, it.key.id);\n            r = objterm ? *objterm / term : 0.0f;\n            if (min_ratio > r) min_ratio = r, enter = it.key;\n        }\n        assert(enter.id != 0);\n        am_takerow(S, leave, &tmp);\n        am_solvefor(S, &tmp, enter, leave);\n        am_substitute_rows(S, enter, &tmp);\n        am_putrow(S, enter, &tmp);\n    }\n}\n\nAM_API void am_suggest(am_Solver *S, am_Id var, am_Num value) {\n    am_Suggest **ps, *s;\n    am_Num delta;\n    if (S == NULL || var == 0) return;\n    ps = (am_Suggest**)am_gettable(&S->suggests, var);\n    s = ps ? *ps : am_newedit(S, var, AM_MEDIUM);\n    assert(s != NULL);\n    delta = value - s->edit_value, s->edit_value = value;\n    am_delta_edit_constant(S, s, delta);\n    am_dual_optimize(S);\n    if (S->auto_update) am_updatevars(S);\n}\n\nAM_API am_Solver *am_newsolver(am_Allocf *allocf, void *ud) {\n    am_Solver *S;\n    if (allocf == NULL) allocf = am_default_allocf;\n    S = (am_Solver*)allocf(&ud, NULL, sizeof(am_Solver), 0, am_AllocSolver);\n    if (S == NULL) return NULL;\n    memset(S, 0, sizeof(*S));\n    S->ud     = ud;\n    S->allocf = allocf;\n    am_initrow(&S->objective);\n    am_inittable(&S->vars, sizeof(am_Var));\n    am_inittable(&S->constraints, sizeof(am_Constraint*));\n    am_inittable(&S->suggests, sizeof(am_Suggest*));\n    am_inittable(&S->rows, sizeof(am_Row));\n    am_initpool(&S->conspool, sizeof(am_Constraint));\n    return S;\n}\n\nAM_API void am_delsolver(am_Solver *S) {\n    am_Iterator it = am_itertable(&S->constraints);\n    while (am_nextentry(&it)) {\n        am_Constraint *cons = *am_val(am_Constraint*,it);\n        am_freerow(S, &cons->expression);\n        if (am_isexternal(cons->sym)) am_free(S, cons, am_AllocConstraint);\n    }\n    it = am_itertable(&S->suggests);\n    while (am_nextentry(&it)) {\n        am_Suggest *s = *am_val(am_Suggest*,it);\n        am_freetable(S, &s->dirtyset);\n        am_freetable(S, &s->constraint.expression.terms);\n        am_free(S, s, am_AllocSuggest);\n    }\n    it = am_itertable(&S->rows);\n    while (am_nextentry(&it))\n        am_freerow(S, am_val(am_Row,it));\n    am_freerow(S, &S->objective);\n    am_freetable(S, &S->vars);\n    am_freetable(S, &S->constraints);\n    am_freetable(S, &S->suggests);\n    am_freetable(S, &S->rows);\n    am_freepool(&S->conspool);\n    am_freearena(S, &S->arena);\n    am_free(S, S, am_AllocSolver);\n}\n\nAM_API void am_resetsolver(am_Solver *S) {\n    am_Iterator it;\n    if (S == NULL) return;\n    am_clearedits(S);\n    it = am_itertable(&S->constraints);\n    while (am_nextentry(&it)) {\n        am_Constraint *cons = *am_val(am_Constraint*,it);\n        cons->marker = cons->other = am_null();\n    }\n    it = am_itertable(&S->rows);\n    while (am_nextentry(&it))\n        am_freerow(S, am_val(am_Row,it));\n    am_resettable(&S->rows);\n    am_resetrow(&S->objective);\n    am_freearena(S, &S->arena);\n    assert(S->infeasible_rows.id == 0);\n    S->age = 0;\n}\n\n/* dump & load */\n\n#ifndef AM_NAME_LEN\n# define AM_NAME_LEN 256\n#endif /* AM_NAME_LEN */\n#ifndef AM_BUF_LEN\n# define AM_BUF_LEN  4096\n#endif /* AM_BUF_LEN */\n\ntypedef struct am_DumpCtx {\n    const am_Solver *S;\n    unsigned  *syms; \n    unsigned  *cons;\n    am_Table   symmap;\n    am_Dumper *dumper;\n    char      *p;\n    int        ret;\n    char       buf[AM_BUF_LEN];\n} am_DumpCtx;\n\nstatic int am_intcmp(const void *lhs, const void *rhs)\n{ return *(const int*)lhs - *(const int*)rhs; }\n\nstatic int am_writechar(am_DumpCtx *ctx, int ch) {\n    if ((ctx->p - ctx->buf) >= AM_BUF_LEN) {\n        amE(ctx->ret = ctx->dumper->writer(ctx->dumper, ctx->buf, AM_BUF_LEN));\n        ctx->p = ctx->buf;\n    }\n    return (*ctx->p++ = ch & 0xFF), AM_OK;\n}\n\nstatic int am_writeraw(am_DumpCtx *ctx, am_Size n, int width) {\n    switch (width) {\n    default: return AM_FAILED;\n    case 32: amE(am_writechar(ctx, n >> 24));\n             amE(am_writechar(ctx, n >> 16)); /* FALLTHROUGH */\n    case 16: amE(am_writechar(ctx, n >> 8));\n             amE(am_writechar(ctx, n & 0xFF));\n    }\n    return AM_OK;\n}\n\nstatic int am_writeuint32(am_DumpCtx *ctx, am_Size n) {\n    if (n <= 0x7F) amE(am_writechar(ctx, n));\n    else if (n <= 0xFF) {\n        amE(am_writechar(ctx, 0xCC));\n        amE(am_writechar(ctx, n));\n    } else if (n <= 0xFFFF) {\n        amE(am_writechar(ctx, 0xCD));\n        amE(am_writeraw(ctx, n, 16));\n    } else {\n        amE(am_writechar(ctx, 0xCE));\n        amE(am_writeraw(ctx, n, 32));\n    }\n    return AM_OK;\n}\n\nstatic int am_writefloat(am_DumpCtx *ctx, am_Num n) {\n    union { double f64; float f32; unsigned u32; } u;\n    if (sizeof(am_Num) == sizeof(float))\n        u.f32 = n;\n    else\n        u.f64 = n, u.f32 = (float)u.f64;\n    amE(am_writechar(ctx, 0xCA));\n    amE(am_writeraw(ctx, u.u32, 32));\n    return AM_OK;\n}\n\nstatic int am_writestring(am_DumpCtx *ctx, const char *name) {\n    am_Dumper *d = ctx->dumper;\n    size_t namelen = (name ? strlen(name) : 0), buflen;\n    amC(namelen < AM_NAME_LEN && namelen <= 0xFF);\n    if (namelen < 32)\n        amE(am_writechar(ctx, 0xA0 + (int)namelen));\n    else {\n        amE(am_writechar(ctx, 0xD9));\n        amE(am_writechar(ctx, (int)namelen));\n    }\n    if ((buflen = (ctx->p - ctx->buf)) + namelen > AM_BUF_LEN) {\n        ctx->p = ctx->buf;\n        if (buflen) amE(ctx->ret = d->writer(d, ctx->buf, buflen));\n        if (namelen > AM_BUF_LEN) return ctx->ret = d->writer(d, name, namelen);\n    }\n    memcpy(ctx->p, name, namelen);\n    return ctx->p += namelen, AM_OK;\n}\n\nstatic int am_writecount(am_DumpCtx *ctx, am_Size count) {\n    if (count < 16)\n        amE(am_writechar(ctx, 0x90 + count));\n    else if (count <= 0xFFFF) {\n        amE(am_writechar(ctx, 0xDC));\n        amE(am_writeraw(ctx, count, 16));\n    } else {\n        amE(am_writechar(ctx, 0xDD));\n        amE(am_writeraw(ctx, count, 32));\n    }\n    return AM_OK;\n}\n\nstatic unsigned am_mapid(am_DumpCtx *ctx, unsigned key) {\n    unsigned *id = (unsigned*)am_gettable(&ctx->symmap, key);\n    return assert(id != NULL), *id;\n}\n\nstatic int am_writerow(am_DumpCtx *ctx, const am_Row *row) {\n    am_Iterator it = am_itertable(&row->terms);\n    amE(am_writecount(ctx, row->terms.count * 2 + 1));\n    amE(am_writefloat(ctx, row->constant));\n    while (am_nextentry(&it)) {\n        amE(am_writeuint32(ctx, am_mapid(ctx, it.key.id) << 2 | it.key.type));\n        amE(am_writefloat(ctx, *am_val(am_Num,it)));\n    }\n    return AM_OK;\n}\n\nstatic int am_writevars(am_DumpCtx *ctx) {\n    const am_Solver *S = ctx->S;\n    am_Size i;\n    amE(am_writecount(ctx, S->vars.count));\n    for (i = 0; i < S->vars.count; ++i) {\n        am_Var *ve = (am_Var*)am_gettable(&S->vars, ctx->syms[i]);\n        assert(ve != NULL);\n        amE(am_writestring(ctx,\n                    ctx->dumper->var_name(ctx->dumper, i, ctx->syms[i], ve->pvalue)));\n    }\n    return AM_OK;\n}\n\nstatic int am_writeconstraints(am_DumpCtx *ctx) {\n    const am_Solver *S = ctx->S;\n    am_Size i;\n    amE(am_writecount(ctx, S->constraints.count));\n    for (i = 0; i < S->constraints.count; ++i) {\n        am_Constraint **ce = (am_Constraint**)am_gettable(\n                &S->constraints, ctx->cons[i]);\n        unsigned id;\n        assert(ce != NULL);\n        amE(am_writecount(ctx, 6));\n        /* [name, strength, marker, other, relation, row] */\n        amE(am_writestring(ctx, ctx->dumper->cons_name ?\n                    ctx->dumper->cons_name(ctx->dumper, i, (*ce)) : NULL));\n        amE(am_writefloat(ctx, (*ce)->strength));\n        if (id = 0, (*ce)->marker.type)\n            id = am_mapid(ctx, (*ce)->marker_id) << 2 | (*ce)->marker.type;\n        amE(am_writeuint32(ctx, id));\n        if (id = 0, (*ce)->other.type)\n            id = am_mapid(ctx, (*ce)->other_id) << 2 | (*ce)->other.type;\n        amE(am_writeuint32(ctx, id));\n        amE(am_writeuint32(ctx, (*ce)->relation));\n        amE(am_writerow(ctx, &(*ce)->expression));\n    }\n    return AM_OK;\n}\n\nstatic int am_writerows(am_DumpCtx *ctx) {\n    const am_Solver *S = ctx->S;\n    size_t arena = 0;\n    am_Iterator it = am_itertable(&S->rows);\n    while (am_nextentry(&it))\n        arena += am_calcsize(am_val(am_Row,it)->terms.count);\n    if (arena > ~(am_Size)0) arena = 0;\n    amE(am_writecount(ctx, S->rows.count*2 + 1));\n    amE(am_writeuint32(ctx, (am_Size)arena));\n    while (am_nextentry(&it)) {\n        amE(am_writeuint32(ctx, am_mapid(ctx, it.key.id) << 2 | it.key.type));\n        amE(am_writerow(ctx, am_val(am_Row,it)));\n    }\n    return AM_OK;\n}\n\nstatic int am_collect(am_DumpCtx *ctx) {\n    const am_Solver *S = ctx->S;\n    am_Symbol sym = {0, 0};\n    size_t i, vc = S->vars.count, cc = 0, count = 0;\n    am_Iterator it = am_itertable(&S->vars);\n    while (am_nextentry(&it)) ctx->syms[count++] = it.key.id;\n    it = am_itertable(&S->constraints);\n    while (am_nextentry(&it)) {\n        am_Constraint *cons = *am_val(am_Constraint*,it);\n        if (cons->marker.type) ctx->syms[count++] = cons->marker_id;\n        if (cons->other.type) ctx->syms[count++] = cons->other_id;\n        ctx->cons[cc++] = it.key.id;\n    }\n    qsort(ctx->cons, cc, sizeof(unsigned), am_intcmp);\n    qsort(ctx->syms, vc, sizeof(unsigned), am_intcmp);\n    qsort(ctx->syms + vc, count - vc, sizeof(unsigned), am_intcmp);\n    am_inittable(&ctx->symmap, sizeof(unsigned));\n    amE(am_growtable(S, &ctx->symmap, count, NULL));\n    for (i = 0; i < count; ++i) {\n        unsigned *val = (unsigned*)am_settable(S, &ctx->symmap,\n                (sym.id = ctx->syms[i], sym));\n        assert(val != NULL), *val = (unsigned)i;\n    }\n    return assert(count == ctx->symmap.count), AM_OK;\n}\n\nstatic int am_dumpall(am_DumpCtx *ctx) {\n    const am_Solver *S = ctx->S;\n    am_Dumper *d = ctx->dumper;\n    amE(am_writecount(ctx, 5));\n    /* [total, vars, constraints, rows, objective] */\n    amE(am_writeuint32(ctx, ctx->symmap.count));\n    amE(am_writevars(ctx));\n    amE(am_writeconstraints(ctx));\n    amE(am_writerows(ctx));\n    amE(am_writerow(ctx, &S->objective));\n    if (ctx->p > ctx->buf)\n        amE(ctx->ret = d->writer(d, ctx->buf, (ctx->p - ctx->buf)));\n    return ctx->ret;\n}\n\nAM_API int am_dump(am_Solver *S, am_Dumper *dumper) {\n    size_t cons_alloc, sym_alloc;\n    am_DumpCtx ctx;\n    ctx.ret = AM_FAILED;\n    amC(S && dumper && dumper->writer && dumper->var_name);\n    am_clearedits(S);\n    cons_alloc = sizeof(unsigned) * S->constraints.count;\n    sym_alloc = sizeof(unsigned) * (cons_alloc*2 + S->vars.count);\n    memset(&ctx, 0, sizeof(ctx));\n    ctx.syms = (unsigned*)S->allocf(&S->ud, NULL, sym_alloc, 0, am_AllocDump);\n    ctx.cons = (unsigned*)S->allocf(&S->ud, NULL, cons_alloc, 0, am_AllocDump);\n    ctx.S = S;\n    if (ctx.syms && ctx.cons && (ctx.ret = am_collect(&ctx)) == AM_OK) {\n        ctx.dumper = dumper;\n        ctx.p = ctx.buf;\n        ctx.ret = am_dumpall(&ctx);\n    }\n    if (ctx.syms) S->allocf(&S->ud, ctx.syms, 0, sym_alloc, am_AllocDump);\n    if (ctx.cons) S->allocf(&S->ud, ctx.cons, 0, cons_alloc, am_AllocDump);\n    am_freetable(S, &ctx.symmap);\n    return ctx.ret;\n}\n\ntypedef struct am_LoadCtx {\n    am_Solver  *S;\n    am_Loader  *loader;\n    size_t      n;\n    const char *p, *s;\n    am_Size     offset;\n    char        buf[AM_NAME_LEN];\n} am_LoadCtx;\n\n#define am_getchar(ctx) ((ctx)->n-- > 0 ? *(ctx)->p++ & 0xFF : am_fill(ctx))\n\nstatic int am_fill(am_LoadCtx *ctx) {\n    ctx->p = ctx->loader->reader(ctx->loader, &ctx->n);\n    amC(ctx->p && ctx->n);\n    return ctx->n -= 1, *ctx->p++ & 0xFF;\n}\n\nstatic int am_readraw_slow(am_LoadCtx *ctx, unsigned *pv, int width) {\n    int c1 = 0, c2 = 0, c3, c4;\n    switch (width) {\n    default: return AM_FAILED;\n    case 32: amC((c1 = am_getchar(ctx)) != AM_FAILED);\n             amC((c2 = am_getchar(ctx)) != AM_FAILED); /* FALLTHROUGH */\n    case 16: amC((c3 = am_getchar(ctx)) != AM_FAILED);\n             amC((c4 = am_getchar(ctx)) != AM_FAILED);\n    }\n    return (*pv = c1 << 24 | c2 << 16 | c3 << 8 | c4), AM_OK;\n}\n\nstatic int am_readraw32(am_LoadCtx *ctx, unsigned *pv) {\n    if (ctx->n >= 4) {\n        unsigned n = *ctx->p++ & 0xFF;\n        n <<= 8; n |= *ctx->p++ & 0xFF;\n        n <<= 8; n |= *ctx->p++ & 0xFF;\n        n <<= 8; n |= *ctx->p++ & 0xFF;\n        return *pv = n, ctx->n -= 4, AM_OK;\n    }\n    return am_readraw_slow(ctx, pv, 32);\n}\n\nstatic int am_readraw16(am_LoadCtx *ctx, unsigned *pv) {\n    if (ctx->n >= 2) {\n        unsigned n = *ctx->p++ & 0xFF;\n        n <<= 8; n |= *ctx->p++ & 0xFF;\n        return *pv = n, ctx->n -= 2, AM_OK;\n    }\n    return am_readraw_slow(ctx, pv, 16);\n}\n\nstatic int am_readuint32(am_LoadCtx *ctx, am_Size *pv) {\n    int ty, c;\n    switch (ty = am_getchar(ctx)) {\n    default: amC(ty <= 0x7F); *pv = ty; break;\n    case 0xCC: amC((c = am_getchar(ctx)) != AM_FAILED); *pv = c; break;\n    case 0xCD: amE(am_readraw16(ctx, pv)); break;\n    case 0xCE: amE(am_readraw32(ctx, pv)); break;\n    }\n    return AM_OK;\n}\n\nstatic int am_readfloat(am_LoadCtx *ctx, am_Num *pv) {\n    union { float f32; unsigned u32[2]; } u;\n    amC(am_getchar(ctx) == 0xCA);\n    amE(am_readraw32(ctx, &u.u32[0]));\n    return *pv = (am_Num)u.f32, AM_OK;\n}\n\nstatic int am_readstring(am_LoadCtx *ctx) {\n    char *buf = ctx->buf;\n    am_Size size;\n    int ty, c;\n    switch (ty = am_getchar(ctx)) {\n    default:   amC(ty >= 0xA0 && ty <= 0xBF); size = ty - 0xA0;   break;\n    case 0xD9: amC((c = am_getchar(ctx)) != AM_FAILED); size = c; break;\n    }\n    ctx->s = size ? ctx->buf : NULL;\n    if (ctx->n >= size)\n        return memcpy(ctx->buf, ctx->p, size), ctx->buf[size] = 0,\n               ctx->p += size, ctx->n -= size, AM_OK;\n    for (;;) {\n        memcpy(buf, ctx->p, ctx->n), buf += ctx->n, size -= (am_Size)ctx->n;\n        amC((c = am_fill(ctx)) != AM_FAILED);\n        *buf++ = c, size -= 1;\n        if (size <= ctx->n) {\n            memcpy(buf, ctx->p, size), buf[size] = 0;\n            return (ctx->n -= size, ctx->p += size), AM_OK;\n        }\n    }\n}\n\nstatic int am_readcount(am_LoadCtx *ctx, am_Size *pcount) {\n    int ty;\n    switch (ty = am_getchar(ctx)) {\n    default: amC(ty >= 0x90 && ty <= 0x9F); *pcount = ty - 0x90; break;\n    case 0xDC: amE(am_readraw16(ctx, pcount)); break;\n    case 0xDD: amE(am_readraw32(ctx, pcount)); break; \n    }\n    return AM_OK;\n}\n\nstatic int am_readrow(am_LoadCtx *ctx, am_Row *row, void **arena) {\n    am_Size i, count, value;\n    amE(am_readcount(ctx, &count));\n    amC(count >= 1 && (count & 1) == 1);\n    amE(am_growtable(ctx->S, &row->terms, count/2, arena));\n    amE(am_readfloat(ctx, &row->constant));\n    for (i = 1; i < count; i += 2) {\n        am_Symbol sym = {0, 0};\n        am_Num *term;\n        amE(am_readuint32(ctx, &value));\n        sym.id = ctx->offset+(value>>2), sym.type = value & 3;\n        amC(term = (am_Num*)am_settable(ctx->S, &row->terms, sym));\n        amE(am_readfloat(ctx, term));\n    }\n    return AM_OK;\n}\n\nstatic int am_readvars(am_LoadCtx *ctx) {\n    am_Solver *S = ctx->S;\n    am_Size i, count;\n    am_Symbol sym = {0, AM_EXTERNAL};\n    amE(am_readcount(ctx, &count));\n    amE(am_growtable(S, &S->vars, count, NULL));\n    for (i = 0; i < count; ++i) {\n        am_Var *ve;\n        am_Num *pvalue;\n        amE(am_readstring(ctx));\n        pvalue = ctx->loader->load_var(ctx->loader, ctx->s, i, ctx->offset+i);\n        amC(pvalue != NULL);\n        sym.id = ctx->offset+i;\n        amC(ve = (am_Var*)am_settable(S, &S->vars, sym));\n        memset(ve, 0, sizeof(*ve));\n        ve->pvalue   = pvalue;\n        ve->refcount = 1;\n    }\n    return AM_OK;\n}\n\nstatic int am_readmarker(am_LoadCtx *ctx, am_Symbol *sym, unsigned *id) {\n    am_Size value;\n    amE(am_readuint32(ctx, &value));\n    *id = ctx->offset + (value >> 2);\n    sym->type = value & 3;\n    if (sym->type) sym->id = *id;\n    return AM_OK;\n}\n\nstatic int am_readconstraints(am_LoadCtx *ctx) {\n    am_Size i, count, value;\n    am_Solver *S = ctx->S;\n    amE(am_readcount(ctx, &count));\n    amE(am_growtable(S, &S->constraints, count, NULL));\n    for (i = 0; i < count; ++i) {\n        am_Constraint *cons;\n        am_Num strength;\n        amE(am_readcount(ctx, &value));\n        amC(value == 6); /* [name, strength, marker, other, relation, row] */\n        amE(am_readstring(ctx));\n        amE(am_readfloat(ctx, &strength));\n        amC(cons = am_newconstraint(S, strength));\n        if (ctx->loader->load_cons)\n            ctx->loader->load_cons(ctx->loader, ctx->s, i, cons);\n        amE(am_readmarker(ctx, &cons->marker, &cons->marker_id));\n        amE(am_readmarker(ctx, &cons->other, &value));\n        cons->other_id = value;\n        amE(am_readuint32(ctx, &value));\n        amC(value >= AM_LESSEQUAL && value <= AM_GREATEQUAL);\n        cons->relation = value;\n        amE(am_readrow(ctx, &cons->expression, NULL));\n    }\n    return AM_OK;\n}\n\nstatic int am_readrows(am_LoadCtx *ctx) {\n    am_Solver *S = ctx->S;\n    am_Size i, count, value, arena;\n    void **arena_buf = NULL, *ptr = NULL;\n    amE(am_readcount(ctx, &count));\n    amC((count & 1) == 1);\n    amE(am_readuint32(ctx, &arena));\n    if (arena) {\n        am_allocarena(S, &S->arena, arena * (sizeof(am_Key)+sizeof(am_Num)));\n        ptr = S->arena.buf, arena_buf = &ptr;\n    }\n    amE(am_growtable(S, &S->rows, count/2, NULL));\n    for (i = 1; i < count; i += 2) {\n        am_Symbol sym;\n        am_Row *row;\n        amE(am_readuint32(ctx, &value));\n        sym.id = ctx->offset + (value >> 2), sym.type = value & 3;\n        amC(row = (am_Row*)am_settable(S, &S->rows, sym));\n        am_initrow(row);\n        amE(am_readrow(ctx, row, arena_buf));\n    }\n    assert(!ptr || (am_Size)((char*)ptr-(char*)S->arena.buf) == S->arena.size);\n    return AM_OK;\n}\n\nAM_API int am_load(am_Solver *S, am_Loader *loader) {\n    am_LoadCtx ctx;\n    am_Size count, total;\n    amC(S && loader && loader->reader && loader->load_var);\n    memset(&ctx, 0, sizeof(ctx));\n    ctx.S = S;\n    ctx.offset = S->current_id + 1;\n    ctx.loader = loader;\n    amE(am_readcount(&ctx, &count));\n    amC(count == 5); /* [total, vars, constraints, rows, objective] */\n    amE(am_readuint32(&ctx, &total));\n    amC(ctx.offset + total <= 0x3FFFFFFF);\n    am_resetsolver(S);\n    amE(am_readvars(&ctx));\n    amE(am_readconstraints(&ctx));\n    amE(am_readrows(&ctx));\n    amE(am_readrow(&ctx, &S->objective, NULL));\n    return S->current_id = ctx.offset + total, AM_OK;\n}\n\n\nAM_NS_END\n\n#endif /* AM_IMPLEMENTATION */\n\n/* cc: flags+='-shared -O2 -pedantic -std=c89 -DAM_IMPLEMENTATION -xc'\n   unixcc: output='amoeba.so'\n   win32cc: output='amoeba.dll' */\n\n"
  },
  {
    "path": "amoeba.lua",
    "content": "\nlocal function meta(name, parent)\n   local t = {}\n   t.__name  = name\n   t.__index = t\n   return setmetatable(t, parent)\nend\n\nlocal function approx(a, b)\n   if a > b then return a - b < 1e-6 end\n   return b - a < 1e-6\nend\n\nlocal function near_zero(n)\n   return approx(n, 0.0)\nend\n\nlocal Variable, Expression, Constraint do\n\nVariable   = meta \"Variable\"\nExpression = meta \"Expression\"\nConstraint = meta \"Constraint\"\n\nConstraint.REQUIRED = 1000000000.0\nConstraint.STRONG   = 1000000.0\nConstraint.MEDIUM   = 1000.0\nConstraint.WEAK     = 1.0\n\nfunction Variable:__unm() return Expression.new(self, -1.0) end\nfunction Expression:__unm() return Expression.new(self):multiply(-1) end\n\nfunction Variable:__add(other) return Expression.new(self) + other end\nfunction Variable:__sub(other) return Expression.new(self) - other end\nfunction Variable:__mul(other) return Expression.new(self) * other end\nfunction Variable:__div(other) return Expression.new(self) / other end\n\nfunction Variable:le(other) return Expression.new(self):le(other) end\nfunction Variable:eq(other) return Expression.new(self):eq(other) end\nfunction Variable:ge(other) return Expression.new(self):ge(other) end\n\nfunction Expression:__add(other) return Expression.new(self):add(other) end\nfunction Expression:__sub(other) return Expression.new(self):add(-other) end\nfunction Expression:__mul(other) return Expression.new(self):multiply(other) end\nfunction Expression:__div(other) return Expression.new(self):multiply(1.0/other) end\n\nfunction Expression:le(other) return Constraint.new(\"<=\", self, other) end\nfunction Expression:eq(other) return Constraint.new(\"==\", self, other) end\nfunction Expression:ge(other) return Constraint.new(\">=\", self, other) end\n\nfunction Constraint:__call(...) return self:add(...) end\n\nfunction Variable.new(name, type, id)\n   type = type or \"external\"\n   assert(type == \"external\" or\n          type == \"slack\" or\n          type == \"error\" or\n          type == \"dummy\", type)\n   local self = {\n      id    = id,\n      name  = name,\n      value = 0.0,\n      type  = type or \"external\",\n      is_dummy        = type == \"dummy\",\n      is_slack        = type == \"slack\",\n      is_error        = type == \"error\",\n      is_external     = type == \"external\",\n      is_pivotable    = type == \"slack\" or type == \"error\",\n      is_restricted   = type ~= \"external\",\n   }\n   return setmetatable(self, Variable)\nend\n\nfunction Variable:__tostring()\n   return (\"amoeba.Variable: %s = %g\"):format(self.name, self.value)\nend\n\nfunction Expression.new(other, multiplier, constant)\n   local self = setmetatable({}, Expression)\n   return self:add(other, multiplier, constant)\nend\n\nfunction Expression:tostring()\n   local t = { (\"%g\"):format(self.constant or 0.0) }\n   for k, v in self:iter_vars() do\n      t[#t+1] = v < 0.0 and ' - ' or ' + '\n      v = math.abs(v)\n      if not approx(v, 1.0) then\n         t[#t+1] = (\"%g*\"):format(v)\n      end\n      t[#t+1] = k.name\n   end\n   return table.concat(t)\nend\n\nfunction Expression:__tostring()\n   return \"Exp: \"..self:tostring()\nend\n\nfunction Expression:add(other, multiplier, constant)\n   if other == nil then return self end\n   self.constant = (self.constant or 0.0) + (constant or 0.0)\n   multiplier = multiplier or 1.0\n   if tonumber(other) then\n      self.constant = self.constant + other*multiplier\n      return self\n   end\n   local mt = getmetatable(other)\n   if mt == Variable then\n      multiplier = (self[other] or 0.0) + multiplier\n      self[other] = not near_zero(multiplier) and multiplier or nil\n   elseif mt == Expression then\n      for k, v in pairs(other) do\n         multiplier = (self[k] or 0.0) + multiplier * v\n         self[k] = not near_zero(multiplier) and multiplier or nil\n      end\n      self.constant = self.constant or 0.0\n   else\n      error(\"constant/variable/expression expected\")\n   end\n   return self\nend\n\nfunction Expression:multiply(other)\n   if tonumber(other) then\n      for k, v in pairs(self) do\n         self[k] = v * other\n      end\n      return self\n   end\n   local mt = getmetatable(other)\n   if mt == Variable then\n      return self:multiply(Expression.new(other))\n   elseif mt == Expression then\n      if other:is_constant() then\n         return self:multiply(other.constant)\n      elseif self.constant then\n         local constant = self.constant\n         self.constant = 0.0\n         return self:add(other):multiply(constant)\n      end\n      error(\"attempt to multiply two non-constant expression\")\n   else\n      error(\"number/variable/constant expression expected\")\n   end\nend\n\nfunction Expression:choose_pivotable()\n   for k in pairs(self) do\n      if k.is_pivotable then\n         return k\n      end\n   end\nend\n\nfunction Expression:is_constant()\n   local f, state = self:iter_vars()\n   return f(state) == nil\nend\n\nfunction Expression:solve_for(new, old)\n   -- expr: old ==    a[n] *new +        constant +    a[i]*      v[i]...\n   -- =>    new == (1/a[n])*old - 1/a[n]*constant - (1/a[n])*a[i]*v[i]...\n   local multiplier = assert(self[new])\n   assert(new ~= old and not near_zero(multiplier))\n   self[new] = nil\n   local reciprocal = 1.0 / multiplier\n   self:multiply(-reciprocal)\n   if old then self[old] = reciprocal end\n   return new\nend\n\nfunction Expression:substitute_out(var, expr)\n   assert(var ~= \"constant\")\n   local multiplier = self[var]\n   if not multiplier then return end\n   self[var] = nil\n   self:add(expr, multiplier)\nend\n\nfunction Expression:iter_vars()\n   return function(self1, k1)\n      local k, v = next(self1, k1)\n      if k == 'constant' then\n         return next(self1, k1)\n      end\n      return k, v\n   end, self\nend\n\nfunction Constraint.new(op, expr1, expr2, strength)\n   local self = setmetatable({}, Constraint)\n   if not op then\n      self.expression = Expression.new()\n   else\n      self:relation(op)\n      if self.op == '<=' then\n         self.expression = Expression.new(expr2 or 0.0):add(expr1, -1.0)\n      else\n         self.expression = Expression.new(expr1 or 0.0):add(expr2, -1.0)\n      end\n   end\n   return self:strength(strength or Constraint.REQUIRED)\nend\n\nfunction Constraint:__tostring()\n   local repr = \"amoeba.Constraint: [\"..self.expression:tostring()\n   if self.is_inequality then\n      repr = repr .. \" >= 0.0]\"\n   else\n      repr = repr .. \" == 0.0]\"\n   end\n   return repr\nend\n\nfunction Constraint:add(other, multiplier, constant)\n   if other == \">=\" or other == \"<=\" or other == \"==\" then\n      self:relation(other)\n   else\n      multiplier = multiplier or 1.0\n      if self.op == '>=' then multiplier = -multiplier end\n      self.expression:add(other, multiplier, constant)\n   end\n   return self\nend\n\nfunction Constraint:relation(op)\n   assert(op == '==' or op == '<=' or op == '>=' or\n          op == 'eq' or op == 'le' or op == 'ge',\n          \"op must be '==', '>=' or '<='\")\n   if op == 'eq'     then op = '=='\n   elseif op == 'le' then op = '<='\n   elseif op == 'ge' then op = '>=' end\n   self.op = op\n   if self.op ~= '==' then\n      self.is_inequality = true\n   end\n   if self.op ~= '>=' and self.expression then\n      self.expression:multiply(-1.0)\n   end\n   return self\nend\n\nfunction Constraint:strength(strength)\n   if self.solver then\n      self.solver:setstrength(self, strength)\n   else\n      self.weight = Constraint[strength] or tonumber(strength) or self.weight\n      self.is_required = self.weight >= Constraint.REQUIRED\n   end\n   return self\nend\n\nfunction Constraint:clone(strength)\n   local new = Constraint.new():strength(strength)\n   new:add(self)\n   new.op            = self.op\n   new.is_inequality = self.is_inequality\n   return new\nend\n\nend\n\nlocal SimplexSolver = meta \"SimplexSolver\" do\n\n-- implements\n\nlocal function update_external_variables(self)\n   for var in pairs(self.vars) do\n      local row = self.rows[var]\n      var.value = row and row.constant or 0.0\n   end\nend\n\nlocal function substitute_out(self, var, expr)\n   for k, row in pairs(self.rows) do\n      row:substitute_out(var, expr)\n      if k.is_restricted and row.constant < 0.0 then\n         self.infeasible_rows[#self.infeasible_rows+1] = k\n      end\n   end\n   self.objective:substitute_out(var, expr)\nend\n\nlocal function optimize(self, objective)\n   objective = objective or self.objective\n   while true do\n      local entry, exit\n      for var, multiplier in objective:iter_vars() do\n         if not var.is_dummy and multiplier < 0.0 then\n            entry = var\n            break\n         end\n      end\n      if not entry then return end\n\n      local r\n      local min_ratio = math.huge\n      for var, row in pairs(self.rows) do\n         local multiplier = row[entry]\n         if multiplier and var.is_pivotable and multiplier < 0.0 then\n            r = -row.constant / multiplier\n            if r < min_ratio or (approx(r, min_ratio) and\n                                 var.id < exit.id) then\n               min_ratio, exit = r, var\n            end\n         end\n      end\n      assert(exit, \"objective function is unbounded\")\n\n      -- do pivot\n      local row = self.rows[exit]\n      self.rows[exit] = nil\n      row:solve_for(entry, exit)\n      substitute_out(self, entry, row)\n      if objective ~= self.objective then\n         objective:substitute_out(entry, row)\n      end\n      self.rows[entry] = row\n   end\nend\n\nlocal function make_variable(self, type)\n   local id = self.last_varid\n   self.last_varid = id + 1\n   local prefix = type == \"eplus\" and \"ep\" or\n                  type == \"eminus\" and \"em\" or\n                  type == \"dummy\" and \"d\" or\n                  type == \"artificial\" and \"a\" or \"s\"\n   if not type or type == \"artificial\" then\n      type = \"slack\"\n   elseif type == \"eplus\" or type == \"eminus\" then\n      type = \"error\"\n   end\n   return Variable.new(prefix..id, type, id)\nend\n\nlocal function make_expression(self, cons)\n   local expr = Expression.new(cons.expression.constant)\n   local var1, var2\n   for k, v in cons.expression:iter_vars() do\n      if not k.id then\n         k.id = self.last_varid\n         self.last_varid = k.id + 1\n      end\n      if not self.vars[k] then\n         self.vars[k] = true\n      end\n      expr:add(self.rows[k] or k, v)\n   end\n   if cons.is_inequality then\n      var1 = make_variable(self) -- slack\n      expr[var1] = -1.0\n      if not cons.is_required then\n         var2 = make_variable(self, \"eminus\")\n         expr[var2] = 1.0\n         self.objective[var2] = cons.weight\n      end\n   elseif cons.is_required then\n      var1 = make_variable(self, 'dummy')\n      expr[var1] = 1.0\n   else\n      var1 = make_variable(self, 'eplus')\n      var2 = make_variable(self, 'eminus')\n      expr[var1] = -1.0\n      expr[var2] =  1.0\n      self.objective[var1] = cons.weight\n      self.objective[var2] = cons.weight\n   end\n   if expr.constant < 0.0 then expr:multiply(-1.0) end\n   return expr, var1, var2\nend\n\nlocal function choose_subject(_, expr, var1, var2)\n   for k in expr:iter_vars() do\n      if k.is_external then return k end\n   end\n   if var1 and var1.is_pivotable then return var1 end\n   if var2 and var2.is_pivotable then return var2 end\n   for k in expr:iter_vars() do\n      if not k.is_dummy then return nil end -- no luck\n   end\n   if not near_zero(expr.constant) then\n      return nil, \"unsatisfiable required constraint added\"\n   end\n   return var1\nend\n\nlocal function add_with_artificial_variable(self, expr)\n   local a = make_variable(self, 'artificial')\n   self.last_varid = self.last_varid - 1\n\n   self.rows[a] = expr\n   optimize(self, expr)\n   local row = self.rows[a]\n   self.rows[a] = nil\n\n   local success = near_zero(expr.constant)\n   if row then\n      if row:is_constant() then\n         return success\n      end\n      local entering = row:choose_pivotable()\n      if not entering then return false end\n\n      row:solve_for(entering, a)\n      self.rows[entering] = row\n   end\n\n   for _, r in pairs(self.rows) do r[a] = nil end\n   self.objective[a] = nil\n   return success\nend\n\nlocal function get_marker_leaving_row(self, marker)\n   local r1, r2 = math.huge, math.huge\n   local first, second, third\n   for var, row in pairs(self.rows) do\n      local multiplier = row[marker]\n      if multiplier then\n         if var.is_external then\n            third = var\n         elseif multiplier < 0.0 then\n            local r = -row.constant / multiplier\n            if r < r1 then r1 = r; first = var end\n         else\n            local r = row.constant / multiplier\n            if r < r2 then r2 = r; second = var end\n         end\n      end\n   end\n   return first or second or third\nend\n\nlocal function delta_edit_constant(self, delta, var1, var2)\n   local row = self.rows[var1]\n   if row then\n      row.constant = row.constant - delta\n      if row.constant < 0.0 then\n         self.infeasible_rows[#self.infeasible_rows+1] = var1\n      end\n      return\n   end\n   row = self.rows[var2]\n   if row then\n      row.constant = row.constant + delta\n      if row.constant < 0.0 then\n         self.infeasible_rows[#self.infeasible_rows+1] = var2\n      end\n      return\n   end\n   for var, r in pairs(self.rows) do\n      r.constant = r.constant + (r[var1] or 0.0)*delta\n      if var.is_restricted and r.constant < 0.0 then\n         self.infeasible_rows[#self.infeasible_rows+1] = var\n      end\n   end\nend\n\nlocal function dual_optimize(self)\n   while true do\n      local count = #self.infeasible_rows\n      if count == 0 then return end\n      local exit = self.infeasible_rows[count]\n      self.infeasible_rows[count] = nil\n\n      local row = self.rows[exit]\n      if row and row.constant < 0.0 then\n         local entry\n         local min_ratio = math.huge\n         for var, multiplier in row:iter_vars() do\n            if multiplier > 0.0 and not var.is_dummy then\n               local r = (self.objective[var] or 0.0) / multiplier\n               if r < min_ratio then\n                  min_ratio, entry = r, var\n               end\n            end\n         end\n         assert(entry, \"dual optimize failed\")\n\n         -- pivot\n         self.rows[exit] = nil\n         row:solve_for(entry, exit)\n         substitute_out(self, entry, row)\n         self.rows[entry] = row\n      end\n   end\nend\n\n-- interface\n\nfunction SimplexSolver:hasvariable(var) return self.vars[var] end\nfunction SimplexSolver:hasconstraint(cons) return self.constraints[cons] end\nfunction SimplexSolver:hasedit(var) return self.edits[var] end\nfunction SimplexSolver.var(_, ...) return Variable.new(...) end\nfunction SimplexSolver.constraint(_, ...) return Constraint.new(...) end\n\nfunction SimplexSolver.new()\n   local self = {}\n   self.last_varid = 1\n\n   self.vars        = {}\n   self.edits       = {}\n   self.constraints = {}\n\n   self.objective       = Expression.new()\n   self.rows            = {}\n   self.infeasible_rows = {}\n\n   return setmetatable(self, SimplexSolver)\nend\n\nfunction SimplexSolver:__tostring()\n   local t = { \"amoeba.Solver: {\\n\" }\n   t[#t+1] = (\"  objective = %s\\n\"):format(self.objective:tostring())\n   if next(self.rows) then\n      t[#t+1] =  \"  rows:\\n\"\n      local keys = {}\n      for k in pairs(self.rows) do\n         keys[#keys+1] = k\n      end\n      table.sort(keys, function(a, b) return a.id < b.id end)\n      for idx, k in ipairs(keys) do local v = self.rows[k]\n         t[#t+1] = (\"    %d. %s(%g) = %s\\n\"):format(idx, k.name, k.value, v:tostring())\n      end\n   end\n   if next(self.edits) then\n      t[#t+1] = \"  edits:\\n\"\n      local idx = 1\n      for k, v in pairs(self.edits) do\n         t[#t+1] = (\"    %d. %s = %s; info = { %s, %s, %g }\\n\"):format(\n            idx, k.name, k.value, v.plus.name, v.minus.name,\n            v.prev_constant)\n         idx = idx + 1\n      end\n   end\n   if #self.infeasible_rows ~= 0 then\n      t[#t+1] = \"  infeasible_rows: {\"\n      for _, var in ipairs(self.infeasible_rows) do\n         t[#t+1] = (\" %s\"):format(var.name)\n      end\n      t[#t+1] = \" }\\n\"\n   end\n   if #self.vars ~= 0 then\n      t[#t+1] = \" vars: {\"\n      for var in pairs(self.vars) do\n         t[#t+1] = (\" %s\"):format(var.name)\n      end\n      t[#t+1] = \" }\\n\"\n   end\n   t[#t+1] = \"}\"\n   return table.concat(t)\nend\n\nfunction SimplexSolver:addconstraint(cons, ...)\n   if getmetatable(cons) ~= Constraint then\n      cons = Constraint.new(cons, ...)\n   end\n   if self.constraints[cons] then return cons end\n   local expr, var1, var2 = make_expression(self, cons)\n   local subject, err = choose_subject(self, expr, var1, var2)\n   if subject then\n      expr:solve_for(subject)\n      substitute_out(self, subject, expr)\n      self.rows[subject] = expr\n   elseif err then\n      return nil, err\n   elseif not add_with_artificial_variable(self, expr) then\n      return nil, \"constraint added may unbounded\"\n   end\n   self.constraints[cons] = {\n      marker = var1,\n      other = var2,\n   }\n   cons.solver = self\n   optimize(self)\n   update_external_variables(self)\n   return cons\nend\n\nfunction SimplexSolver:delconstraint(cons)\n   local info = self.constraints[cons]\n   if not info then return end\n   self.constraints[cons] = nil\n\n   if info.marker and info.marker.is_error then\n      self.objective:add(self.rows[info.marker] or info.marker, -cons.weight)\n   end\n   if info.other and info.other.is_error then\n      self.objective:add(self.rows[info.other] or info.other, -cons.weight)\n   end\n   if self.objective:is_constant() then\n      self.objective.constant = 0.0\n   end\n\n   local row = self.rows[info.marker]\n   if row then\n      self.rows[info.marker] = nil\n   else\n      local var = assert(get_marker_leaving_row(self, info.marker),\n                         \"failed to find leaving row\")\n      row = self.rows[var]\n      self.rows[var] = nil\n      row:solve_for(info.marker, var)\n      substitute_out(self, info.marker, row)\n   end\n   cons.solver = nil\n   optimize(self)\n   update_external_variables(self)\n   return cons\nend\n\nfunction SimplexSolver:addedit(var, strength)\n   if self.edits[var] then return end\n   strength = strength or Constraint.MEDIUM\n   assert(strength < Constraint.REQUIRED, \"attempt to edit a required var\")\n   local cons = Constraint.new(\"==\", var, var.value, strength)\n   assert(self:addconstraint(cons))\n   local info = self.constraints[cons]\n   self.edits[var] = {\n      constraint = cons,\n      plus = info.marker,\n      minus = info.other,\n      prev_constant = var.value or 0.0,\n   }\n   return self\nend\n\nfunction SimplexSolver:deledit(var)\n   local info = self.edits[var]\n   if info then\n      self:delconstraint(info.constraint)\n      self.edits[var] = nil\n   end\nend\n\nfunction SimplexSolver:suggest(var, value)\n   local info = self.edits[var]\n   if not info then self:addedit(var); info = self.edits[var] end\n   local delta = value - info.prev_constant\n   info.prev_constant = value\n   delta_edit_constant(self, delta, info.plus, info.minus)\n   dual_optimize(self)\n   update_external_variables(self)\nend\n\nfunction SimplexSolver:setstrength(cons, strength)\n   local info = self.constraints[cons]\n   if not info then cons.weight = strength end\n   assert(info.marker and info.marker.is_error, \"attempt to change required strength\")\n   local multiplier = strength / cons.strength\n   cons.weight = strength\n   self.is_required = self.weight >= Constraint.REQUIRED\n   if near_zero(diff) then return self end\n\n   self.objective:add(self.rows[info.marker] or info.marker, multiplier)\n   self.objective:add(self.rows[info.other] or info.other, multiplier)\n   optimize(self)\n   update_external_variables(self)\n   return self\nend\n\nfunction SimplexSolver:resolve()\n   dual_optimize(self)\n   set_external_variables()\n   reset_stay_constant(self)\n   self.infeasible_rows = {}\nend\n\nfunction SimplexSolver:set_constant(cons, constant)\n   local info = self.constraints[cons]\n   if not info then return end\n   local delta = info.prev_constant - constant\n   info.prev_constant = constant\n\n   if info.marker.is_slack or cons.is_required then\n      for var, row in pairs(self.rows) do\n         row:add((row[info.marker] or 0.0) * -delta)\n         if var.is_restricted and row.constant < 0.0 then\n            self.infeasible_rows[#self.infeasible_rows+1] = var\n         end\n      end\n   else\n      delta_edit_constant(self, delta, info.marker, info.other)\n   end\n   dual_optimize(self)\n   update_external_variables(self)\nend\n\nend\n\nreturn SimplexSolver\n"
  },
  {
    "path": "enaml_like_benchmark.cpp",
    "content": "/*-----------------------------------------------------------------------------\n| Copyright (c) 2020, Nucleic Development Team.\n|\n| Distributed under the terms of the Modified BSD License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n\n// Time updating an EditVariable in a set of constraints typical of enaml use.\n\n#define ANKERL_NANOBENCH_IMPLEMENT\n#include \"nanobench.h\"\n\n#define AM_IMPLEMENTATION\n#include \"amoeba.h\"\n\nstatic void build_solver(am_Solver* S, am_Id width, am_Id height, am_Num *values) {\n    /* Create custom strength */\n    am_Num mmedium = AM_MEDIUM * 1.25;\n    am_Num smedium = AM_MEDIUM * 100;\n\n    /* Create the variable */\n    am_Id left            = am_newvariable(S, &values[0]);\n    am_Id top             = am_newvariable(S, &values[1]);\n    am_Id contents_top    = am_newvariable(S, &values[2]);\n    am_Id contents_bottom = am_newvariable(S, &values[3]);\n    am_Id contents_left   = am_newvariable(S, &values[4]);\n    am_Id contents_right  = am_newvariable(S, &values[5]);\n    am_Id midline         = am_newvariable(S, &values[6]);\n    am_Id ctleft          = am_newvariable(S, &values[7]);\n    am_Id ctheight        = am_newvariable(S, &values[8]);\n    am_Id cttop           = am_newvariable(S, &values[9]);\n    am_Id ctwidth         = am_newvariable(S, &values[10]);\n    am_Id lb1left         = am_newvariable(S, &values[11]);\n    am_Id lb1height       = am_newvariable(S, &values[12]);\n    am_Id lb1top          = am_newvariable(S, &values[13]);\n    am_Id lb1width        = am_newvariable(S, &values[14]);\n    am_Id lb2left         = am_newvariable(S, &values[15]);\n    am_Id lb2height       = am_newvariable(S, &values[16]);\n    am_Id lb2top          = am_newvariable(S, &values[17]);\n    am_Id lb2width        = am_newvariable(S, &values[18]);\n    am_Id lb3left         = am_newvariable(S, &values[19]);\n    am_Id lb3height       = am_newvariable(S, &values[20]);\n    am_Id lb3top          = am_newvariable(S, &values[21]);\n    am_Id lb3width        = am_newvariable(S, &values[22]);\n    am_Id fl1left         = am_newvariable(S, &values[23]);\n    am_Id fl1height       = am_newvariable(S, &values[24]);\n    am_Id fl1top          = am_newvariable(S, &values[25]);\n    am_Id fl1width        = am_newvariable(S, &values[26]);\n    am_Id fl2left         = am_newvariable(S, &values[27]);\n    am_Id fl2height       = am_newvariable(S, &values[28]);\n    am_Id fl2top          = am_newvariable(S, &values[29]);\n    am_Id fl2width        = am_newvariable(S, &values[30]);\n    am_Id fl3left         = am_newvariable(S, &values[31]);\n    am_Id fl3height       = am_newvariable(S, &values[32]);\n    am_Id fl3top          = am_newvariable(S, &values[33]);\n    am_Id fl3width        = am_newvariable(S, &values[34]);\n\n#ifdef __GNUC__\n# pragma GCC diagnostic push\n# pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\n# pragma GCC diagnostic ignored \"-Wc99-extensions\"\n#endif\n\n    /* Add the constraints */\n    const struct Info {\n        struct Item {\n            am_Id  var;\n            am_Num mul;\n        } term[5];\n        am_Num constant;\n        int    relation;\n        am_Num strength;\n    } constraints[] = {\n        { {{left}},                                                -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{height}},                                              0,            AM_EQUAL,      AM_MEDIUM   },\n        { {{top}},                                                 -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{width}},                                               -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{height}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{top,-1},{contents_top}},                               -10,          AM_EQUAL,      AM_REQUIRED },\n        { {{lb3height}},                                           -16,          AM_EQUAL,      AM_STRONG   },\n        { {{lb3height}},                                           -16,          AM_GREATEQUAL, AM_STRONG   },\n        { {{ctleft}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{cttop}},                                               -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctwidth}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3left}},                                             0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight}},                                            -24,          AM_GREATEQUAL, smedium     },\n        { {{ctwidth}},                                             -1.67772e+07, AM_LESSEQUAL,  smedium     },\n        { {{ctheight}},                                            -24,          AM_LESSEQUAL,  smedium     },\n        { {{fl3top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1width}},                                            -67,          AM_EQUAL,      AM_WEAK     },\n        { {{lb2width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2width}},                                            -125,         AM_GREATEQUAL, AM_STRONG   },\n        { {{fl2height}},                                           -21,          AM_EQUAL,      AM_STRONG   },\n        { {{fl2height}},                                           -21,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb3top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1width}},                                            -67,          AM_GREATEQUAL, AM_STRONG   },\n        { {{fl2left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2width}},                                            -66,          AM_EQUAL,      AM_WEAK     },\n        { {{lb2width}},                                            -66,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb2height}},                                           -16,          AM_EQUAL,      AM_STRONG   },\n        { {{fl1height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top,-1},{lb3top},{lb2height,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{lb3top,-1},{lb3height,-1},{fl3top}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3top,-1},{lb3height,-1},{fl3top}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_EQUAL,      mmedium     },\n        { {{fl1top},{contents_top,-1}},                            0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1top},{contents_top,-1}},                            0,            AM_EQUAL,      mmedium     },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{left,-1},{width,-1},{contents_right}},                 10,           AM_EQUAL,      AM_REQUIRED },\n        { {{top,-1},{height,-1},{contents_bottom}},                10,           AM_EQUAL,      AM_REQUIRED },\n        { {{left,-1},{contents_left}},                             -10,          AM_EQUAL,      AM_REQUIRED },\n        { {{lb3left},{contents_left,-1}},                          0,            AM_EQUAL,      mmedium     },\n        { {{fl1left},{midline,-1}},                                0,            AM_EQUAL,      AM_STRONG   },\n        { {{fl2left},{midline,-1}},                                0,            AM_EQUAL,      AM_STRONG   },\n        { {{ctleft},{midline,-1}},                                 0,            AM_EQUAL,      AM_STRONG   },\n        { {{fl1top},{fl1height,0.5},{lb1top,-1},{lb1height,-0.5}}, 0,            AM_EQUAL,      AM_STRONG   },\n        { {{lb1left},{contents_left,-1}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left},{contents_left,-1}},                          0,            AM_EQUAL,      mmedium     },\n        { {{lb1left,-1},{fl1left},{lb1width,-1}},                  -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left,-1},{fl1left},{lb1width,-1}},                  -10,          AM_EQUAL,      mmedium     },\n        { {{fl1left,-1},{contents_right},{fl1width,-1}},           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{width}},                                               0,            AM_EQUAL,      AM_MEDIUM   },\n        { {{fl1top,-1},{fl2top},{fl1height,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1top,-1},{fl2top},{fl1height,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{cttop},{fl2top,-1},{fl2height,-1}},                    -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight,-1},{cttop,-1},{fl3top}},                     -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{cttop},{fl2top,-1},{fl2height,-1}},                    -10,          AM_EQUAL,      mmedium     },\n        { {{fl1left,-1},{contents_right},{fl1width,-1}},           -0,           AM_EQUAL,      mmedium     },\n        { {{lb2top,-1},{lb2height,-0.5},{fl2top},{fl2height,0.5}}, 0,            AM_EQUAL,      AM_STRONG   },\n        { {{contents_left,-1},{lb2left}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{contents_left,-1},{lb2left}},                          0,            AM_EQUAL,      mmedium     },\n        { {{fl2left},{lb2width,-1},{lb2left,-1}},                  -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight,-1},{cttop,-1},{fl3top}},                     -10,          AM_EQUAL,      mmedium     },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_EQUAL,      mmedium     },\n        { {{lb1top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2left},{lb2width,-1},{lb2left,-1}},                  -10,          AM_EQUAL,      mmedium     },\n        { {{fl2left,-1},{fl2width,-1},{contents_right}},           -0,           AM_EQUAL,      mmedium     },\n        { {{fl2left,-1},{fl2width,-1},{contents_right}},           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3left},{contents_left,-1}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight,0.5},{cttop},{lb3top,-1},{lb3height,-0.5}},   0,            AM_EQUAL,      AM_STRONG   },\n        { {{ctleft},{lb3left,-1},{lb3width,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctwidth,-1},{ctleft,-1},{contents_right}},             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctleft},{lb3left,-1},{lb3width,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{fl3left},{contents_left,-1}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3left},{contents_left,-1}},                          0,            AM_EQUAL,      mmedium     },\n        { {{ctwidth,-1},{ctleft,-1},{contents_right}},             -0,           AM_EQUAL,      mmedium     },\n        { {{fl3left,-1},{contents_right},{fl3width,-1}},           -0,           AM_EQUAL,      mmedium     },\n        { {{contents_top,-1},{lb1top}},                            0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{contents_top,-1},{lb1top}},                            0,            AM_EQUAL,      mmedium     },\n        { {{fl3left,-1},{contents_right},{fl3width,-1}},           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top},{lb1top,-1},{lb1height,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top,-1},{lb3top},{lb2height,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top},{lb1top,-1},{lb1height,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{fl1height}},                                           -21,          AM_EQUAL,      AM_STRONG   },\n        { {{fl1height}},                                           -21,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb2left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2height}},                                           -16,          AM_GREATEQUAL, AM_STRONG   },\n        { {{fl2top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1height}},                                           -16,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb1height}},                                           -16,          AM_EQUAL,      AM_STRONG   },\n        { {{fl3width}},                                            -125,         AM_GREATEQUAL, AM_STRONG   },\n        { {{fl3height}},                                           -21,          AM_EQUAL,      AM_STRONG   },\n        { {{fl3height}},                                           -21,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb3height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctwidth}},                                             -119,         AM_GREATEQUAL, smedium     },\n        { {{lb3width}},                                            -24,          AM_EQUAL,      AM_WEAK     },\n        { {{lb3width}},                                            -24,          AM_GREATEQUAL, AM_STRONG   },\n        { {{fl1width}},                                            -125,         AM_GREATEQUAL, AM_STRONG   },\n    };\n\n#ifdef __GNUC__\n# pragma GCC diagnostic pop\n#endif\n\n    size_t i;\n    int ret;\n\n    /* Add the edit variables */\n    ret = am_addedit(S, width, AM_STRONG);\n    assert(ret == AM_OK), (void)ret;\n    ret = am_addedit(S, height, AM_STRONG);\n    assert(ret == AM_OK), (void)ret;\n\n    for (i = 0; i < sizeof(constraints)/sizeof(constraints[0]); ++i) {\n        am_Constraint *c = am_newconstraint(S, constraints[i].strength);\n        const struct Info::Item *p;\n        for (p = constraints[i].term; p->var; ++p) {\n            ret = am_addterm(c, p->var, p->mul ? p->mul : 1);\n            assert(ret == AM_OK), (void)ret;\n        }\n        ret = am_addconstant(c, constraints[i].constant);\n        assert(ret == AM_OK), (void)ret;\n        ret = am_setrelation(c, constraints[i].relation);\n        assert(ret == AM_OK), (void)ret;\n        ret = am_add(c);\n        assert(ret == AM_OK), (void)ret;\n    }\n}\n\nint main() {\n    am_Num values[35];\n    am_Solver *S;\n\n    // demo how to use a memory pool across solvers.\n    am_MemPool conspool; am_initpool(&conspool, sizeof(am_Constraint));\n    const size_t hash_size = (sizeof(am_Num) + sizeof(am_Key)) * 8;\n    am_MemPool hashpool; am_initpool(&hashpool, hash_size);\n    auto alloc = [&](void *ptr, size_t ns, size_t os, am_AllocType ty) {\n        if (ty == am_AllocConstraint) {\n            if (ns) return am_poolalloc(&conspool);\n            return am_poolfree(&conspool, ptr), (void*)0;\n        }\n        if (ty == am_AllocHash) {\n            if (ns == hash_size && os == 0) return am_poolalloc(&hashpool);\n            if (os == hash_size && ns == 0) return am_poolfree(&hashpool, ptr), (void*)0;\n        }\n        if (ns) return realloc(ptr, ns);\n        return free(ptr), (void*)0;\n    };\n    auto alloc_func = [](void** pud, void *ptr, size_t ns, size_t os, am_AllocType ty) {\n        const auto func = &decltype(alloc)::operator();\n        auto alloc_ptr = *(decltype(alloc)**)(pud);\n        return (alloc_ptr->*func)(ptr, ns, os, ty);\n    };\n\n#if !DISABLE_BUILD\n    ankerl::nanobench::Bench().minEpochIterations(100).run(\"building solver\", [&] {\n        am_Solver *S = am_newsolver(alloc_func, &alloc);\n        am_Num w, h;\n        am_Id width = am_newvariable(S, &w);\n        am_Id height = am_newvariable(S, &h);\n        build_solver(S, width, height, values);\n        ankerl::nanobench::doNotOptimizeAway(S); //< prevent the compiler to optimize away the S\n        am_delsolver(S);\n    });\n#endif /* !DISABLE_BUILD */\n\n#if !DISABLE_LOAD\n    std::vector<char> buf; \n    {\n        am_Num w, h;\n        S = am_newsolver(NULL, NULL);\n        am_Id width = am_newvariable(S, &w);\n        am_Id height = am_newvariable(S, &h);\n        build_solver(S, width, height, values);\n\n        struct MyDumper {\n            am_Dumper base;\n            std::vector<char> *buf;\n        };\n        MyDumper d;\n        d.buf = &buf;\n        d.base.var_name = [](am_Dumper*, unsigned idx, am_Id, am_Num*) {\n            return idx == 0 ? \"width\" : idx == 1 ? \"height\" : NULL;\n        };\n        d.base.cons_name = nullptr;\n        d.base.writer = [](am_Dumper* rd, const void *buf, size_t len) {\n            MyDumper *d = (MyDumper*)rd;\n            d->buf->insert(d->buf->end(), (char*)buf, (char*)buf+len);\n            return AM_OK;\n        };\n        int ret = am_dump(S, &d.base);\n        assert(ret == AM_OK), (void)ret;\n        am_delsolver(S);\n    }\n\n    ankerl::nanobench::Bench().minEpochIterations(10000).run(\"load solver\", [&] {\n        struct MyLoader {\n            am_Loader base;\n            am_Num values[37];\n            std::vector<char> *buf;\n        };\n        MyLoader l;\n        l.buf = &buf;\n        l.base.load_var = [](am_Loader* rl, const char*, unsigned idx, am_Id) {\n            MyLoader *l = (MyLoader*)rl;\n            return &l->values[idx];\n        };\n        l.base.load_cons = nullptr;\n        l.base.reader = [](am_Loader* rl, size_t *plen) {\n            MyLoader *l = (MyLoader*)rl;\n            std::vector<char> *buf = l->buf;\n            if (buf) {\n                *plen = buf->size();\n                l->buf = nullptr;\n                return (const char*)buf->data();\n            }\n            return (const char*)nullptr;\n        };\n        am_Solver *S = am_newsolver(alloc_func, &alloc);\n        int ret = am_load(S, &l.base);\n        assert(ret == AM_OK), (void)ret;\n        am_delsolver(S);\n    });\n#endif /* !DISABLE_LOAD */\n\n#if !DISABLE_SUGGEST\n    struct Size {\n        int width;\n        int height;\n    };\n\n    Size sizes[] = {\n        { 400, 600 },\n        { 600, 400 },\n        { 800, 1200 },\n        { 1200, 800 },\n        { 400, 800 },\n        { 800, 400 }\n    };\n\n    S = am_newsolver(NULL, NULL);\n    am_Num width, height;\n    am_Id widthVar = am_newvariable(S, &width);\n    am_Id heightVar = am_newvariable(S, &height);\n    build_solver(S, widthVar, heightVar, values);\n\n    for (const Size& size : sizes) {\n        am_Num width = (am_Num)size.width;\n        am_Num height = (am_Num)size.height;\n\n        ankerl::nanobench::Bench().minEpochIterations(100000).run(\"suggest value \" + std::to_string(size.width) + \"x\" + std::to_string(size.height), [&] {\n            am_suggest(S, widthVar, width);\n            am_suggest(S, heightVar, height);\n            am_updatevars(S);\n        });\n    }\n\n    ankerl::nanobench::doNotOptimizeAway(width);\n    ankerl::nanobench::doNotOptimizeAway(height);\n\n    am_delsolver(S);\n#endif /* !DISABLE_SUGGEST */\n\n    am_freepool(&conspool);\n    am_freepool(&hashpool);\n    return 0;\n}\n\n// cc: flags+='-ggdb -O3 -DNDEBUG'\n"
  },
  {
    "path": "lua_amoeba.c",
    "content": "#define LUA_LIB\n#include <lua.h>\n#include <lauxlib.h>\n#include <string.h>\n\n#define AM_STATIC_API\n#include \"amoeba.h\"\n\n#define AML_SOLVER_TYPE \"amoeba.Solver\"\n#define AML_VAR_TYPE    \"amoeba.Variable\"\n#define AML_CONS_TYPE   \"amoeba.Constraint\"\n\nenum aml_ItemType { AML_VAR, AML_CONS, AML_CONSTANT };\n\ntypedef struct aml_Solver {\n    am_Solver *solver;\n    int        ref_vars;\n    int        ref_cons;\n} aml_Solver;\n\ntypedef struct aml_Var {\n    am_Num      value;\n    am_Id       var;\n    aml_Solver *S;\n    const char *name;\n} aml_Var;\n\ntypedef struct aml_Cons {\n    am_Constraint *cons;\n} aml_Cons;\n\ntypedef struct aml_Item {\n    int            type;\n    am_Id          var;\n    am_Constraint *cons;\n    am_Num         value;\n} aml_Item;\n\n/* utils */\n\nstatic int aml_argferror(lua_State *L, int idx, const char *fmt, ...) {\n    va_list l;\n    va_start(l, fmt);\n    lua_pushvfstring(L, fmt, l);\n    va_end(l);\n    return luaL_argerror(L, idx, lua_tostring(L, -1));\n}\n\nstatic int aml_typeerror(lua_State *L, int idx, const char *tname) {\n    return aml_argferror(L, idx, \"%s expected, got %s\",\n            tname, luaL_typename(L, idx));\n}\n\nstatic void aml_setweak(lua_State *L, const char *mode) {\n    lua_createtable(L, 0, 1);\n    lua_pushstring(L, mode);\n    lua_setfield(L, -2, \"__mode\");\n    lua_setmetatable(L, -2);\n}\n\nstatic am_Id aml_checkvar(lua_State *L, aml_Solver *S, int idx) {\n    aml_Var *lvar = (aml_Var*)luaL_testudata(L, idx, AML_VAR_TYPE);\n    const char *name;\n    if (lvar != NULL) {\n        if (lvar->var == 0) luaL_argerror(L, idx, \"invalid variable\");\n        return lvar->var;\n    }\n    name = luaL_checkstring(L, idx);\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n    if (lua_getfield(L, -2, name) == LUA_TUSERDATA)\n        return lua_remove(L, -2), aml_checkvar(L, S, -1);\n    lua_pop(L, 2);\n    return aml_argferror(L, idx, \"variable named '%s' not exists\",\n            lua_tostring(L, idx));\n}\n\nstatic aml_Cons *aml_registercons(lua_State *L, am_Constraint *cons) {\n    aml_Cons *lcons = (aml_Cons*)lua_newuserdata(L, sizeof(aml_Cons));\n    aml_Solver *S = *(aml_Solver**)cons;\n    lcons->cons = cons;\n    luaL_setmetatable(L, AML_CONS_TYPE);\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_cons);\n    lua_pushvalue(L, -2);\n    lua_rawsetp(L, -2, lcons);\n    lua_pop(L, 1);\n    return lcons;\n}\n\nstatic aml_Cons *aml_newcons(lua_State *L, aml_Solver *S, am_Num strength) {\n    am_Constraint *cons = am_newconstraint(S->solver, strength);\n    if (cons == NULL) luaL_error(L, \"Create constraint failed\");\n    *(aml_Solver**)cons = S;\n    return aml_registercons(L, cons);\n}\n\nstatic aml_Item aml_checkitem(lua_State *L, aml_Solver *S, int idx) {\n    aml_Item item = { 0 };\n    aml_Cons *lcons;\n    aml_Var  *lvar;\n    switch (lua_type(L, idx)) {\n    case LUA_TSTRING:\n        item.var = aml_checkvar(L, S, idx);\n        item.type = AML_VAR;\n        return item;\n    case LUA_TNUMBER:\n        item.value = lua_tonumber(L, idx);\n        item.type  = AML_CONSTANT;\n        return item;\n    case LUA_TUSERDATA:\n        lcons = (aml_Cons*)luaL_testudata(L, idx, AML_CONS_TYPE);\n        if (lcons) {\n            if (lcons->cons == NULL) luaL_argerror(L, idx, \"invalid constraint\");\n            item.cons  = lcons->cons;\n            item.type  = AML_CONS;\n            return item;\n        }\n        lvar = luaL_testudata(L, idx, AML_VAR_TYPE);\n        if (lvar) {\n            if (lvar->var == 0) luaL_argerror(L, idx, \"invalid variable\");\n            item.var = lvar->var;\n            item.type  = AML_VAR;\n            return item;\n        }\n        /* FALLTHROUGH */\n    default:\n        aml_typeerror(L, idx, \"number/string/variable/constraint\");\n    }\n    return item;\n}\n\nstatic aml_Solver *aml_checkitems(lua_State *L, int start, aml_Item *items) {\n    aml_Var *lvar;\n    aml_Cons *lcons;\n    if ((lcons = (aml_Cons*)luaL_testudata(L, start, AML_CONS_TYPE)) != NULL) {\n        aml_Solver *S = *(aml_Solver**)lcons->cons;\n        items[0].type = AML_CONS, items[0].cons = lcons->cons;\n        items[1] = aml_checkitem(L, S, start+1);\n        return S;\n    }\n    if ((lcons = (aml_Cons*)luaL_testudata(L, start+1, AML_CONS_TYPE)) != NULL) {\n        aml_Solver *S = *(aml_Solver**)lcons->cons;\n        items[1].type = AML_CONS, items[1].cons = lcons->cons;\n        items[0] = aml_checkitem(L, S, start);\n        return S;\n    }\n    if ((lvar = (aml_Var*)luaL_testudata(L, start, AML_VAR_TYPE)) != NULL) {\n        if (lvar->var == 0) luaL_argerror(L, start, \"invalid variable\");\n        items[0].type = AML_VAR, items[0].var = lvar->var;\n        items[1] = aml_checkitem(L, lvar->S, start+1);\n        return lvar->S;\n    }\n    if ((lvar = (aml_Var*)luaL_testudata(L, start+1, AML_VAR_TYPE)) != NULL) {\n        if (lvar->var == 0) luaL_argerror(L, start+1, \"invalid variable\");\n        items[1].type = AML_VAR, items[1].var = lvar->var;\n        items[0] = aml_checkitem(L, lvar->S, start);\n        return lvar->S;\n    }\n    aml_typeerror(L, start, \"variable/constraint\");\n    return NULL;\n}\n\nstatic int aml_pusherror(lua_State *L, int ret) {\n    if (ret == AM_OK) return lua_settop(L, 1), 1;\n    lua_pushnil(L);\n    switch (ret) {\n    default:             lua_pushfstring(L, \"Unknown Error: %d\", ret); break;\n    case AM_FAILED:      lua_pushliteral(L, \"FAILED\"); break;\n    case AM_UNSATISFIED: lua_pushliteral(L, \"UNSATISFIED\"); break;\n    case AM_UNBOUND:     lua_pushliteral(L, \"UNBOUND\"); break;\n    }\n    lua_pushinteger(L, ret);\n    return 3;\n}\n\nstatic void aml_performitem(lua_State *L, am_Constraint *cons, aml_Item *item, am_Num coef) {\n    int ret = AM_FAILED;\n    switch (item->type) {\n    case AML_CONSTANT: ret = am_addconstant(cons, item->value*coef); break;\n    case AML_VAR:      ret = am_addterm(cons, item->var, coef); break;\n    case AML_CONS:     ret = am_mergeconstraint(cons, item->cons, coef); break;\n    }\n    if (ret != AM_OK) aml_pusherror(L, ret), lua_pop(L, 1), lua_error(L);\n}\n\nstatic am_Num aml_checkstrength(lua_State *L, int idx, am_Num def) {\n    int type = lua_type(L, idx);\n    const char *s;\n    switch (type) {\n    case LUA_TSTRING:\n        s = lua_tostring(L, idx);\n        if (strcmp(s, \"required\") == 0) return AM_REQUIRED;\n        if (strcmp(s, \"strong\")   == 0) return AM_STRONG;\n        if (strcmp(s, \"medium\")   == 0) return AM_MEDIUM;\n        if (strcmp(s, \"weak\")     == 0) return AM_WEAK;\n        aml_argferror(L, idx, \"invalid strength value '%s'\", s);\n        break;\n    case LUA_TNONE:\n    case LUA_TNIL:    return def;\n    case LUA_TNUMBER: return lua_tonumber(L, idx);\n    }\n    aml_typeerror(L, idx, \"number/string\");\n    return 0.0f;\n}\n\nstatic int aml_checkrelation(lua_State *L, int idx) {\n    const char *op = luaL_checkstring(L, idx);\n    if (strcmp(op, \"==\") == 0)      return AM_EQUAL;\n    else if (strcmp(op, \"<=\") == 0) return AM_LESSEQUAL;\n    else if (strcmp(op, \">=\") == 0) return AM_GREATEQUAL;\n    else if (strcmp(op, \"eq\") == 0)  return AM_EQUAL;\n    else if (strcmp(op, \"le\") == 0)  return AM_LESSEQUAL;\n    else if (strcmp(op, \"ge\") == 0)  return AM_GREATEQUAL;\n    return aml_argferror(L, 2, \"invalid relation operator: '%s'\", op);\n}\n\nstatic aml_Cons *aml_makecons(lua_State *L, aml_Solver *S, int start) {\n    aml_Cons *lcons;\n    int op = aml_checkrelation(L, start);\n    am_Num strength = aml_checkstrength(L, start+3, AM_REQUIRED);\n    aml_Item items[2];\n    aml_checkitems(L, start+1, items);\n    lcons = aml_newcons(L, S, strength);\n    aml_performitem(L, lcons->cons, &items[0], 1.0f);\n    am_setrelation(lcons->cons, op);\n    aml_performitem(L, lcons->cons, &items[1], 1.0f);\n    return lcons;\n}\n\nstatic void aml_dumpkey(luaL_Buffer *B, int idx, am_Symbol sym) {\n    lua_State *L = B->L;\n    aml_Var *lvar;\n    lua_rawgeti(L, idx, sym.id);\n    lvar = (aml_Var*)luaL_testudata(L, -1, AML_VAR_TYPE);\n    lua_pop(L, 1);\n    if (lvar) luaL_addstring(B, lvar->name);\n    else {\n        int ch = 'v';\n        switch (sym.type) {\n        case AM_EXTERNAL: ch = 'v'; break;\n        case AM_SLACK:    ch = 's'; break;\n        case AM_ERROR:    ch = 'e'; break;\n        case AM_DUMMY:    ch = 'd'; break;\n        }\n        lua_pushfstring(L, \"%c%d\", ch, sym.id);\n        luaL_addvalue(B);\n    }\n}\n\nstatic void aml_dumprow(luaL_Buffer *B, int idx, am_Row *row) {\n    am_Iterator it = am_itertable(&row->terms);\n    lua_State *L = B->L;\n    lua_pushfstring(L, \"%f\", row->constant);\n    luaL_addvalue(B);\n    while ((am_nextentry(&it))) {\n        am_Num coef = *am_val(am_Num,it);\n        lua_pushfstring(L, \" %c \", coef > 0.0f ? '+' : '-');\n        luaL_addvalue(B);\n        if (coef < 0.0f) coef = -coef;\n        if (!am_approx(coef, 1.0f)) {\n            lua_pushfstring(L, \"%f*\", coef);\n            luaL_addvalue(B);\n        }\n        aml_dumpkey(B, idx, it.key);\n    }\n}\n\n/* expression */\n\nstatic int Lexpr_neg(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    aml_Solver *S = *(aml_Solver**)lcons;\n    aml_Cons *newcons = aml_newcons(L, S, AM_REQUIRED);\n    return aml_pusherror(L, am_mergeconstraint(newcons->cons, lcons->cons, -1.0f));\n}\n\nstatic int Lexpr_add(lua_State *L) {\n    aml_Cons *lcons;\n    aml_Item items[2];\n    aml_Solver *S = aml_checkitems(L, 1, items);\n    lcons = aml_newcons(L, S, AM_REQUIRED);\n    aml_performitem(L, lcons->cons, &items[0], 1.0f);\n    aml_performitem(L, lcons->cons, &items[1], 1.0f);\n    return 1;\n}\n\nstatic int Lexpr_sub(lua_State *L) {\n    aml_Cons *lcons;\n    aml_Item items[2];\n    aml_Solver *S = aml_checkitems(L, 1, items);\n    lcons = aml_newcons(L, S, AM_REQUIRED);\n    aml_performitem(L, lcons->cons, &items[0], 1.0f);\n    aml_performitem(L, lcons->cons, &items[1], -1.0f);\n    return 1;\n}\n\nstatic int Lexpr_mul(lua_State *L) {\n    aml_Item items[2];\n    aml_Solver *S = aml_checkitems(L, 1, items);\n    if (items[0].type == AML_CONSTANT) {\n        aml_Cons *lcons = aml_newcons(L, S, AM_REQUIRED);\n        aml_performitem(L, lcons->cons, &items[1], items[0].value);\n    }\n    else if (items[1].type == AML_CONSTANT) {\n        aml_Cons *lcons = aml_newcons(L, S, AM_REQUIRED);\n        aml_performitem(L, lcons->cons, &items[0], items[1].value);\n    }\n    else luaL_error(L, \"attempt to multiply two expression\");\n    return 1;\n}\n\nstatic int Lexpr_div(lua_State *L) {\n    aml_Item items[2];\n    aml_Solver *S = aml_checkitems(L, 1, items);\n    if (items[0].type == AML_CONSTANT)\n        luaL_error(L, \"attempt to divide a expression\");\n    if (items[1].type == AML_CONSTANT) {\n        aml_Cons *lcons = aml_newcons(L, S, AM_REQUIRED);\n        aml_performitem(L, lcons->cons, &items[0], 1.0f/items[1].value);\n    }\n    else luaL_error(L, \"attempt to divide two expression\");\n    return 1;\n}\n\nstatic int Lexpr_cmp(lua_State *L, int op) {\n    aml_Item items[2];\n    aml_Solver *S = aml_checkitems(L, 1, items);\n    aml_Cons *lcons = aml_newcons(L, S, AM_REQUIRED);\n    aml_performitem(L, lcons->cons, &items[0], 1.0f);\n    am_setrelation(lcons->cons, op);\n    aml_performitem(L, lcons->cons, &items[1], 1.0f);\n    return 1;\n}\n\nstatic int Lexpr_le(lua_State *L) { return Lexpr_cmp(L, AM_LESSEQUAL); }\nstatic int Lexpr_eq(lua_State *L) { return Lexpr_cmp(L, AM_EQUAL); }\nstatic int Lexpr_ge(lua_State *L) { return Lexpr_cmp(L, AM_GREATEQUAL); }\n\n/* variable */\n\nstatic aml_Var *aml_newvar(lua_State *L, aml_Solver *S, am_Id var, const char *name) {\n    aml_Var *lvar = (aml_Var*)lua_newuserdata(L, sizeof(aml_Var));\n    if (var == 0) {\n        var = am_newvariable(S->solver, &lvar->value);\n        if (var == 0) luaL_error(L, \"Create variable failed\");\n    }\n    if (name == NULL) {\n        lua_settop(L, 1);\n        lua_pushfstring(L, \"v%d\", var);\n        name = lua_tostring(L, 2);\n    }\n    lvar->S    = S;\n    lvar->var  = var;\n    lvar->name = name;\n    luaL_setmetatable(L, AML_VAR_TYPE);\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n    lua_pushvalue(L, -2);\n    lua_setfield(L, -2, name);\n    lua_pushvalue(L, -2);\n    lua_rawseti(L, -2, var);\n    lua_pop(L, 1);\n    return lvar;\n}\n\nstatic int Lvar_new(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    int type = lua_type(L, 2);\n    if (type != LUA_TNONE || type != LUA_TNIL) {\n        if (type != LUA_TSTRING && type != LUA_TNUMBER)\n            return aml_typeerror(L, 2, \"number/string\");\n        lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n        lua_pushvalue(L, 2);\n        if (lua_rawget(L, -2) != LUA_TNIL) return 1;\n        if (type == LUA_TNUMBER)\n            aml_argferror(L, 2, \"variable#%d not exists\", lua_tointeger(L, 2));\n        lua_pop(L, 1);\n    }\n    return aml_newvar(L, S, 0, lua_tostring(L, 2)), 1;\n}\n\nstatic int Lvar_delete(lua_State *L) {\n    aml_Var *lvar = (aml_Var*)luaL_checkudata(L, 1, AML_VAR_TYPE);\n    if (lvar->var == 0) return 0;\n    am_delvariable(lvar->S->solver, lvar->var);\n    lua_rawgeti(L, LUA_REGISTRYINDEX, lvar->S->ref_vars);\n    lua_pushnil(L);\n    lua_setfield(L, -2, lvar->name);\n    lvar->var  = 0;\n    lvar->name = NULL;\n    return 0;\n}\n\nstatic int Lvar_value(lua_State *L) {\n    aml_Var *lvar = (aml_Var*)luaL_checkudata(L, 1, AML_VAR_TYPE);\n    if (lvar->var == 0) luaL_argerror(L, 1, \"invalid variable\");\n    return lua_pushnumber(L, lvar->value), 1;\n}\n\nstatic int Lvar_tostring(lua_State *L) {\n    aml_Var *lvar = (aml_Var*)luaL_checkudata(L, 1, AML_VAR_TYPE);\n    if (lvar->var) lua_pushfstring(L, AML_VAR_TYPE \"(%p): %s = %f\",\n                lvar->var, lvar->name, lvar->value);\n    else lua_pushstring(L, AML_VAR_TYPE \": deleted\");\n    return 1;\n}\n\nstatic void open_variable(lua_State *L) {\n    luaL_Reg libs[] = {\n        { \"__neg\", Lexpr_neg },\n        { \"__add\", Lexpr_add },\n        { \"__sub\", Lexpr_sub },\n        { \"__mul\", Lexpr_mul },\n        { \"__div\", Lexpr_div },\n        { \"le\", Lexpr_le },\n        { \"eq\", Lexpr_eq },\n        { \"ge\", Lexpr_ge },\n        { \"__tostring\", Lvar_tostring },\n        { \"__gc\", Lvar_delete },\n#define ENTRY(name) { #name, Lvar_##name }\n        ENTRY(new),\n        ENTRY(delete),\n        ENTRY(value),\n#undef  ENTRY\n        { NULL, NULL }\n    };\n    if (luaL_newmetatable(L, AML_VAR_TYPE)) {\n        luaL_setfuncs(L, libs, 0);\n        lua_pushvalue(L, -1);\n        lua_setfield(L, -2, \"__index\");\n    }\n}\n\n/* constraint */\n\nstatic int Lcons_new(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    if (lua_gettop(L) >= 3) aml_makecons(L, S, 2);\n    else aml_newcons(L, S, aml_checkstrength(L, 2, AM_REQUIRED));\n    return 1;\n}\n\nstatic int Lcons_delete(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    aml_Solver *S = *(aml_Solver**)lcons->cons;\n    if (lcons->cons == NULL) return 0;\n    am_delconstraint(lcons->cons);\n    lcons->cons = NULL;\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n    lua_pushnil(L);\n    lua_rawsetp(L, -2, lcons);\n    return 0;\n}\n\nstatic int Lcons_reset(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    if (lcons->cons == NULL) luaL_argerror(L, 1, \"invalid constraint\");\n    am_resetconstraint(lcons->cons);\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lcons_add(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    aml_Solver *S = *(aml_Solver**)lcons;\n    aml_Item item;\n    int ret;\n    luaL_argcheck(L, lcons->cons, 1, \"invalid constraint\");\n    if (lua_type(L, 2) == LUA_TSTRING) {\n        const char *s = lua_tostring(L, 2);\n        if (s[0] == '<' || s[0] == '>' || s[0] == '=') {\n            ret = am_setrelation(lcons->cons, aml_checkrelation(L, 2));\n            if (ret != AM_OK) luaL_error(L, \"constraint has been added to solver!\");\n            return lua_settop(L, 1), 1;\n        }\n    }\n    item = aml_checkitem(L, S, 2);\n    aml_performitem(L, lcons->cons, &item, 1.0f);\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lcons_relation(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    int op = aml_checkrelation(L, 2);\n    if (lcons->cons == NULL) luaL_argerror(L, 1, \"invalid constraint\");\n    if (am_setrelation(lcons->cons, op) != AM_OK)\n        luaL_error(L, \"constraint has been added to solver!\");\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lcons_strength(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    am_Num strength = aml_checkstrength(L, 2, AM_REQUIRED);\n    if (lcons->cons == NULL) luaL_argerror(L, 1, \"invalid constraint\");\n    if (am_setstrength(lcons->cons, strength) != AM_OK)\n        luaL_error(L, \"constraint has been added to solver!\");\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lcons_tostring(lua_State *L) {\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 1, AML_CONS_TYPE);\n    aml_Solver *S = *(aml_Solver**)lcons;\n    luaL_Buffer B;\n    if (lcons->cons == NULL) {\n        lua_pushstring(L, AML_CONS_TYPE \": deleted\");\n        return 1;\n    }\n    lua_settop(L, 1);\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n    luaL_buffinit(L, &B);\n    lua_pushfstring(L, AML_CONS_TYPE \"(%p): [\", lcons->cons);\n    luaL_addvalue(&B);\n    aml_dumprow(&B, 2, &lcons->cons->expression);\n    if (lcons->cons->relation == AM_EQUAL)\n        luaL_addstring(&B, \" == 0.0]\");\n    else\n        luaL_addstring(&B, \" >= 0.0]\");\n    if (lcons->cons->marker.id != 0) {\n        luaL_addstring(&B, \"(added:\");\n        aml_dumpkey(&B, 2, lcons->cons->marker);\n        luaL_addchar(&B, '-');\n        aml_dumpkey(&B, 2, lcons->cons->other);\n        luaL_addchar(&B, ')');\n    }\n    luaL_pushresult(&B);\n    return 1;\n}\n\nstatic void open_constraint(lua_State *L) {\n    luaL_Reg libs[] = {\n        { \"__call\", Lcons_add },\n        { \"__neg\", Lexpr_neg },\n        { \"__add\", Lexpr_add },\n        { \"__sub\", Lexpr_sub },\n        { \"__mul\", Lexpr_mul },\n        { \"__div\", Lexpr_div },\n        { \"le\", Lexpr_le },\n        { \"eq\", Lexpr_eq },\n        { \"ge\", Lexpr_ge },\n        { \"__bor\", Lcons_strength },\n        { \"__tostring\", Lcons_tostring },\n        { \"__gc\", Lcons_delete },\n#define ENTRY(name) { #name, Lcons_##name }\n        ENTRY(new),\n        ENTRY(delete),\n        ENTRY(reset),\n        ENTRY(add),\n        ENTRY(relation),\n        ENTRY(strength),\n#undef  ENTRY\n        { NULL, NULL }\n    };\n    if (luaL_newmetatable(L, AML_CONS_TYPE)) {\n        luaL_setfuncs(L, libs, 0);\n        lua_pushvalue(L, -1);\n        lua_setfield(L, -2, \"__index\");\n    }\n}\n\n/* dump & load */\n\ntypedef struct aml_Dumper {\n    am_Dumper base;\n    luaL_Buffer *B;\n} aml_Dumper;\n\nstatic const char *aml_varname(am_Dumper *d, unsigned idx, am_Id var, am_Num *value)\n{ return (void)d, (void)idx, (void)var, ((aml_Var*)value)->name; }\n\nstatic int aml_writer(am_Dumper *d, const void *buf, size_t len) {\n    /* Write the actually data */\n    aml_Dumper *ld = (aml_Dumper*)d;\n    luaL_addlstring(ld->B, buf, len);\n    return AM_OK;\n}\n\nstatic int Ldump(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    luaL_Buffer B;\n    aml_Dumper d;\n    int ret;\n    memset(&d, 0, sizeof(d));\n    d.base.var_name = aml_varname;\n    d.base.writer = aml_writer;\n    d.B = &B;\n    luaL_buffinit(L, &B);\n    ret = am_dump(S->solver, &d.base);\n    if (ret == AM_OK)\n        return luaL_pushresult(&B), 1;\n    return aml_pusherror(L, ret);\n}\n\ntypedef struct aml_Loader {\n    am_Loader  base;\n    lua_State  *L;\n    aml_Solver *S;\n    const char *s;\n    size_t      len;\n    int         cons_index;\n    int         loader_index;\n} aml_Loader;\n\nstatic am_Num *aml_loadvar(am_Loader *l, const char *name, unsigned idx, am_Id var) {\n    aml_Loader *ll = (aml_Loader*)l;\n    aml_Var *lvar = aml_newvar(ll->L, ll->S, var, name);\n    return (void)idx, &lvar->value;\n}\n\nstatic void aml_loadcons(am_Loader *l, const char *name, unsigned idx, am_Constraint *cons) {\n    aml_Loader *ll = (aml_Loader*)l;\n    if (ll->cons_index == 0) return;\n    (void)name;\n    *(aml_Solver**)cons = ll->S;\n    aml_registercons(ll->L, cons);\n    lua_rawseti(ll->L, ll->cons_index, idx);\n}\n\nstatic const char *aml_reader(am_Loader *l, size_t *plen) {\n    aml_Loader *ll = (aml_Loader*)l;\n    lua_State *L = ll->L;\n    if (ll->loader_index == 0) {\n        const char *p = ll->s;\n        *plen = ll->len;\n        ll->s = NULL, ll->len = 0;\n        return p;\n    }\n    luaL_checkstack(L, 2, \"too many nested functions\");\n    lua_pushvalue(L, 1);\n    lua_call(L, 0, 1);\n    if (lua_isnil(L, -1))\n        return lua_pop(L, 1), *plen = 0, NULL;\n    else if (!lua_isstring(L, -1))\n        luaL_error(L, \"reader function must return a string\");\n    lua_replace(L, ll->loader_index);  /* save string in reserved slot */\n    return lua_tolstring(L, ll->loader_index, plen);\n}\n\nstatic int Lload(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    aml_Loader ll;\n    memset(&ll, 0, sizeof(ll));\n    ll.s = lua_tolstring(L, 1, &ll.len);\n    ll.L = L, ll.S = S;\n    ll.cons_index = (!lua_isnone(L, 2) ? 2 : 0);\n    ll.base.load_var = aml_loadvar;\n    ll.base.load_cons = aml_loadcons;\n    ll.base.reader = aml_reader;\n    if (ll.s == NULL) {\n        luaL_checktype(L, 1, LUA_TFUNCTION);\n        ll.loader_index = 3;\n        lua_settop(L, 3);  /* create reserved slot */\n    }\n    return aml_pusherror(L, am_load(S->solver, &ll.base));\n}\n\n/* solver */\n\nstatic int Lnew(lua_State *L) {\n    aml_Solver *S = lua_newuserdata(L, sizeof(aml_Solver));\n    if ((S->solver = am_newsolver(NULL, NULL)) == NULL)\n        return 0;\n    lua_createtable(L, 0, 4); aml_setweak(L, \"v\");\n    S->ref_vars = luaL_ref(L, LUA_REGISTRYINDEX);\n    lua_createtable(L, 0, 4); aml_setweak(L, \"v\");\n    S->ref_cons = luaL_ref(L, LUA_REGISTRYINDEX);\n    luaL_setmetatable(L, AML_SOLVER_TYPE);\n    am_autoupdate(S->solver, lua_toboolean(L, 1));\n    return 1;\n}\n\nstatic int Ldelete(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    if (S->solver == NULL) return 0;\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n    lua_pushnil(L);\n    while (lua_next(L, -2)) {\n        aml_Var *lvar = (aml_Var*)luaL_testudata(L, -1, AML_VAR_TYPE);\n        if (lvar && lvar->var) {\n            am_delvariable(lvar->S->solver, lvar->var);\n            lvar->var  = 0, lvar->name = NULL;\n        }\n        lua_pop(L, 1);\n    }\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_cons);\n    lua_pushnil(L);\n    while (lua_next(L, -2)) {\n        aml_Cons *lcons = (aml_Cons*)luaL_testudata(L, -1, AML_CONS_TYPE);\n        if (lcons && lcons->cons) {\n            am_delconstraint(lcons->cons);\n            lcons->cons = NULL;\n        }\n        lua_pop(L, 1);\n    }\n    luaL_unref(L, LUA_REGISTRYINDEX, S->ref_vars);\n    luaL_unref(L, LUA_REGISTRYINDEX, S->ref_cons);\n    am_delsolver(S->solver);\n    S->solver = NULL;\n    return 0;\n}\n\nstatic int Ltostring(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    luaL_Buffer B;\n    lua_settop(L, 1);\n    lua_rawgeti(L, LUA_REGISTRYINDEX, S->ref_vars);\n    luaL_buffinit(L, &B);\n    lua_pushfstring(L, AML_SOLVER_TYPE \"(%p): {\", S->solver);\n    luaL_addvalue(&B);\n    luaL_addstring(&B, \"\\n  objective = \");\n    aml_dumprow(&B, 2, &S->solver->objective);\n    if (S->solver->rows.count != 0) {\n        am_Iterator it = am_itertable(&S->solver->rows);\n        int idx = 0;\n        lua_pushfstring(L, \"\\n  rows(%d):\", S->solver->rows.count);\n        luaL_addvalue(&B);\n        while (am_nextentry(&it)) {\n            am_Row *row = am_val(am_Row,it);\n            lua_pushfstring(L, \"\\n    %d. \", ++idx);\n            luaL_addvalue(&B);\n            aml_dumpkey(&B, 2, it.key);\n            luaL_addstring(&B, \" = \");\n            aml_dumprow(&B, 2, row);\n        }\n    }\n    if (S->solver->infeasible_rows.id != 0) {\n        am_Symbol key = S->solver->infeasible_rows;\n        am_Row *row = (am_Row*)am_gettable(&S->solver->rows, key.id);\n        luaL_addstring(&B, \"\\n  infeasible rows: \");\n        aml_dumpkey(&B, 2, key);\n        while (row != NULL) {\n            luaL_addstring(&B, \", \");\n            aml_dumpkey(&B, 2, key);\n            row = (am_Row*)am_gettable(&S->solver->rows, row->infeasible_next.id);\n        }\n    }\n    luaL_addstring(&B, \"\\n}\");\n    luaL_pushresult(&B);\n    return 1;\n}\n\nstatic int Lreset(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    am_resetsolver(S->solver);\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lclearedits(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    am_clearedits(S->solver);\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lautoupdate(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    am_autoupdate(S->solver, lua_toboolean(L, 2));\n    return 0;\n}\n\nstatic int Laddconstraint(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    aml_Cons *lcons = (aml_Cons*)luaL_testudata(L, 2, AML_CONS_TYPE);\n    if (lcons == NULL) lcons = aml_makecons(L, S, 2);\n    return aml_pusherror(L, am_add(lcons->cons));\n}\n\nstatic int Ldelconstraint(lua_State *L) {\n    luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    aml_Cons *lcons = (aml_Cons*)luaL_checkudata(L, 2, AML_CONS_TYPE);\n    am_remove(lcons->cons);\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Laddedit(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    am_Id  var = aml_checkvar(L, S, 2);\n    am_Num strength = aml_checkstrength(L, 3, AM_MEDIUM);\n    return aml_pusherror(L, am_addedit(S->solver, var, strength));\n}\n\nstatic int Ldeledit(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    am_Id var = aml_checkvar(L, S, 2);\n    am_deledit(S->solver, var);\n    return lua_settop(L, 1), 1;\n}\n\nstatic int Lsuggest(lua_State *L) {\n    aml_Solver *S = (aml_Solver*)luaL_checkudata(L, 1, AML_SOLVER_TYPE);\n    am_Id  var = aml_checkvar(L, S, 2);\n    am_Num value = (am_Num)luaL_checknumber(L, 3);\n    am_suggest(S->solver, var, value);\n    return lua_settop(L, 1), 1;\n}\n\nLUALIB_API int luaopen_amoeba(lua_State *L) {\n    luaL_Reg libs[] = {\n        { \"var\",        Lvar_new  },\n        { \"constraint\", Lcons_new },\n        { \"__tostring\", Ltostring },\n#define ENTRY(name) { #name, L##name }\n        ENTRY(new),\n        ENTRY(delete),\n        ENTRY(autoupdate),\n        ENTRY(reset),\n        ENTRY(addconstraint),\n        ENTRY(delconstraint),\n        ENTRY(clearedits),\n        ENTRY(addedit),\n        ENTRY(deledit),\n        ENTRY(suggest),\n        ENTRY(dump),\n        ENTRY(load),\n#undef  ENTRY\n        { NULL, NULL }\n    };\n    open_variable(L);\n    open_constraint(L);\n    if (luaL_newmetatable(L, AML_SOLVER_TYPE)) {\n        luaL_setfuncs(L, libs, 0);\n        lua_pushvalue(L, -1);\n        lua_setfield(L, -2, \"__index\");\n    }\n    return 1;\n}\n\n/* maccc: flags+='-undefined dynamic_lookup -bundle -O2' output='amoeba.so'\n * win32cc: flags+='-DLUA_BUILD_AS_DLL -shared -O3' libs+='-llua54' output='amoeba.dll' */\n\n"
  },
  {
    "path": "nanobench.h",
    "content": "//  __   _ _______ __   _  _____  ______  _______ __   _ _______ _     _\n//  | \\  | |_____| | \\  | |     | |_____] |______ | \\  | |       |_____|\n//  |  \\_| |     | |  \\_| |_____| |_____] |______ |  \\_| |_____  |     |\n//\n// Microbenchmark framework for C++11/14/17/20\n// https://github.com/martinus/nanobench\n//\n// Licensed under the MIT License <http://opensource.org/licenses/MIT>.\n// SPDX-License-Identifier: MIT\n// Copyright (c) 2019-2020 Martin Ankerl <martin.ankerl@gmail.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n#ifndef ANKERL_NANOBENCH_H_INCLUDED\n#define ANKERL_NANOBENCH_H_INCLUDED\n\n// see https://semver.org/\n#define ANKERL_NANOBENCH_VERSION_MAJOR 4 // incompatible API changes\n#define ANKERL_NANOBENCH_VERSION_MINOR 2 // backwards-compatible changes\n#define ANKERL_NANOBENCH_VERSION_PATCH 0 // backwards-compatible bug fixes\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// public facing api - as minimal as possible\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <chrono>  // high_resolution_clock\n#include <cstring> // memcpy\n#include <iosfwd>  // for std::ostream* custom output target in Config\n#include <string>  // all names\n#include <vector>  // holds all results\n\n#define ANKERL_NANOBENCH(x) ANKERL_NANOBENCH_PRIVATE_##x()\n\n#define ANKERL_NANOBENCH_PRIVATE_CXX() __cplusplus\n#define ANKERL_NANOBENCH_PRIVATE_CXX98() 199711L\n#define ANKERL_NANOBENCH_PRIVATE_CXX11() 201103L\n#define ANKERL_NANOBENCH_PRIVATE_CXX14() 201402L\n#define ANKERL_NANOBENCH_PRIVATE_CXX17() 201703L\n\n#if ANKERL_NANOBENCH(CXX) >= ANKERL_NANOBENCH(CXX17)\n#    define ANKERL_NANOBENCH_PRIVATE_NODISCARD() [[nodiscard]]\n#else\n#    define ANKERL_NANOBENCH_PRIVATE_NODISCARD()\n#endif\n\n#if defined(__clang__)\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_PUSH() \\\n        _Pragma(\"clang diagnostic push\") _Pragma(\"clang diagnostic ignored \\\"-Wpadded\\\"\")\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_POP() _Pragma(\"clang diagnostic pop\")\n#else\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_PUSH()\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_POP()\n#endif\n\n#if defined(__GNUC__)\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_PUSH() _Pragma(\"GCC diagnostic push\") _Pragma(\"GCC diagnostic ignored \\\"-Weffc++\\\"\")\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_POP() _Pragma(\"GCC diagnostic pop\")\n#else\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_PUSH()\n#    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_POP()\n#endif\n\n#if defined(ANKERL_NANOBENCH_LOG_ENABLED)\n#    include <iostream>\n#    define ANKERL_NANOBENCH_LOG(x)                                                 \\\n        do {                                                                        \\\n            std::cout << __FUNCTION__ << \"@\" << __LINE__ << \": \" << x << std::endl; \\\n        } while (0)\n#else\n#    define ANKERL_NANOBENCH_LOG(x) \\\n        do {                        \\\n        } while (0)\n#endif\n\n#if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)\n#    define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1\n#else\n#    define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0\n#endif\n\n#if defined(__clang__)\n#    define ANKERL_NANOBENCH_NO_SANITIZE(...) __attribute__((no_sanitize(__VA_ARGS__)))\n#else\n#    define ANKERL_NANOBENCH_NO_SANITIZE(...)\n#endif\n\n#if defined(_MSC_VER)\n#    define ANKERL_NANOBENCH_PRIVATE_NOINLINE() __declspec(noinline)\n#else\n#    define ANKERL_NANOBENCH_PRIVATE_NOINLINE() __attribute__((noinline))\n#endif\n\n// workaround missing \"is_trivially_copyable\" in g++ < 5.0\n// See https://stackoverflow.com/a/31798726/48181\n#if defined(__GNUC__) && __GNUC__ < 5\n#    define ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)\n#else\n#    define ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value\n#endif\n\n// declarations ///////////////////////////////////////////////////////////////////////////////////\n\nnamespace ankerl {\nnamespace nanobench {\n\nusing Clock = std::conditional<std::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock,\n                               std::chrono::steady_clock>::type;\nclass Bench;\nstruct Config;\nclass Result;\nclass Rng;\nclass BigO;\n\n/**\n * @brief Renders output from a mustache-like template and benchmark results.\n *\n * The templating facility here is heavily inspired by [mustache - logic-less templates](https://mustache.github.io/).\n * It adds a few more features that are necessary to get all of the captured data out of nanobench. Please read the\n * excellent [mustache manual](https://mustache.github.io/mustache.5.html) to see what this is all about.\n *\n * nanobench output has two nested layers, *result* and *measurement*.  Here is a hierarchy of the allowed tags:\n *\n * * `{{#result}}` Marks the begin of the result layer. Whatever comes after this will be instantiated as often as\n *   a benchmark result is available. Within it, you can use these tags:\n *\n *    * `{{title}}` See Bench::title().\n *\n *    * `{{name}}` Benchmark name, usually directly provided with Bench::run(), but can also be set with Bench::name().\n *\n *    * `{{unit}}` Unit, e.g. `byte`. Defaults to `op`, see Bench::title().\n *\n *    * `{{batch}}` Batch size, see Bench::batch().\n *\n *    * `{{complexityN}}` Value used for asymptotic complexity calculation. See Bench::complexityN().\n *\n *    * `{{epochs}}` Number of epochs, see Bench::epochs().\n *\n *    * `{{clockResolution}}` Accuracy of the clock, i.e. what's the smallest time possible to measure with the clock.\n *      For modern systems, this can be around 20 ns. This value is automatically determined by nanobench at the first\n *      benchmark that is run, and used as a static variable throughout the application's runtime.\n *\n *    * `{{clockResolutionMultiple}}` Configuration multiplier for `clockResolution`. See Bench::clockResolutionMultiple().\n *      This is the target runtime for each measurement (epoch). That means the more accurate your clock is, the faster\n *      will be the benchmark. Basing the measurement's runtime on the clock resolution is the main reason why nanobench is so fast.\n *\n *    * `{{maxEpochTime}}` Configuration for a maximum time each measurement (epoch) is allowed to take. Note that at least\n *      a single iteration will be performed, even when that takes longer than maxEpochTime. See Bench::maxEpochTime().\n *\n *    * `{{minEpochTime}}` Minimum epoch time, usually not set. See Bench::minEpochTime().\n *\n *    * `{{minEpochIterations}}` See Bench::minEpochIterations().\n *\n *    * `{{epochIterations}}` See Bench::epochIterations().\n *\n *    * `{{warmup}}` Number of iterations used before measuring starts. See Bench::warmup().\n *\n *    * `{{relative}}` True or false, depending on the setting you have used. See Bench::relative().\n *\n *    Apart from these tags, it is also possible to use some mathematical operations on the measurement data. The operations\n *    are of the form `{{command(name)}}`.  Currently `name` can be one of `elapsed`, `iterations`. If performance counters\n *    are available (currently only on current Linux systems), you also have `pagefaults`, `cpucycles`,\n *    `contextswitches`, `instructions`, `branchinstructions`, and `branchmisses`. All the measuers (except `iterations`) are\n *    provided for a single iteration (so `elapsed` is the time a single iteration took). The following tags are available:\n *\n *    * `{{median(<name>)}}` Calculate median of a measurement data set, e.g. `{{median(elapsed)}}`.\n *\n *    * `{{average(<name>)}}` Average (mean) calculation.\n *\n *    * `{{medianAbsolutePercentError(<name>)}}` Calculates MdAPE, the Median Absolute Percentage Error. The MdAPE is an excellent\n *      metric for the variation of measurements. It is more robust to outliers than the\n *      [Mean absolute percentage error (M-APE)](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error).\n *      @f[\n *       \\mathrm{MdAPE}(e) = \\mathrm{med}\\{| \\frac{e_i - \\mathrm{med}\\{e\\}}{e_i}| \\}\n *      @f]\n *      E.g. for *elapsed*: First, @f$ \\mathrm{med}\\{e\\} @f$ calculates the median by sorting and then taking the middle element\n *      of all *elapsed* measurements. This is used to calculate the absolute percentage\n *      error to this median for each measurement, as in  @f$ | \\frac{e_i - \\mathrm{med}\\{e\\}}{e_i}| @f$. All these results\n *      are sorted, and the middle value is chosen as the median absolute percent error.\n *\n *      This measurement is a bit hard to interpret, but it is very robust against outliers. E.g. a value of 5% means that half of the\n *      measurements deviate less than 5% from the median, and the other deviate more than 5% from the median.\n *\n *    * `{{sum(<name>)}}` Sums of all the measurements. E.g. `{{sum(iterations)}}` will give you the total number of iterations\n*        measured in this benchmark.\n *\n *    * `{{minimum(<name>)}}` Minimum of all measurements.\n *\n *    * `{{maximum(<name>)}}` Maximum of all measurements.\n *\n *    * `{{sumProduct(<first>, <second>)}}` Calculates the sum of the products of corresponding measures:\n *      @f[\n *          \\mathrm{sumProduct}(a,b) = \\sum_{i=1}^{n}a_i\\cdot b_i\n *      @f]\n *      E.g. to calculate total runtime of the benchmark, you multiply iterations with elapsed time for each measurement, and\n *      sum these results up:\n *      `{{sumProduct(iterations, elapsed)}}`.\n *\n *    * `{{#measurement}}` To access individual measurement results, open the begin tag for measurements.\n *\n *       * `{{elapsed}}` Average elapsed wall clock time per iteration, in seconds.\n *\n *       * `{{iterations}}` Number of iterations in the measurement. The number of iterations will fluctuate due\n *         to some applied randomness, to enhance accuracy.\n *\n *       * `{{pagefaults}}` Average number of pagefaults per iteration.\n *\n *       * `{{cpucycles}}` Average number of CPU cycles processed per iteration.\n *\n *       * `{{contextswitches}}` Average number of context switches per iteration.\n *\n *       * `{{instructions}}` Average number of retired instructions per iteration.\n *\n *       * `{{branchinstructions}}` Average number of branches executed per iteration.\n *\n *       * `{{branchmisses}}` Average number of branches that were missed per iteration.\n *\n *    * `{{/measurement}}` Ends the measurement tag.\n *\n * * `{{/result}}` Marks the end of the result layer. This is the end marker for the template part that will be instantiated\n *   for each benchmark result.\n *\n *\n *  For the layer tags *result* and *measurement* you additionally can use these special markers:\n *\n *  * ``{{#-first}}`` - Begin marker of a template that will be instantiated *only for the first* entry in the layer. Use is only\n *    allowed between the begin and end marker of the layer allowed. So between ``{{#result}}`` and ``{{/result}}``, or between\n *    ``{{#measurement}}`` and ``{{/measurement}}``. Finish the template with ``{{/-first}}``.\n *\n *  * ``{{^-first}}`` - Begin marker of a template that will be instantiated *for each except the first* entry in the layer. This,\n *    this is basically the inversion of ``{{#-first}}``. Use is only allowed between the begin and end marker of the layer allowed.\n *    So between ``{{#result}}`` and ``{{/result}}``, or between ``{{#measurement}}`` and ``{{/measurement}}``.\n *\n *  * ``{{/-first}}`` - End marker for either ``{{#-first}}`` or ``{{^-first}}``.\n *\n *  * ``{{#-last}}`` - Begin marker of a template that will be instantiated *only for the last* entry in the layer. Use is only\n *    allowed between the begin and end marker of the layer allowed. So between ``{{#result}}`` and ``{{/result}}``, or between\n *    ``{{#measurement}}`` and ``{{/measurement}}``. Finish the template with ``{{/-last}}``.\n *\n *  * ``{{^-last}}`` - Begin marker of a template that will be instantiated *for each except the last* entry in the layer. This,\n *    this is basically the inversion of ``{{#-last}}``. Use is only allowed between the begin and end marker of the layer allowed.\n *    So between ``{{#result}}`` and ``{{/result}}``, or between ``{{#measurement}}`` and ``{{/measurement}}``.\n *\n *  * ``{{/-last}}`` - End marker for either ``{{#-last}}`` or ``{{^-last}}``.\n *\n   @verbatim embed:rst\n\n   For an overview of all the possible data you can get out of nanobench, please see the tutorial at :ref:`tutorial-template-json`.\n\n   The templates that ship with nanobench are:\n\n   * :cpp:func:`templates::csv() <ankerl::nanobench::templates::csv()>`\n   * :cpp:func:`templates::json() <ankerl::nanobench::templates::json()>`\n   * :cpp:func:`templates::htmlBoxplot() <ankerl::nanobench::templates::htmlBoxplot()>`\n   * :cpp:func:`templates::pyperf() <ankerl::nanobench::templates::pyperf()>`\n\n   @endverbatim\n *\n * @param mustacheTemplate The template.\n * @param bench Benchmark, containing all the results.\n * @param out Output for the generated output.\n */\nvoid render(char const* mustacheTemplate, Bench const& bench, std::ostream& out);\nvoid render(std::string const& mustacheTemplate, Bench const& bench, std::ostream& out);\n\n/**\n * Same as render(char const* mustacheTemplate, Bench const& bench, std::ostream& out), but for when\n * you only have results available.\n *\n * @param mustacheTemplate The template.\n * @param results All the results to be used for rendering.\n * @param out Output for the generated output.\n */\nvoid render(char const* mustacheTemplate, std::vector<Result> const& results, std::ostream& out);\nvoid render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out);\n\n// Contains mustache-like templates\nnamespace templates {\n\n/*!\n  @brief CSV data for the benchmark results.\n\n  Generates a comma-separated values dataset. First line is the header, each following line is a summary of each benchmark run.\n\n  @verbatim embed:rst\n  See the tutorial at :ref:`tutorial-template-csv` for an example.\n  @endverbatim\n */\nchar const* csv() noexcept;\n\n/*!\n  @brief HTML output that uses plotly to generate an interactive boxplot chart. See the tutorial for an example output.\n\n  The output uses only the elapsed wall clock time, and displays each epoch as a single dot.\n  @verbatim embed:rst\n  See the tutorial at :ref:`tutorial-template-html` for an example.\n  @endverbatim\n\n  @see ankerl::nanobench::render()\n */\nchar const* htmlBoxplot() noexcept;\n\n/*!\n @brief Output in pyperf  compatible JSON format, which can be used for more analyzations.\n @verbatim embed:rst\n See the tutorial at :ref:`tutorial-template-pyperf` for an example how to further analyze the output.\n @endverbatim\n */\nchar const* pyperf() noexcept;\n\n/*!\n  @brief Template to generate JSON data.\n\n  The generated JSON data contains *all* data that has been generated. All times are as double values, in seconds. The output can get\n  quite large.\n  @verbatim embed:rst\n  See the tutorial at :ref:`tutorial-template-json` for an example.\n  @endverbatim\n */\nchar const* json() noexcept;\n\n} // namespace templates\n\nnamespace detail {\n\ntemplate <typename T>\nstruct PerfCountSet;\n\nclass IterationLogic;\nclass PerformanceCounters;\n\n#if ANKERL_NANOBENCH(PERF_COUNTERS)\nclass LinuxPerformanceCounters;\n#endif\n\n} // namespace detail\n} // namespace nanobench\n} // namespace ankerl\n\n// definitions ////////////////////////////////////////////////////////////////////////////////////\n\nnamespace ankerl {\nnamespace nanobench {\nnamespace detail {\n\ntemplate <typename T>\nstruct PerfCountSet {\n    T pageFaults{};\n    T cpuCycles{};\n    T contextSwitches{};\n    T instructions{};\n    T branchInstructions{};\n    T branchMisses{};\n};\n\n} // namespace detail\n\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nstruct Config {\n    // actual benchmark config\n    std::string mBenchmarkTitle = \"benchmark\";\n    std::string mBenchmarkName = \"noname\";\n    std::string mUnit = \"op\";\n    double mBatch = 1.0;\n    double mComplexityN = -1.0;\n    size_t mNumEpochs = 11;\n    size_t mClockResolutionMultiple = static_cast<size_t>(1000);\n    std::chrono::nanoseconds mMaxEpochTime = std::chrono::milliseconds(100);\n    std::chrono::nanoseconds mMinEpochTime{};\n    uint64_t mMinEpochIterations{1};\n    uint64_t mEpochIterations{0}; // If not 0, run *exactly* these number of iterations per epoch.\n    uint64_t mWarmup = 0;\n    std::ostream* mOut = nullptr;\n    std::chrono::duration<double> mTimeUnit = std::chrono::nanoseconds{1};\n    std::string mTimeUnitName = \"ns\";\n    bool mShowPerformanceCounters = true;\n    bool mIsRelative = false;\n\n    Config();\n    ~Config();\n    Config& operator=(Config const&);\n    Config& operator=(Config&&);\n    Config(Config const&);\n    Config(Config&&) noexcept;\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\n// Result returned after a benchmark has finished. Can be used as a baseline for relative().\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nclass Result {\npublic:\n    enum class Measure : size_t {\n        elapsed,\n        iterations,\n        pagefaults,\n        cpucycles,\n        contextswitches,\n        instructions,\n        branchinstructions,\n        branchmisses,\n        _size\n    };\n\n    explicit Result(Config const& benchmarkConfig);\n\n    ~Result();\n    Result& operator=(Result const&);\n    Result& operator=(Result&&);\n    Result(Result const&);\n    Result(Result&&) noexcept;\n\n    // adds new measurement results\n    // all values are scaled by iters (except iters...)\n    void add(Clock::duration totalElapsed, uint64_t iters, detail::PerformanceCounters const& pc);\n\n    ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept;\n\n    ANKERL_NANOBENCH(NODISCARD) double median(Measure m) const;\n    ANKERL_NANOBENCH(NODISCARD) double medianAbsolutePercentError(Measure m) const;\n    ANKERL_NANOBENCH(NODISCARD) double average(Measure m) const;\n    ANKERL_NANOBENCH(NODISCARD) double sum(Measure m) const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double sumProduct(Measure m1, Measure m2) const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double minimum(Measure m) const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double maximum(Measure m) const noexcept;\n\n    ANKERL_NANOBENCH(NODISCARD) bool has(Measure m) const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double get(size_t idx, Measure m) const;\n    ANKERL_NANOBENCH(NODISCARD) bool empty() const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) size_t size() const noexcept;\n\n    // Finds string, if not found, returns _size.\n    static Measure fromString(std::string const& str);\n\nprivate:\n    Config mConfig{};\n    std::vector<std::vector<double>> mNameToMeasurements{};\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\n/**\n * An extremely fast random generator. Currently, this implements *RomuDuoJr*, developed by Mark Overton. Source:\n * http://www.romu-random.org/\n *\n * RomuDuoJr is extremely fast and provides reasonable good randomness. Not enough for large jobs, but definitely\n * good enough for a benchmarking framework.\n *\n *  * Estimated capacity: @f$ 2^{51} @f$ bytes\n *  * Register pressure: 4\n *  * State size: 128 bits\n *\n * This random generator is a drop-in replacement for the generators supplied by ``<random>``. It is not\n * cryptographically secure. It's intended purpose is to be very fast so that benchmarks that make use\n * of randomness are not distorted too much by the random generator.\n *\n * Rng also provides a few non-standard helpers, optimized for speed.\n */\nclass Rng final {\npublic:\n    /**\n     * @brief This RNG provides 64bit randomness.\n     */\n    using result_type = uint64_t;\n\n    static constexpr uint64_t(min)();\n    static constexpr uint64_t(max)();\n\n    /**\n     * As a safety precausion, we don't allow copying. Copying a PRNG would mean you would have two random generators that produce the\n     * same sequence, which is generally not what one wants. Instead create a new rng with the default constructor Rng(), which is\n     * automatically seeded from `std::random_device`. If you really need a copy, use copy().\n     */\n    Rng(Rng const&) = delete;\n\n    /**\n     * Same as Rng(Rng const&), we don't allow assignment. If you need a new Rng create one with the default constructor Rng().\n     */\n    Rng& operator=(Rng const&) = delete;\n\n    // moving is ok\n    Rng(Rng&&) noexcept = default;\n    Rng& operator=(Rng&&) noexcept = default;\n    ~Rng() noexcept = default;\n\n    /**\n     * @brief Creates a new Random generator with random seed.\n     *\n     * Instead of a default seed (as the random generators from the STD), this properly seeds the random generator from\n     * `std::random_device`. It guarantees correct seeding. Note that seeding can be relatively slow, depending on the source of\n     * randomness used. So it is best to create a Rng once and use it for all your randomness purposes.\n     */\n    Rng();\n\n    /*!\n      Creates a new Rng that is seeded with a specific seed. Each Rng created from the same seed will produce the same randomness\n      sequence. This can be useful for deterministic behavior.\n\n      @verbatim embed:rst\n      .. note::\n\n         The random algorithm might change between nanobench releases. Whenever a faster and/or better random\n         generator becomes available, I will switch the implementation.\n      @endverbatim\n\n      As per the Romu paper, this seeds the Rng with splitMix64 algorithm and performs 10 initial rounds for further mixing up of the\n      internal state.\n\n      @param seed  The 64bit seed. All values are allowed, even 0.\n     */\n    explicit Rng(uint64_t seed) noexcept;\n    Rng(uint64_t x, uint64_t y) noexcept;\n\n    /**\n     * Creates a copy of the Rng, thus the copy provides exactly the same random sequence as the original.\n     */\n    ANKERL_NANOBENCH(NODISCARD) Rng copy() const noexcept;\n\n    /**\n     * @brief Produces a 64bit random value. This should be very fast, thus it is marked as inline. In my benchmark, this is ~46 times\n     * faster than `std::default_random_engine` for producing 64bit random values. It seems that the fastest std contender is\n     * `std::mt19937_64`. Still, this RNG is 2-3 times as fast.\n     *\n     * @return uint64_t The next 64 bit random value.\n     */\n    inline uint64_t operator()() noexcept;\n\n    // This is slightly biased. See\n\n    /**\n     * Generates a random number between 0 and range (excluding range).\n     *\n     * The algorithm only produces 32bit numbers, and is slightly biased. The effect is quite small unless your range is close to the\n     * maximum value of an integer. It is possible to correct the bias with rejection sampling (see\n     * [here](https://lemire.me/blog/2016/06/30/fast-random-shuffling/), but this is most likely irrelevant in practices for the\n     * purposes of this Rng.\n     *\n     * See Daniel Lemire's blog post [A fast alternative to the modulo\n     * reduction](https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/)\n     *\n     * @param range Upper exclusive range. E.g a value of 3 will generate random numbers 0, 1, 2.\n     * @return uint32_t Generated random values in range [0, range(.\n     */\n    inline uint32_t bounded(uint32_t range) noexcept;\n\n    // random double in range [0, 1(\n    // see http://prng.di.unimi.it/\n\n    /**\n     * Provides a random uniform double value between 0 and 1. This uses the method described in [Generating uniform doubles in the\n     * unit interval](http://prng.di.unimi.it/), and is extremely fast.\n     *\n     * @return double Uniformly distributed double value in range [0,1(, excluding 1.\n     */\n    inline double uniform01() noexcept;\n\n    /**\n     * Shuffles all entries in the given container. Although this has a slight bias due to the implementation of bounded(), this is\n     * preferable to `std::shuffle` because it is over 5 times faster. See Daniel Lemire's blog post [Fast random\n     * shuffling](https://lemire.me/blog/2016/06/30/fast-random-shuffling/).\n     *\n     * @param container The whole container will be shuffled.\n     */\n    template <typename Container>\n    void shuffle(Container& container) noexcept;\n\nprivate:\n    static constexpr uint64_t rotl(uint64_t x, unsigned k) noexcept;\n\n    uint64_t mX;\n    uint64_t mY;\n};\n\n/**\n * @brief Main entry point to nanobench's benchmarking facility.\n *\n * It holds configuration and results from one or more benchmark runs. Usually it is used in a single line, where the object is\n * constructed, configured, and then a benchmark is run. E.g. like this:\n *\n *     ankerl::nanobench::Bench().unit(\"byte\").batch(1000).run(\"random fluctuations\", [&] {\n *         // here be the benchmark code\n *     });\n *\n * In that example Bench() constructs the benchmark, it is then configured with unit() and batch(), and after configuration a\n * benchmark is executed with run(). Once run() has finished, it prints the result to `std::cout`. It would also store the results\n * in the Bench instance, but in this case the object is immediately destroyed so it's not available any more.\n */\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nclass Bench {\npublic:\n    /**\n     * @brief Creates a new benchmark for configuration and running of benchmarks.\n     */\n    Bench();\n\n    Bench(Bench&& other);\n    Bench& operator=(Bench&& other);\n    Bench(Bench const& other);\n    Bench& operator=(Bench const& other);\n    ~Bench() noexcept;\n\n    /*!\n      @brief Repeatedly calls `op()` based on the configuration, and performs measurements.\n\n      This call is marked with `noinline` to prevent the compiler to optimize beyond different benchmarks. This can have quite a big\n      effect on benchmark accuracy.\n\n      @verbatim embed:rst\n      .. note::\n\n        Each call to your lambda must have a side effect that the compiler can't possibly optimize it away. E.g. add a result to an\n        externally defined number (like `x` in the above example), and finally call `doNotOptimizeAway` on the variables the compiler\n        must not remove. You can also use :cpp:func:`ankerl::nanobench::doNotOptimizeAway` directly in the lambda, but be aware that\n        this has a small overhead.\n\n      @endverbatim\n\n      @tparam Op The code to benchmark.\n     */\n    template <typename Op>\n    ANKERL_NANOBENCH(NOINLINE)\n    Bench& run(char const* benchmarkName, Op&& op);\n\n    template <typename Op>\n    ANKERL_NANOBENCH(NOINLINE)\n    Bench& run(std::string const& benchmarkName, Op&& op);\n\n    /**\n     * @brief Same as run(char const* benchmarkName, Op op), but instead uses the previously set name.\n     * @tparam Op The code to benchmark.\n     */\n    template <typename Op>\n    ANKERL_NANOBENCH(NOINLINE)\n    Bench& run(Op&& op);\n\n    /**\n     * @brief Title of the benchmark, will be shown in the table header. Changing the title will start a new markdown table.\n     *\n     * @param benchmarkTitle The title of the benchmark.\n     */\n    Bench& title(char const* benchmarkTitle);\n    Bench& title(std::string const& benchmarkTitle);\n    ANKERL_NANOBENCH(NODISCARD) std::string const& title() const noexcept;\n\n    /// Name of the benchmark, will be shown in the table row.\n    Bench& name(char const* benchmarkName);\n    Bench& name(std::string const& benchmarkName);\n    ANKERL_NANOBENCH(NODISCARD) std::string const& name() const noexcept;\n\n    /**\n     * @brief Sets the batch size.\n     *\n     * E.g. number of processed byte, or some other metric for the size of the processed data in each iteration. If you benchmark\n     * hashing of a 1000 byte long string and want byte/sec as a result, you can specify 1000 as the batch size.\n     *\n     * @tparam T Any input type is internally cast to `double`.\n     * @param b batch size\n     */\n    template <typename T>\n    Bench& batch(T b) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double batch() const noexcept;\n\n    /**\n     * @brief Sets the operation unit.\n     *\n     * Defaults to \"op\". Could be e.g. \"byte\" for string processing. This is used for the table header, e.g. to show `ns/byte`. Use\n     * singular (*byte*, not *bytes*). A change clears the currently collected results.\n     *\n     * @param unit The unit name.\n     */\n    Bench& unit(char const* unit);\n    Bench& unit(std::string const& unit);\n    ANKERL_NANOBENCH(NODISCARD) std::string const& unit() const noexcept;\n\n    /**\n     * @brief Sets the time unit to be used for the default output.\n     *\n     * Nanobench defaults to using ns (nanoseconds) as output in the markdown. For some benchmarks this is too coarse, so it is\n     * possible to configure this. E.g. use `timeUnit(1ms, \"ms\")` to show `ms/op` instead of `ns/op`.\n     *\n     * @param tu Time unit to display the results in, default is 1ns.\n     * @param tuName Name for the time unit, default is \"ns\"\n     */\n    Bench& timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName);\n    ANKERL_NANOBENCH(NODISCARD) std::string const& timeUnitName() const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) std::chrono::duration<double> const& timeUnit() const noexcept;\n\n    /**\n     * @brief Set the output stream where the resulting markdown table will be printed to.\n     *\n     * The default is `&std::cout`. You can disable all output by setting `nullptr`.\n     *\n     * @param outstream Pointer to output stream, can be `nullptr`.\n     */\n    Bench& output(std::ostream* outstream) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) std::ostream* output() const noexcept;\n\n    /**\n     * Modern processors have a very accurate clock, being able to measure as low as 20 nanoseconds. This is the main trick nanobech to\n     * be so fast: we find out how accurate the clock is, then run the benchmark only so often that the clock's accuracy is good enough\n     * for accurate measurements.\n     *\n     * The default is to run one epoch for 1000 times the clock resolution. So for 20ns resolution and 11 epochs, this gives a total\n     * runtime of\n     *\n     * @f[\n     * 20ns * 1000 * 11 \\approx 0.2ms\n     * @f]\n     *\n     * To be precise, nanobench adds a 0-20% random noise to each evaluation. This is to prevent any aliasing effects, and further\n     * improves accuracy.\n     *\n     * Total runtime will be higher though: Some initial time is needed to find out the target number of iterations for each epoch, and\n     * there is some overhead involved to start & stop timers and calculate resulting statistics and writing the output.\n     *\n     * @param multiple Target number of times of clock resolution. Usually 1000 is a good compromise between runtime and accuracy.\n     */\n    Bench& clockResolutionMultiple(size_t multiple) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) size_t clockResolutionMultiple() const noexcept;\n\n    /**\n     * @brief Controls number of epochs, the number of measurements to perform.\n     *\n     * The reported result will be the median of evaluation of each epoch. The higher you choose this, the more\n     * deterministic the result be and outliers will be more easily removed. Also the `err%` will be more accurate the higher this\n     * number is. Note that the `err%` will not necessarily decrease when number of epochs is increased. But it will be a more accurate\n     * representation of the benchmarked code's runtime stability.\n     *\n     * Choose the value wisely. In practice, 11 has been shown to be a reasonable choice between runtime performance and accuracy.\n     * This setting goes hand in hand with minEpocIterations() (or minEpochTime()). If you are more interested in *median* runtime, you\n     * might want to increase epochs(). If you are more interested in *mean* runtime, you might want to increase minEpochIterations()\n     * instead.\n     *\n     * @param numEpochs Number of epochs.\n     */\n    Bench& epochs(size_t numEpochs) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) size_t epochs() const noexcept;\n\n    /**\n     * @brief Upper limit for the runtime of each epoch.\n     *\n     * As a safety precausion if the clock is not very accurate, we can set an upper limit for the maximum evaluation time per\n     * epoch. Default is 100ms. At least a single evaluation of the benchmark is performed.\n     *\n     * @see minEpochTime(), minEpochIterations()\n     *\n     * @param t Maximum target runtime for a single epoch.\n     */\n    Bench& maxEpochTime(std::chrono::nanoseconds t) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) std::chrono::nanoseconds maxEpochTime() const noexcept;\n\n    /**\n     * @brief Minimum time each epoch should take.\n     *\n     * Default is zero, so we are fully relying on clockResolutionMultiple(). In most cases this is exactly what you want. If you see\n     * that the evaluation is unreliable with a high `err%`, you can increase either minEpochTime() or minEpochIterations().\n     *\n     * @see maxEpochTime(), minEpochIterations()\n     *\n     * @param t Minimum time each epoch should take.\n     */\n    Bench& minEpochTime(std::chrono::nanoseconds t) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) std::chrono::nanoseconds minEpochTime() const noexcept;\n\n    /**\n     * @brief Sets the minimum number of iterations each epoch should take.\n     *\n     * Default is 1, and we rely on clockResolutionMultiple(). If the `err%` is high and you want a more smooth result, you might want\n     * to increase the minimum number or iterations, or increase the minEpochTime().\n     *\n     * @see minEpochTime(), maxEpochTime(), minEpochIterations()\n     *\n     * @param numIters Minimum number of iterations per epoch.\n     */\n    Bench& minEpochIterations(uint64_t numIters) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) uint64_t minEpochIterations() const noexcept;\n\n    /**\n     * Sets exactly the number of iterations for each epoch. Ignores all other epoch limits. This forces nanobench to use exactly\n     * the given number of iterations for each epoch, not more and not less. Default is 0 (disabled).\n     *\n     * @param numIters Exact number of iterations to use. Set to 0 to disable.\n     */\n    Bench& epochIterations(uint64_t numIters) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) uint64_t epochIterations() const noexcept;\n\n    /**\n     * @brief Sets a number of iterations that are initially performed without any measurements.\n     *\n     * Some benchmarks need a few evaluations to warm up caches / database / whatever access. Normally this should not be needed, since\n     * we show the median result so initial outliers will be filtered away automatically. If the warmup effect is large though, you\n     * might want to set it. Default is 0.\n     *\n     * @param numWarmupIters Number of warmup iterations.\n     */\n    Bench& warmup(uint64_t numWarmupIters) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) uint64_t warmup() const noexcept;\n\n    /**\n     * @brief Marks the next run as the baseline.\n     *\n     * Call `relative(true)` to mark the run as the baseline. Successive runs will be compared to this run. It is calculated by\n     *\n     * @f[\n     * 100\\% * \\frac{baseline}{runtime}\n     * @f]\n     *\n     *  * 100% means it is exactly as fast as the baseline\n     *  * >100% means it is faster than the baseline. E.g. 200% means the current run is twice as fast as the baseline.\n     *  * <100% means it is slower than the baseline. E.g. 50% means it is twice as slow as the baseline.\n     *\n     * See the tutorial section \"Comparing Results\" for example usage.\n     *\n     * @param isRelativeEnabled True to enable processing\n     */\n    Bench& relative(bool isRelativeEnabled) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) bool relative() const noexcept;\n\n    /**\n     * @brief Enables/disables performance counters.\n     *\n     * On Linux nanobench has a powerful feature to use performance counters. This enables counting of retired instructions, count\n     * number of branches, missed branches, etc. On default this is enabled, but you can disable it if you don't need that feature.\n     *\n     * @param showPerformanceCounters True to enable, false to disable.\n     */\n    Bench& performanceCounters(bool showPerformanceCounters) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) bool performanceCounters() const noexcept;\n\n    /**\n     * @brief Retrieves all benchmark results collected by the bench object so far.\n     *\n     * Each call to run() generates a Result that is stored within the Bench instance. This is mostly for advanced users who want to\n     * see all the nitty gritty detials.\n     *\n     * @return All results collected so far.\n     */\n    ANKERL_NANOBENCH(NODISCARD) std::vector<Result> const& results() const noexcept;\n\n    /*!\n      @verbatim embed:rst\n\n      Convenience shortcut to :cpp:func:`ankerl::nanobench::doNotOptimizeAway`.\n\n      @endverbatim\n     */\n    template <typename Arg>\n    Bench& doNotOptimizeAway(Arg&& arg);\n\n    /*!\n      @verbatim embed:rst\n\n      Sets N for asymptotic complexity calculation, so it becomes possible to calculate `Big O\n      <https://en.wikipedia.org/wiki/Big_O_notation>`_ from multiple benchmark evaluations.\n\n      Use :cpp:func:`ankerl::nanobench::Bench::complexityBigO` when the evaluation has finished. See the tutorial\n      :ref:`asymptotic-complexity` for details.\n\n      @endverbatim\n\n      @tparam T Any type is cast to `double`.\n      @param b Length of N for the next benchmark run, so it is possible to calculate `bigO`.\n     */\n    template <typename T>\n    Bench& complexityN(T b) noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double complexityN() const noexcept;\n\n    /*!\n      Calculates [Big O](https://en.wikipedia.org/wiki/Big_O_notation>) of the results with all preconfigured complexity functions.\n      Currently these complexity functions are fitted into the benchmark results:\n\n       @f$ \\mathcal{O}(1) @f$,\n       @f$ \\mathcal{O}(n) @f$,\n       @f$ \\mathcal{O}(\\log{}n) @f$,\n       @f$ \\mathcal{O}(n\\log{}n) @f$,\n       @f$ \\mathcal{O}(n^2) @f$,\n       @f$ \\mathcal{O}(n^3) @f$.\n\n      If we e.g. evaluate the complexity of `std::sort`, this is the result of `std::cout << bench.complexityBigO()`:\n\n      ```\n      |   coefficient |   err% | complexity\n      |--------------:|-------:|------------\n      |   5.08935e-09 |   2.6% | O(n log n)\n      |   6.10608e-08 |   8.0% | O(n)\n      |   1.29307e-11 |  47.2% | O(n^2)\n      |   2.48677e-15 |  69.6% | O(n^3)\n      |   9.88133e-06 | 132.3% | O(log n)\n      |   5.98793e-05 | 162.5% | O(1)\n      ```\n\n      So in this case @f$ \\mathcal{O}(n\\log{}n) @f$ provides the best approximation.\n\n      @verbatim embed:rst\n      See the tutorial :ref:`asymptotic-complexity` for details.\n      @endverbatim\n      @return Evaluation results, which can be printed or otherwise inspected.\n     */\n    std::vector<BigO> complexityBigO() const;\n\n    /**\n     * @brief Calculates bigO for a custom function.\n     *\n     * E.g. to calculate the mean squared error for @f$ \\mathcal{O}(\\log{}\\log{}n) @f$, which is not part of the default set of\n     * complexityBigO(), you can do this:\n     *\n     * ```\n     * auto logLogN = bench.complexityBigO(\"O(log log n)\", [](double n) {\n     *     return std::log2(std::log2(n));\n     * });\n     * ```\n     *\n     * The resulting mean squared error can be printed with `std::cout << logLogN`. E.g. it prints something like this:\n     *\n     * ```text\n     * 2.46985e-05 * O(log log n), rms=1.48121\n     * ```\n     *\n     * @tparam Op Type of mapping operation.\n     * @param name Name for the function, e.g. \"O(log log n)\"\n     * @param op Op's operator() maps a `double` with the desired complexity function, e.g. `log2(log2(n))`.\n     * @return BigO Error calculation, which is streamable to std::cout.\n     */\n    template <typename Op>\n    BigO complexityBigO(char const* name, Op op) const;\n\n    template <typename Op>\n    BigO complexityBigO(std::string const& name, Op op) const;\n\n    /*!\n      @verbatim embed:rst\n\n      Convenience shortcut to :cpp:func:`ankerl::nanobench::render`.\n\n      @endverbatim\n     */\n    Bench& render(char const* templateContent, std::ostream& os);\n    Bench& render(std::string const& templateContent, std::ostream& os);\n\n    Bench& config(Config const& benchmarkConfig);\n    ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept;\n\nprivate:\n    Config mConfig{};\n    std::vector<Result> mResults{};\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\n/**\n * @brief Makes sure none of the given arguments are optimized away by the compiler.\n *\n * @tparam Arg Type of the argument that shouldn't be optimized away.\n * @param arg The input that we mark as being used, even though we don't do anything with it.\n */\ntemplate <typename Arg>\nvoid doNotOptimizeAway(Arg&& arg);\n\nnamespace detail {\n\n#if defined(_MSC_VER)\nvoid doNotOptimizeAwaySink(void const*);\n\ntemplate <typename T>\nvoid doNotOptimizeAway(T const& val);\n\n#else\n\n// These assembly magic is directly from what Google Benchmark is doing. I have previously used what facebook's folly was doing, but\n// this seemd to have compilation problems in some cases. Google Benchmark seemed to be the most well tested anyways.\n// see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307\ntemplate <typename T>\nvoid doNotOptimizeAway(T const& val) {\n    // NOLINTNEXTLINE(hicpp-no-assembler)\n    asm volatile(\"\" : : \"r,m\"(val) : \"memory\");\n}\n\ntemplate <typename T>\nvoid doNotOptimizeAway(T& val) {\n#    if defined(__clang__)\n    // NOLINTNEXTLINE(hicpp-no-assembler)\n    asm volatile(\"\" : \"+r,m\"(val) : : \"memory\");\n#    else\n    // NOLINTNEXTLINE(hicpp-no-assembler)\n    asm volatile(\"\" : \"+m,r\"(val) : : \"memory\");\n#    endif\n}\n#endif\n\n// internally used, but visible because run() is templated.\n// Not movable/copy-able, so we simply use a pointer instead of unique_ptr. This saves us from\n// having to include <memory>, and the template instantiation overhead of unique_ptr which is unfortunately quite significant.\nANKERL_NANOBENCH(IGNORE_EFFCPP_PUSH)\nclass IterationLogic {\npublic:\n    explicit IterationLogic(Bench const& config) noexcept;\n    ~IterationLogic();\n\n    ANKERL_NANOBENCH(NODISCARD) uint64_t numIters() const noexcept;\n    void add(std::chrono::nanoseconds elapsed, PerformanceCounters const& pc) noexcept;\n    void moveResultTo(std::vector<Result>& results) noexcept;\n\nprivate:\n    struct Impl;\n    Impl* mPimpl;\n};\nANKERL_NANOBENCH(IGNORE_EFFCPP_POP)\n\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nclass PerformanceCounters {\npublic:\n    PerformanceCounters(PerformanceCounters const&) = delete;\n    PerformanceCounters& operator=(PerformanceCounters const&) = delete;\n\n    PerformanceCounters();\n    ~PerformanceCounters();\n\n    void beginMeasure();\n    void endMeasure();\n    void updateResults(uint64_t numIters);\n\n    ANKERL_NANOBENCH(NODISCARD) PerfCountSet<uint64_t> const& val() const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) PerfCountSet<bool> const& has() const noexcept;\n\nprivate:\n#if ANKERL_NANOBENCH(PERF_COUNTERS)\n    LinuxPerformanceCounters* mPc = nullptr;\n#endif\n    PerfCountSet<uint64_t> mVal{};\n    PerfCountSet<bool> mHas{};\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\n// Gets the singleton\nPerformanceCounters& performanceCounters();\n\n} // namespace detail\n\nclass BigO {\npublic:\n    using RangeMeasure = std::vector<std::pair<double, double>>;\n\n    template <typename Op>\n    static RangeMeasure mapRangeMeasure(RangeMeasure data, Op op) {\n        for (auto& rangeMeasure : data) {\n            rangeMeasure.first = op(rangeMeasure.first);\n        }\n        return data;\n    }\n\n    static RangeMeasure collectRangeMeasure(std::vector<Result> const& results);\n\n    template <typename Op>\n    BigO(char const* bigOName, RangeMeasure const& rangeMeasure, Op rangeToN)\n        : BigO(bigOName, mapRangeMeasure(rangeMeasure, rangeToN)) {}\n\n    template <typename Op>\n    BigO(std::string const& bigOName, RangeMeasure const& rangeMeasure, Op rangeToN)\n        : BigO(bigOName, mapRangeMeasure(rangeMeasure, rangeToN)) {}\n\n    BigO(char const* bigOName, RangeMeasure const& scaledRangeMeasure);\n    BigO(std::string const& bigOName, RangeMeasure const& scaledRangeMeasure);\n    ANKERL_NANOBENCH(NODISCARD) std::string const& name() const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double constant() const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) double normalizedRootMeanSquare() const noexcept;\n    ANKERL_NANOBENCH(NODISCARD) bool operator<(BigO const& other) const noexcept;\n\nprivate:\n    std::string mName{};\n    double mConstant{};\n    double mNormalizedRootMeanSquare{};\n};\nstd::ostream& operator<<(std::ostream& os, BigO const& bigO);\nstd::ostream& operator<<(std::ostream& os, std::vector<ankerl::nanobench::BigO> const& bigOs);\n\n} // namespace nanobench\n} // namespace ankerl\n\n// implementation /////////////////////////////////////////////////////////////////////////////////\n\nnamespace ankerl {\nnamespace nanobench {\n\nconstexpr uint64_t(Rng::min)() {\n    return 0;\n}\n\nconstexpr uint64_t(Rng::max)() {\n    return (std::numeric_limits<uint64_t>::max)();\n}\n\nANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\nuint64_t Rng::operator()() noexcept {\n    auto x = mX;\n\n    mX = UINT64_C(15241094284759029579) * mY;\n    mY = rotl(mY - x, 27);\n\n    return x;\n}\n\nANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\nuint32_t Rng::bounded(uint32_t range) noexcept {\n    uint64_t r32 = static_cast<uint32_t>(operator()());\n    auto multiresult = r32 * range;\n    return static_cast<uint32_t>(multiresult >> 32U);\n}\n\ndouble Rng::uniform01() noexcept {\n    auto i = (UINT64_C(0x3ff) << 52U) | (operator()() >> 12U);\n    // can't use union in c++ here for type puning, it's undefined behavior.\n    // std::memcpy is optimized anyways.\n    double d;\n    std::memcpy(&d, &i, sizeof(double));\n    return d - 1.0;\n}\n\ntemplate <typename Container>\nvoid Rng::shuffle(Container& container) noexcept {\n    auto size = static_cast<uint32_t>(container.size());\n    for (auto i = size; i > 1U; --i) {\n        using std::swap;\n        auto p = bounded(i); // number in [0, i)\n        swap(container[i - 1], container[p]);\n    }\n}\n\nconstexpr uint64_t Rng::rotl(uint64_t x, unsigned k) noexcept {\n    return (x << k) | (x >> (64U - k));\n}\n\ntemplate <typename Op>\nANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\nBench& Bench::run(Op&& op) {\n    // It is important that this method is kept short so the compiler can do better optimizations/ inlining of op()\n    detail::IterationLogic iterationLogic(*this);\n    auto& pc = detail::performanceCounters();\n\n    while (auto n = iterationLogic.numIters()) {\n        pc.beginMeasure();\n        Clock::time_point before = Clock::now();\n        while (n-- > 0) {\n            op();\n        }\n        Clock::time_point after = Clock::now();\n        pc.endMeasure();\n        pc.updateResults(iterationLogic.numIters());\n        iterationLogic.add(after - before, pc);\n    }\n    iterationLogic.moveResultTo(mResults);\n    return *this;\n}\n\n// Performs all evaluations.\ntemplate <typename Op>\nBench& Bench::run(char const* benchmarkName, Op&& op) {\n    name(benchmarkName);\n    return run(std::forward<Op>(op));\n}\n\ntemplate <typename Op>\nBench& Bench::run(std::string const& benchmarkName, Op&& op) {\n    name(benchmarkName);\n    return run(std::forward<Op>(op));\n}\n\ntemplate <typename Op>\nBigO Bench::complexityBigO(char const* benchmarkName, Op op) const {\n    return BigO(benchmarkName, BigO::collectRangeMeasure(mResults), op);\n}\n\ntemplate <typename Op>\nBigO Bench::complexityBigO(std::string const& benchmarkName, Op op) const {\n    return BigO(benchmarkName, BigO::collectRangeMeasure(mResults), op);\n}\n\n// Set the batch size, e.g. number of processed bytes, or some other metric for the size of the processed data in each iteration.\n// Any argument is cast to double.\ntemplate <typename T>\nBench& Bench::batch(T b) noexcept {\n    mConfig.mBatch = static_cast<double>(b);\n    return *this;\n}\n\n// Sets the computation complexity of the next run. Any argument is cast to double.\ntemplate <typename T>\nBench& Bench::complexityN(T n) noexcept {\n    mConfig.mComplexityN = static_cast<double>(n);\n    return *this;\n}\n\n// Convenience: makes sure none of the given arguments are optimized away by the compiler.\ntemplate <typename Arg>\nBench& Bench::doNotOptimizeAway(Arg&& arg) {\n    detail::doNotOptimizeAway(std::forward<Arg>(arg));\n    return *this;\n}\n\n// Makes sure none of the given arguments are optimized away by the compiler.\ntemplate <typename Arg>\nvoid doNotOptimizeAway(Arg&& arg) {\n    detail::doNotOptimizeAway(std::forward<Arg>(arg));\n}\n\nnamespace detail {\n\n#if defined(_MSC_VER)\ntemplate <typename T>\nvoid doNotOptimizeAway(T const& val) {\n    doNotOptimizeAwaySink(&val);\n}\n\n#endif\n\n} // namespace detail\n} // namespace nanobench\n} // namespace ankerl\n\n#if defined(ANKERL_NANOBENCH_IMPLEMENT)\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// implementation part - only visible in .cpp\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#    include <algorithm> // sort, reverse\n#    include <atomic>    // compare_exchange_strong in loop overhead\n#    include <cstdlib>   // getenv\n#    include <cstring>   // strstr, strncmp\n#    include <fstream>   // ifstream to parse proc files\n#    include <iomanip>   // setw, setprecision\n#    include <iostream>  // cout\n#    include <numeric>   // accumulate\n#    include <random>    // random_device\n#    include <sstream>   // to_s in Number\n#    include <stdexcept> // throw for rendering templates\n#    include <tuple>     // std::tie\n#    if defined(__linux__)\n#        include <unistd.h> //sysconf\n#    endif\n#    if ANKERL_NANOBENCH(PERF_COUNTERS)\n#        include <map> // map\n\n#        include <linux/perf_event.h>\n#        include <sys/ioctl.h>\n#        include <sys/syscall.h>\n#        include <unistd.h>\n#    endif\n\n// declarations ///////////////////////////////////////////////////////////////////////////////////\n\nnamespace ankerl {\nnamespace nanobench {\n\n// helper stuff that is only intended to be used internally\nnamespace detail {\n\nstruct TableInfo;\n\n// formatting utilities\nnamespace fmt {\n\nclass NumSep;\nclass StreamStateRestorer;\nclass Number;\nclass MarkDownColumn;\nclass MarkDownCode;\n\n} // namespace fmt\n} // namespace detail\n} // namespace nanobench\n} // namespace ankerl\n\n// definitions ////////////////////////////////////////////////////////////////////////////////////\n\nnamespace ankerl {\nnamespace nanobench {\n\nuint64_t splitMix64(uint64_t& state) noexcept;\n\nnamespace detail {\n\n// helpers to get double values\ntemplate <typename T>\ninline double d(T t) noexcept {\n    return static_cast<double>(t);\n}\ninline double d(Clock::duration duration) noexcept {\n    return std::chrono::duration_cast<std::chrono::duration<double>>(duration).count();\n}\n\n// Calculates clock resolution once, and remembers the result\ninline Clock::duration clockResolution() noexcept;\n\n} // namespace detail\n\nnamespace templates {\n\nchar const* csv() noexcept {\n    return R\"DELIM(\"title\";\"name\";\"unit\";\"batch\";\"elapsed\";\"error %\";\"instructions\";\"branches\";\"branch misses\";\"total\"\n{{#result}}\"{{title}}\";\"{{name}}\";\"{{unit}}\";{{batch}};{{median(elapsed)}};{{medianAbsolutePercentError(elapsed)}};{{median(instructions)}};{{median(branchinstructions)}};{{median(branchmisses)}};{{sumProduct(iterations, elapsed)}}\n{{/result}})DELIM\";\n}\n\nchar const* htmlBoxplot() noexcept {\n    return R\"DELIM(<html>\n\n<head>\n    <script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>\n</head>\n\n<body>\n    <div id=\"myDiv\"></div>\n    <script>\n        var data = [\n            {{#result}}{\n                name: '{{name}}',\n                y: [{{#measurement}}{{elapsed}}{{^-last}}, {{/last}}{{/measurement}}],\n            },\n            {{/result}}\n        ];\n        var title = '{{title}}';\n\n        data = data.map(a => Object.assign(a, { boxpoints: 'all', pointpos: 0, type: 'box' }));\n        var layout = { title: { text: title }, showlegend: false, yaxis: { title: 'time per unit', rangemode: 'tozero', autorange: true } }; Plotly.newPlot('myDiv', data, layout, {responsive: true});\n    </script>\n</body>\n\n</html>)DELIM\";\n}\n\nchar const* pyperf() noexcept {\n    return R\"DELIM({\n    \"benchmarks\": [\n        {\n            \"runs\": [\n                {\n                    \"values\": [\n{{#measurement}}                        {{elapsed}}{{^-last}},\n{{/last}}{{/measurement}}\n                    ]\n                }\n            ]\n        }\n    ],\n    \"metadata\": {\n        \"loops\": {{sum(iterations)}},\n        \"inner_loops\": {{batch}},\n        \"name\": \"{{title}}\",\n        \"unit\": \"second\"\n    },\n    \"version\": \"1.0\"\n})DELIM\";\n}\n\nchar const* json() noexcept {\n    return R\"DELIM({\n    \"results\": [\n{{#result}}        {\n            \"title\": \"{{title}}\",\n            \"name\": \"{{name}}\",\n            \"unit\": \"{{unit}}\",\n            \"batch\": {{batch}},\n            \"complexityN\": {{complexityN}},\n            \"epochs\": {{epochs}},\n            \"clockResolution\": {{clockResolution}},\n            \"clockResolutionMultiple\": {{clockResolutionMultiple}},\n            \"maxEpochTime\": {{maxEpochTime}},\n            \"minEpochTime\": {{minEpochTime}},\n            \"minEpochIterations\": {{minEpochIterations}},\n            \"epochIterations\": {{epochIterations}},\n            \"warmup\": {{warmup}},\n            \"relative\": {{relative}},\n            \"median(elapsed)\": {{median(elapsed)}},\n            \"medianAbsolutePercentError(elapsed)\": {{medianAbsolutePercentError(elapsed)}},\n            \"median(instructions)\": {{median(instructions)}},\n            \"medianAbsolutePercentError(instructions)\": {{medianAbsolutePercentError(instructions)}},\n            \"median(cpucycles)\": {{median(cpucycles)}},\n            \"median(contextswitches)\": {{median(contextswitches)}},\n            \"median(pagefaults)\": {{median(pagefaults)}},\n            \"median(branchinstructions)\": {{median(branchinstructions)}},\n            \"median(branchmisses)\": {{median(branchmisses)}},\n            \"totalTime\": {{sumProduct(iterations, elapsed)}},\n            \"measurements\": [\n{{#measurement}}                {\n                    \"iterations\": {{iterations}},\n                    \"elapsed\": {{elapsed}},\n                    \"pagefaults\": {{pagefaults}},\n                    \"cpucycles\": {{cpucycles}},\n                    \"contextswitches\": {{contextswitches}},\n                    \"instructions\": {{instructions}},\n                    \"branchinstructions\": {{branchinstructions}},\n                    \"branchmisses\": {{branchmisses}}\n                }{{^-last}},{{/-last}}\n{{/measurement}}            ]\n        }{{^-last}},{{/-last}}\n{{/result}}    ]\n})DELIM\";\n}\n\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nstruct Node {\n    enum class Type { tag, content, section, inverted_section };\n\n    char const* begin;\n    char const* end;\n    std::vector<Node> children;\n    Type type;\n\n    template <size_t N>\n    // NOLINTNEXTLINE(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)\n    bool operator==(char const (&str)[N]) const noexcept {\n        return static_cast<size_t>(std::distance(begin, end) + 1) == N && 0 == strncmp(str, begin, N - 1);\n    }\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\nstatic std::vector<Node> parseMustacheTemplate(char const** tpl) {\n    std::vector<Node> nodes;\n\n    while (true) {\n        auto begin = std::strstr(*tpl, \"{{\");\n        auto end = begin;\n        if (begin != nullptr) {\n            begin += 2;\n            end = std::strstr(begin, \"}}\");\n        }\n\n        if (begin == nullptr || end == nullptr) {\n            // nothing found, finish node\n            nodes.emplace_back(Node{*tpl, *tpl + std::strlen(*tpl), std::vector<Node>{}, Node::Type::content});\n            return nodes;\n        }\n\n        nodes.emplace_back(Node{*tpl, begin - 2, std::vector<Node>{}, Node::Type::content});\n\n        // we found a tag\n        *tpl = end + 2;\n        switch (*begin) {\n        case '/':\n            // finished! bail out\n            return nodes;\n\n        case '#':\n            nodes.emplace_back(Node{begin + 1, end, parseMustacheTemplate(tpl), Node::Type::section});\n            break;\n\n        case '^':\n            nodes.emplace_back(Node{begin + 1, end, parseMustacheTemplate(tpl), Node::Type::inverted_section});\n            break;\n\n        default:\n            nodes.emplace_back(Node{begin, end, std::vector<Node>{}, Node::Type::tag});\n            break;\n        }\n    }\n}\n\nstatic bool generateFirstLast(Node const& n, size_t idx, size_t size, std::ostream& out) {\n    ANKERL_NANOBENCH_LOG(\"n.type=\" << static_cast<int>(n.type));\n    bool matchFirst = n == \"-first\";\n    bool matchLast = n == \"-last\";\n    if (!matchFirst && !matchLast) {\n        return false;\n    }\n\n    bool doWrite = false;\n    if (n.type == Node::Type::section) {\n        doWrite = (matchFirst && idx == 0) || (matchLast && idx == size - 1);\n    } else if (n.type == Node::Type::inverted_section) {\n        doWrite = (matchFirst && idx != 0) || (matchLast && idx != size - 1);\n    }\n\n    if (doWrite) {\n        for (auto const& child : n.children) {\n            if (child.type == Node::Type::content) {\n                out.write(child.begin, std::distance(child.begin, child.end));\n            }\n        }\n    }\n    return true;\n}\n\nstatic bool matchCmdArgs(std::string const& str, std::vector<std::string>& matchResult) {\n    matchResult.clear();\n    auto idxOpen = str.find('(');\n    auto idxClose = str.find(')', idxOpen);\n    if (idxClose == std::string::npos) {\n        return false;\n    }\n\n    matchResult.emplace_back(str.substr(0, idxOpen));\n\n    // split by comma\n    matchResult.emplace_back(std::string{});\n    for (size_t i = idxOpen + 1; i != idxClose; ++i) {\n        if (str[i] == ' ' || str[i] == '\\t') {\n            // skip whitespace\n            continue;\n        }\n        if (str[i] == ',') {\n            // got a comma => new string\n            matchResult.emplace_back(std::string{});\n            continue;\n        }\n        // no whitespace no comma, append\n        matchResult.back() += str[i];\n    }\n    return true;\n}\n\nstatic bool generateConfigTag(Node const& n, Config const& config, std::ostream& out) {\n    using detail::d;\n\n    if (n == \"title\") {\n        out << config.mBenchmarkTitle;\n        return true;\n    } else if (n == \"name\") {\n        out << config.mBenchmarkName;\n        return true;\n    } else if (n == \"unit\") {\n        out << config.mUnit;\n        return true;\n    } else if (n == \"batch\") {\n        out << config.mBatch;\n        return true;\n    } else if (n == \"complexityN\") {\n        out << config.mComplexityN;\n        return true;\n    } else if (n == \"epochs\") {\n        out << config.mNumEpochs;\n        return true;\n    } else if (n == \"clockResolution\") {\n        out << d(detail::clockResolution());\n        return true;\n    } else if (n == \"clockResolutionMultiple\") {\n        out << config.mClockResolutionMultiple;\n        return true;\n    } else if (n == \"maxEpochTime\") {\n        out << d(config.mMaxEpochTime);\n        return true;\n    } else if (n == \"minEpochTime\") {\n        out << d(config.mMinEpochTime);\n        return true;\n    } else if (n == \"minEpochIterations\") {\n        out << config.mMinEpochIterations;\n        return true;\n    } else if (n == \"epochIterations\") {\n        out << config.mEpochIterations;\n        return true;\n    } else if (n == \"warmup\") {\n        out << config.mWarmup;\n        return true;\n    } else if (n == \"relative\") {\n        out << config.mIsRelative;\n        return true;\n    }\n    return false;\n}\n\nstatic std::ostream& generateResultTag(Node const& n, Result const& r, std::ostream& out) {\n    if (generateConfigTag(n, r.config(), out)) {\n        return out;\n    }\n    // match e.g. \"median(elapsed)\"\n    // g++ 4.8 doesn't implement std::regex :(\n    // static std::regex const regOpArg1(\"^([a-zA-Z]+)\\\\(([a-zA-Z]*)\\\\)$\");\n    // std::cmatch matchResult;\n    // if (std::regex_match(n.begin, n.end, matchResult, regOpArg1)) {\n    std::vector<std::string> matchResult;\n    if (matchCmdArgs(std::string(n.begin, n.end), matchResult)) {\n        if (matchResult.size() == 2) {\n            auto m = Result::fromString(matchResult[1]);\n            if (m == Result::Measure::_size) {\n                return out << 0.0;\n            }\n\n            if (matchResult[0] == \"median\") {\n                return out << r.median(m);\n            }\n            if (matchResult[0] == \"average\") {\n                return out << r.average(m);\n            }\n            if (matchResult[0] == \"medianAbsolutePercentError\") {\n                return out << r.medianAbsolutePercentError(m);\n            }\n            if (matchResult[0] == \"sum\") {\n                return out << r.sum(m);\n            }\n            if (matchResult[0] == \"minimum\") {\n                return out << r.minimum(m);\n            }\n            if (matchResult[0] == \"maximum\") {\n                return out << r.maximum(m);\n            }\n        } else if (matchResult.size() == 3) {\n            auto m1 = Result::fromString(matchResult[1]);\n            auto m2 = Result::fromString(matchResult[2]);\n            if (m1 == Result::Measure::_size || m2 == Result::Measure::_size) {\n                return out << 0.0;\n            }\n\n            if (matchResult[0] == \"sumProduct\") {\n                return out << r.sumProduct(m1, m2);\n            }\n        }\n    }\n\n    // match e.g. \"sumProduct(elapsed, iterations)\"\n    // static std::regex const regOpArg2(\"^([a-zA-Z]+)\\\\(([a-zA-Z]*)\\\\s*,\\\\s+([a-zA-Z]*)\\\\)$\");\n\n    // nothing matches :(\n    throw std::runtime_error(\"command '\" + std::string(n.begin, n.end) + \"' not understood\");\n}\n\nstatic void generateResultMeasurement(std::vector<Node> const& nodes, size_t idx, Result const& r, std::ostream& out) {\n    for (auto const& n : nodes) {\n        if (!generateFirstLast(n, idx, r.size(), out)) {\n            ANKERL_NANOBENCH_LOG(\"n.type=\" << static_cast<int>(n.type));\n            switch (n.type) {\n            case Node::Type::content:\n                out.write(n.begin, std::distance(n.begin, n.end));\n                break;\n\n            case Node::Type::inverted_section:\n                throw std::runtime_error(\"got a inverted section inside measurement\");\n\n            case Node::Type::section:\n                throw std::runtime_error(\"got a section inside measurement\");\n\n            case Node::Type::tag: {\n                auto m = Result::fromString(std::string(n.begin, n.end));\n                if (m == Result::Measure::_size || !r.has(m)) {\n                    out << 0.0;\n                } else {\n                    out << r.get(idx, m);\n                }\n                break;\n            }\n            }\n        }\n    }\n}\n\nstatic void generateResult(std::vector<Node> const& nodes, size_t idx, std::vector<Result> const& results, std::ostream& out) {\n    auto const& r = results[idx];\n    for (auto const& n : nodes) {\n        if (!generateFirstLast(n, idx, results.size(), out)) {\n            ANKERL_NANOBENCH_LOG(\"n.type=\" << static_cast<int>(n.type));\n            switch (n.type) {\n            case Node::Type::content:\n                out.write(n.begin, std::distance(n.begin, n.end));\n                break;\n\n            case Node::Type::inverted_section:\n                throw std::runtime_error(\"got a inverted section inside result\");\n\n            case Node::Type::section:\n                if (n == \"measurement\") {\n                    for (size_t i = 0; i < r.size(); ++i) {\n                        generateResultMeasurement(n.children, i, r, out);\n                    }\n                } else {\n                    throw std::runtime_error(\"got a section inside result\");\n                }\n                break;\n\n            case Node::Type::tag:\n                generateResultTag(n, r, out);\n                break;\n            }\n        }\n    }\n}\n\n} // namespace templates\n\n// helper stuff that only intended to be used internally\nnamespace detail {\n\nchar const* getEnv(char const* name);\nbool isEndlessRunning(std::string const& name);\nbool isWarningsEnabled();\n\ntemplate <typename T>\nT parseFile(std::string const& filename);\n\nvoid gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<std::string>& recommendations);\nvoid printStabilityInformationOnce(std::ostream* os);\n\n// remembers the last table settings used. When it changes, a new table header is automatically written for the new entry.\nuint64_t& singletonHeaderHash() noexcept;\n\n// determines resolution of the given clock. This is done by measuring multiple times and returning the minimum time difference.\nClock::duration calcClockResolution(size_t numEvaluations) noexcept;\n\n// formatting utilities\nnamespace fmt {\n\n// adds thousands separator to numbers\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nclass NumSep : public std::numpunct<char> {\npublic:\n    explicit NumSep(char sep);\n    char do_thousands_sep() const override;\n    std::string do_grouping() const override;\n\nprivate:\n    char mSep;\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\n// RAII to save & restore a stream's state\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nclass StreamStateRestorer {\npublic:\n    explicit StreamStateRestorer(std::ostream& s);\n    ~StreamStateRestorer();\n\n    // sets back all stream info that we remembered at construction\n    void restore();\n\n    // don't allow copying / moving\n    StreamStateRestorer(StreamStateRestorer const&) = delete;\n    StreamStateRestorer& operator=(StreamStateRestorer const&) = delete;\n    StreamStateRestorer(StreamStateRestorer&&) = delete;\n    StreamStateRestorer& operator=(StreamStateRestorer&&) = delete;\n\nprivate:\n    std::ostream& mStream;\n    std::locale mLocale;\n    std::streamsize const mPrecision;\n    std::streamsize const mWidth;\n    std::ostream::char_type const mFill;\n    std::ostream::fmtflags const mFmtFlags;\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\n// Number formatter\nclass Number {\npublic:\n    Number(int width, int precision, double value);\n    Number(int width, int precision, int64_t value);\n    std::string to_s() const;\n\nprivate:\n    friend std::ostream& operator<<(std::ostream& os, Number const& n);\n    std::ostream& write(std::ostream& os) const;\n\n    int mWidth;\n    int mPrecision;\n    double mValue;\n};\n\n// helper replacement for std::to_string of signed/unsigned numbers so we are locale independent\nstd::string to_s(uint64_t s);\n\nstd::ostream& operator<<(std::ostream& os, Number const& n);\n\nclass MarkDownColumn {\npublic:\n    MarkDownColumn(int w, int prec, std::string const& tit, std::string const& suff, double val);\n    std::string title() const;\n    std::string separator() const;\n    std::string invalid() const;\n    std::string value() const;\n\nprivate:\n    int mWidth;\n    int mPrecision;\n    std::string mTitle;\n    std::string mSuffix;\n    double mValue;\n};\n\n// Formats any text as markdown code, escaping backticks.\nclass MarkDownCode {\npublic:\n    explicit MarkDownCode(std::string const& what);\n\nprivate:\n    friend std::ostream& operator<<(std::ostream& os, MarkDownCode const& mdCode);\n    std::ostream& write(std::ostream& os) const;\n\n    std::string mWhat{};\n};\n\nstd::ostream& operator<<(std::ostream& os, MarkDownCode const& mdCode);\n\n} // namespace fmt\n} // namespace detail\n} // namespace nanobench\n} // namespace ankerl\n\n// implementation /////////////////////////////////////////////////////////////////////////////////\n\nnamespace ankerl {\nnamespace nanobench {\n\nvoid render(char const* mustacheTemplate, std::vector<Result> const& results, std::ostream& out) {\n    detail::fmt::StreamStateRestorer restorer(out);\n\n    out.precision(std::numeric_limits<double>::digits10);\n    auto nodes = templates::parseMustacheTemplate(&mustacheTemplate);\n\n    for (auto const& n : nodes) {\n        ANKERL_NANOBENCH_LOG(\"n.type=\" << static_cast<int>(n.type));\n        switch (n.type) {\n        case templates::Node::Type::content:\n            out.write(n.begin, std::distance(n.begin, n.end));\n            break;\n\n        case templates::Node::Type::inverted_section:\n            throw std::runtime_error(\"unknown list '\" + std::string(n.begin, n.end) + \"'\");\n\n        case templates::Node::Type::section:\n            if (n == \"result\") {\n                const size_t nbResults = results.size();\n                for (size_t i = 0; i < nbResults; ++i) {\n                    generateResult(n.children, i, results, out);\n                }\n            } else if (n == \"measurement\") {\n                if (results.size() != 1) {\n                    throw std::runtime_error(\n                        \"render: can only use section 'measurement' here if there is a single result, but there are \" +\n                        detail::fmt::to_s(results.size()));\n                }\n                // when we only have a single result, we can immediately go into its measurement.\n                auto const& r = results.front();\n                for (size_t i = 0; i < r.size(); ++i) {\n                    generateResultMeasurement(n.children, i, r, out);\n                }\n            } else {\n                throw std::runtime_error(\"render: unknown section '\" + std::string(n.begin, n.end) + \"'\");\n            }\n            break;\n\n        case templates::Node::Type::tag:\n            if (results.size() == 1) {\n                // result & config are both supported there\n                generateResultTag(n, results.front(), out);\n            } else {\n                // This just uses the last result's config.\n                if (!generateConfigTag(n, results.back().config(), out)) {\n                    throw std::runtime_error(\"unknown tag '\" + std::string(n.begin, n.end) + \"'\");\n                }\n            }\n            break;\n        }\n    }\n}\n\nvoid render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out) {\n    render(mustacheTemplate.c_str(), results, out);\n}\n\nvoid render(char const* mustacheTemplate, const Bench& bench, std::ostream& out) {\n    render(mustacheTemplate, bench.results(), out);\n}\n\nvoid render(std::string const& mustacheTemplate, const Bench& bench, std::ostream& out) {\n    render(mustacheTemplate.c_str(), bench.results(), out);\n}\n\nnamespace detail {\n\nPerformanceCounters& performanceCounters() {\n#    if defined(__clang__)\n#        pragma clang diagnostic push\n#        pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n#    endif\n    static PerformanceCounters pc;\n#    if defined(__clang__)\n#        pragma clang diagnostic pop\n#    endif\n    return pc;\n}\n\n// Windows version of doNotOptimizeAway\n// see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307\n// see https://github.com/facebook/folly/blob/master/folly/Benchmark.h#L280\n// see https://docs.microsoft.com/en-us/cpp/preprocessor/optimize\n#    if defined(_MSC_VER)\n#        pragma optimize(\"\", off)\nvoid doNotOptimizeAwaySink(void const*) {}\n#        pragma optimize(\"\", on)\n#    endif\n\ntemplate <typename T>\nT parseFile(std::string const& filename) {\n    std::ifstream fin(filename);\n    T num{};\n    fin >> num;\n    return num;\n}\n\nchar const* getEnv(char const* name) {\n#    if defined(_MSC_VER)\n#        pragma warning(push)\n#        pragma warning(disable : 4996) // getenv': This function or variable may be unsafe.\n#    endif\n    return std::getenv(name);\n#    if defined(_MSC_VER)\n#        pragma warning(pop)\n#    endif\n}\n\nbool isEndlessRunning(std::string const& name) {\n    auto endless = getEnv(\"NANOBENCH_ENDLESS\");\n    return nullptr != endless && endless == name;\n}\n\n// True when environment variable NANOBENCH_SUPPRESS_WARNINGS is either not set at all, or set to \"0\"\nbool isWarningsEnabled() {\n    auto suppression = getEnv(\"NANOBENCH_SUPPRESS_WARNINGS\");\n    return nullptr == suppression || suppression == std::string(\"0\");\n}\n\nvoid gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<std::string>& recommendations) {\n    warnings.clear();\n    recommendations.clear();\n\n    bool recommendCheckFlags = false;\n\n#    if defined(DEBUG)\n    warnings.emplace_back(\"DEBUG defined\");\n    recommendCheckFlags = true;\n#    endif\n\n    bool recommendPyPerf = false;\n#    if defined(__linux__)\n    auto nprocs = sysconf(_SC_NPROCESSORS_CONF);\n    if (nprocs <= 0) {\n        warnings.emplace_back(\"couldn't figure out number of processors - no governor, turbo check possible\");\n    } else {\n\n        // check frequency scaling\n        for (long id = 0; id < nprocs; ++id) {\n            auto idStr = detail::fmt::to_s(static_cast<uint64_t>(id));\n            auto sysCpu = \"/sys/devices/system/cpu/cpu\" + idStr;\n            auto minFreq = parseFile<int64_t>(sysCpu + \"/cpufreq/scaling_min_freq\");\n            auto maxFreq = parseFile<int64_t>(sysCpu + \"/cpufreq/scaling_max_freq\");\n            if (minFreq != maxFreq) {\n                auto minMHz = static_cast<double>(minFreq) / 1000.0;\n                auto maxMHz = static_cast<double>(maxFreq) / 1000.0;\n                warnings.emplace_back(\"CPU frequency scaling enabled: CPU \" + idStr + \" between \" +\n                                      detail::fmt::Number(1, 1, minMHz).to_s() + \" and \" + detail::fmt::Number(1, 1, maxMHz).to_s() +\n                                      \" MHz\");\n                recommendPyPerf = true;\n                break;\n            }\n        }\n\n        auto currentGovernor = parseFile<std::string>(\"/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor\");\n        if (\"performance\" != currentGovernor) {\n            warnings.emplace_back(\"CPU governor is '\" + currentGovernor + \"' but should be 'performance'\");\n            recommendPyPerf = true;\n        }\n\n        if (0 == parseFile<int>(\"/sys/devices/system/cpu/intel_pstate/no_turbo\")) {\n            warnings.emplace_back(\"Turbo is enabled, CPU frequency will fluctuate\");\n            recommendPyPerf = true;\n        }\n    }\n#    endif\n\n    if (recommendCheckFlags) {\n        recommendations.emplace_back(\"Make sure you compile for Release\");\n    }\n    if (recommendPyPerf) {\n        recommendations.emplace_back(\"Use 'pyperf system tune' before benchmarking. See https://github.com/psf/pyperf\");\n    }\n}\n\nvoid printStabilityInformationOnce(std::ostream* outStream) {\n    static bool shouldPrint = true;\n    if (shouldPrint && outStream && isWarningsEnabled()) {\n        auto& os = *outStream;\n        shouldPrint = false;\n        std::vector<std::string> warnings;\n        std::vector<std::string> recommendations;\n        gatherStabilityInformation(warnings, recommendations);\n        if (warnings.empty()) {\n            return;\n        }\n\n        os << \"Warning, results might be unstable:\" << std::endl;\n        for (auto const& w : warnings) {\n            os << \"* \" << w << std::endl;\n        }\n\n        os << std::endl << \"Recommendations\" << std::endl;\n        for (auto const& r : recommendations) {\n            os << \"* \" << r << std::endl;\n        }\n    }\n}\n\n// remembers the last table settings used. When it changes, a new table header is automatically written for the new entry.\nuint64_t& singletonHeaderHash() noexcept {\n    static uint64_t sHeaderHash{};\n    return sHeaderHash;\n}\n\nANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\ninline uint64_t hash_combine(uint64_t seed, uint64_t val) {\n    return seed ^ (val + UINT64_C(0x9e3779b9) + (seed << 6U) + (seed >> 2U));\n}\n\n// determines resolution of the given clock. This is done by measuring multiple times and returning the minimum time difference.\nClock::duration calcClockResolution(size_t numEvaluations) noexcept {\n    auto bestDuration = Clock::duration::max();\n    Clock::time_point tBegin;\n    Clock::time_point tEnd;\n    for (size_t i = 0; i < numEvaluations; ++i) {\n        tBegin = Clock::now();\n        do {\n            tEnd = Clock::now();\n        } while (tBegin == tEnd);\n        bestDuration = (std::min)(bestDuration, tEnd - tBegin);\n    }\n    return bestDuration;\n}\n\n// Calculates clock resolution once, and remembers the result\nClock::duration clockResolution() noexcept {\n    static Clock::duration sResolution = calcClockResolution(20);\n    return sResolution;\n}\n\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nstruct IterationLogic::Impl {\n    enum class State { warmup, upscaling_runtime, measuring, endless };\n\n    explicit Impl(Bench const& bench)\n        : mBench(bench)\n        , mResult(bench.config()) {\n        printStabilityInformationOnce(mBench.output());\n\n        // determine target runtime per epoch\n        mTargetRuntimePerEpoch = detail::clockResolution() * mBench.clockResolutionMultiple();\n        if (mTargetRuntimePerEpoch > mBench.maxEpochTime()) {\n            mTargetRuntimePerEpoch = mBench.maxEpochTime();\n        }\n        if (mTargetRuntimePerEpoch < mBench.minEpochTime()) {\n            mTargetRuntimePerEpoch = mBench.minEpochTime();\n        }\n\n        if (isEndlessRunning(mBench.name())) {\n            std::cerr << \"NANOBENCH_ENDLESS set: running '\" << mBench.name() << \"' endlessly\" << std::endl;\n            mNumIters = (std::numeric_limits<uint64_t>::max)();\n            mState = State::endless;\n        } else if (0 != mBench.warmup()) {\n            mNumIters = mBench.warmup();\n            mState = State::warmup;\n        } else if (0 != mBench.epochIterations()) {\n            // exact number of iterations\n            mNumIters = mBench.epochIterations();\n            mState = State::measuring;\n        } else {\n            mNumIters = mBench.minEpochIterations();\n            mState = State::upscaling_runtime;\n        }\n    }\n\n    // directly calculates new iters based on elapsed&iters, and adds a 10% noise. Makes sure we don't underflow.\n    ANKERL_NANOBENCH(NODISCARD) uint64_t calcBestNumIters(std::chrono::nanoseconds elapsed, uint64_t iters) noexcept {\n        auto doubleElapsed = d(elapsed);\n        auto doubleTargetRuntimePerEpoch = d(mTargetRuntimePerEpoch);\n        auto doubleNewIters = doubleTargetRuntimePerEpoch / doubleElapsed * d(iters);\n\n        auto doubleMinEpochIters = d(mBench.minEpochIterations());\n        if (doubleNewIters < doubleMinEpochIters) {\n            doubleNewIters = doubleMinEpochIters;\n        }\n        doubleNewIters *= 1.0 + 0.2 * mRng.uniform01();\n\n        // +0.5 for correct rounding when casting\n        // NOLINTNEXTLINE(bugprone-incorrect-roundings)\n        return static_cast<uint64_t>(doubleNewIters + 0.5);\n    }\n\n    ANKERL_NANOBENCH_NO_SANITIZE(\"integer\") void upscale(std::chrono::nanoseconds elapsed) {\n        if (elapsed * 10 < mTargetRuntimePerEpoch) {\n            // we are far below the target runtime. Multiply iterations by 10 (with overflow check)\n            if (mNumIters * 10 < mNumIters) {\n                // overflow :-(\n                showResult(\"iterations overflow. Maybe your code got optimized away?\");\n                mNumIters = 0;\n                return;\n            }\n            mNumIters *= 10;\n        } else {\n            mNumIters = calcBestNumIters(elapsed, mNumIters);\n        }\n    }\n\n    void add(std::chrono::nanoseconds elapsed, PerformanceCounters const& pc) noexcept {\n#    if defined(ANKERL_NANOBENCH_LOG_ENABLED)\n        auto oldIters = mNumIters;\n#    endif\n\n        switch (mState) {\n        case State::warmup:\n            if (isCloseEnoughForMeasurements(elapsed)) {\n                // if elapsed is close enough, we can skip upscaling and go right to measurements\n                // still, we don't add the result to the measurements.\n                mState = State::measuring;\n                mNumIters = calcBestNumIters(elapsed, mNumIters);\n            } else {\n                // not close enough: switch to upscaling\n                mState = State::upscaling_runtime;\n                upscale(elapsed);\n            }\n            break;\n\n        case State::upscaling_runtime:\n            if (isCloseEnoughForMeasurements(elapsed)) {\n                // if we are close enough, add measurement and switch to always measuring\n                mState = State::measuring;\n                mTotalElapsed += elapsed;\n                mTotalNumIters += mNumIters;\n                mResult.add(elapsed, mNumIters, pc);\n                mNumIters = calcBestNumIters(mTotalElapsed, mTotalNumIters);\n            } else {\n                upscale(elapsed);\n            }\n            break;\n\n        case State::measuring:\n            // just add measurements - no questions asked. Even when runtime is low. But we can't ignore\n            // that fluctuation, or else we would bias the result\n            mTotalElapsed += elapsed;\n            mTotalNumIters += mNumIters;\n            mResult.add(elapsed, mNumIters, pc);\n            if (0 != mBench.epochIterations()) {\n                mNumIters = mBench.epochIterations();\n            } else {\n                mNumIters = calcBestNumIters(mTotalElapsed, mTotalNumIters);\n            }\n            break;\n\n        case State::endless:\n            mNumIters = (std::numeric_limits<uint64_t>::max)();\n            break;\n        }\n\n        if (static_cast<uint64_t>(mResult.size()) == mBench.epochs()) {\n            // we got all the results that we need, finish it\n            showResult(\"\");\n            mNumIters = 0;\n        }\n\n        ANKERL_NANOBENCH_LOG(mBench.name() << \": \" << detail::fmt::Number(20, 3, static_cast<double>(elapsed.count())) << \" elapsed, \"\n                                           << detail::fmt::Number(20, 3, static_cast<double>(mTargetRuntimePerEpoch.count()))\n                                           << \" target. oldIters=\" << oldIters << \", mNumIters=\" << mNumIters\n                                           << \", mState=\" << static_cast<int>(mState));\n    }\n\n    void showResult(std::string const& errorMessage) const {\n        ANKERL_NANOBENCH_LOG(errorMessage);\n\n        if (mBench.output() != nullptr) {\n            // prepare column data ///////\n            std::vector<fmt::MarkDownColumn> columns;\n\n            auto rMedian = mResult.median(Result::Measure::elapsed);\n\n            if (mBench.relative()) {\n                double d = 100.0;\n                if (!mBench.results().empty()) {\n                    d = rMedian <= 0.0 ? 0.0 : mBench.results().front().median(Result::Measure::elapsed) / rMedian * 100.0;\n                }\n                columns.emplace_back(11, 1, \"relative\", \"%\", d);\n            }\n\n            if (mBench.complexityN() > 0) {\n                columns.emplace_back(14, 0, \"complexityN\", \"\", mBench.complexityN());\n            }\n\n            columns.emplace_back(22, 2, mBench.timeUnitName() + \"/\" + mBench.unit(), \"\",\n                                 rMedian / (mBench.timeUnit().count() * mBench.batch()));\n            columns.emplace_back(22, 2, mBench.unit() + \"/s\", \"\", rMedian <= 0.0 ? 0.0 : mBench.batch() / rMedian);\n\n            double rErrorMedian = mResult.medianAbsolutePercentError(Result::Measure::elapsed);\n            columns.emplace_back(10, 1, \"err%\", \"%\", rErrorMedian * 100.0);\n\n            double rInsMedian = -1.0;\n            if (mResult.has(Result::Measure::instructions)) {\n                rInsMedian = mResult.median(Result::Measure::instructions);\n                columns.emplace_back(18, 2, \"ins/\" + mBench.unit(), \"\", rInsMedian / mBench.batch());\n            }\n\n            double rCycMedian = -1.0;\n            if (mResult.has(Result::Measure::cpucycles)) {\n                rCycMedian = mResult.median(Result::Measure::cpucycles);\n                columns.emplace_back(18, 2, \"cyc/\" + mBench.unit(), \"\", rCycMedian / mBench.batch());\n            }\n            if (rInsMedian > 0.0 && rCycMedian > 0.0) {\n                columns.emplace_back(9, 3, \"IPC\", \"\", rCycMedian <= 0.0 ? 0.0 : rInsMedian / rCycMedian);\n            }\n            if (mResult.has(Result::Measure::branchinstructions)) {\n                double rBraMedian = mResult.median(Result::Measure::branchinstructions);\n                columns.emplace_back(17, 2, \"bra/\" + mBench.unit(), \"\", rBraMedian / mBench.batch());\n                if (mResult.has(Result::Measure::branchmisses)) {\n                    double p = 0.0;\n                    if (rBraMedian >= 1e-9) {\n                        p = 100.0 * mResult.median(Result::Measure::branchmisses) / rBraMedian;\n                    }\n                    columns.emplace_back(10, 1, \"miss%\", \"%\", p);\n                }\n            }\n\n            columns.emplace_back(12, 2, \"total\", \"\", mResult.sumProduct(Result::Measure::iterations, Result::Measure::elapsed));\n\n            // write everything\n            auto& os = *mBench.output();\n\n            // combine all elements that are relevant for printing the header\n            uint64_t hash = 0;\n            hash = hash_combine(std::hash<std::string>{}(mBench.unit()), hash);\n            hash = hash_combine(std::hash<std::string>{}(mBench.title()), hash);\n            hash = hash_combine(std::hash<std::string>{}(mBench.timeUnitName()), hash);\n            hash = hash_combine(std::hash<double>{}(mBench.timeUnit().count()), hash);\n            hash = hash_combine(mBench.relative(), hash);\n            hash = hash_combine(mBench.performanceCounters(), hash);\n\n            if (hash != singletonHeaderHash()) {\n                singletonHeaderHash() = hash;\n\n                // no result yet, print header\n                os << std::endl;\n                for (auto const& col : columns) {\n                    os << col.title();\n                }\n                os << \"| \" << mBench.title() << std::endl;\n\n                for (auto const& col : columns) {\n                    os << col.separator();\n                }\n                os << \"|:\" << std::string(mBench.title().size() + 1U, '-') << std::endl;\n            }\n\n            if (!errorMessage.empty()) {\n                for (auto const& col : columns) {\n                    os << col.invalid();\n                }\n                os << \"| :boom: \" << fmt::MarkDownCode(mBench.name()) << \" (\" << errorMessage << ')' << std::endl;\n            } else {\n                for (auto const& col : columns) {\n                    os << col.value();\n                }\n                os << \"| \";\n                auto showUnstable = isWarningsEnabled() && rErrorMedian >= 0.05;\n                if (showUnstable) {\n                    os << \":wavy_dash: \";\n                }\n                os << fmt::MarkDownCode(mBench.name());\n                if (showUnstable) {\n                    auto avgIters = static_cast<double>(mTotalNumIters) / static_cast<double>(mBench.epochs());\n                    // NOLINTNEXTLINE(bugprone-incorrect-roundings)\n                    auto suggestedIters = static_cast<uint64_t>(avgIters * 10 + 0.5);\n\n                    os << \" (Unstable with ~\" << detail::fmt::Number(1, 1, avgIters)\n                       << \" iters. Increase `minEpochIterations` to e.g. \" << suggestedIters << \")\";\n                }\n                os << std::endl;\n            }\n        }\n    }\n\n    ANKERL_NANOBENCH(NODISCARD) bool isCloseEnoughForMeasurements(std::chrono::nanoseconds elapsed) const noexcept {\n        return elapsed * 3 >= mTargetRuntimePerEpoch * 2;\n    }\n\n    uint64_t mNumIters = 1;\n    Bench const& mBench;\n    std::chrono::nanoseconds mTargetRuntimePerEpoch{};\n    Result mResult;\n    Rng mRng{123};\n    std::chrono::nanoseconds mTotalElapsed{};\n    uint64_t mTotalNumIters = 0;\n\n    State mState = State::upscaling_runtime;\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\nIterationLogic::IterationLogic(Bench const& bench) noexcept\n    : mPimpl(new Impl(bench)) {}\n\nIterationLogic::~IterationLogic() {\n    if (mPimpl) {\n        delete mPimpl;\n    }\n}\n\nuint64_t IterationLogic::numIters() const noexcept {\n    ANKERL_NANOBENCH_LOG(mPimpl->mBench.name() << \": mNumIters=\" << mPimpl->mNumIters);\n    return mPimpl->mNumIters;\n}\n\nvoid IterationLogic::add(std::chrono::nanoseconds elapsed, PerformanceCounters const& pc) noexcept {\n    mPimpl->add(elapsed, pc);\n}\n\nvoid IterationLogic::moveResultTo(std::vector<Result>& results) noexcept {\n    results.emplace_back(std::move(mPimpl->mResult));\n}\n\n#    if ANKERL_NANOBENCH(PERF_COUNTERS)\n\nANKERL_NANOBENCH(IGNORE_PADDED_PUSH)\nclass LinuxPerformanceCounters {\npublic:\n    struct Target {\n        Target(uint64_t* targetValue_, bool correctMeasuringOverhead_, bool correctLoopOverhead_)\n            : targetValue(targetValue_)\n            , correctMeasuringOverhead(correctMeasuringOverhead_)\n            , correctLoopOverhead(correctLoopOverhead_) {}\n\n        uint64_t* targetValue{};\n        bool correctMeasuringOverhead{};\n        bool correctLoopOverhead{};\n    };\n\n    ~LinuxPerformanceCounters();\n\n    // quick operation\n    inline void start() {}\n\n    inline void stop() {}\n\n    bool monitor(perf_sw_ids swId, Target target);\n    bool monitor(perf_hw_id hwId, Target target);\n\n    bool hasError() const noexcept {\n        return mHasError;\n    }\n\n    // Just reading data is faster than enable & disabling.\n    // we subtract data ourselves.\n    inline void beginMeasure() {\n        if (mHasError) {\n            return;\n        }\n\n        // NOLINTNEXTLINE(hicpp-signed-bitwise)\n        mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);\n        if (mHasError) {\n            return;\n        }\n\n        // NOLINTNEXTLINE(hicpp-signed-bitwise)\n        mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);\n    }\n\n    inline void endMeasure() {\n        if (mHasError) {\n            return;\n        }\n\n        // NOLINTNEXTLINE(hicpp-signed-bitwise)\n        mHasError = (-1 == ioctl(mFd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP));\n        if (mHasError) {\n            return;\n        }\n\n        auto const numBytes = sizeof(uint64_t) * mCounters.size();\n        auto ret = read(mFd, mCounters.data(), numBytes);\n        mHasError = ret != static_cast<ssize_t>(numBytes);\n    }\n\n    void updateResults(uint64_t numIters);\n\n    // rounded integer division\n    template <typename T>\n    static inline T divRounded(T a, T divisor) {\n        return (a + divisor / 2) / divisor;\n    }\n\n    template <typename Op>\n    ANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\n    void calibrate(Op&& op) {\n        // clear current calibration data,\n        for (auto& v : mCalibratedOverhead) {\n            v = UINT64_C(0);\n        }\n\n        // create new calibration data\n        auto newCalibration = mCalibratedOverhead;\n        for (auto& v : newCalibration) {\n            v = (std::numeric_limits<uint64_t>::max)();\n        }\n        for (size_t iter = 0; iter < 100; ++iter) {\n            beginMeasure();\n            op();\n            endMeasure();\n            if (mHasError) {\n                return;\n            }\n\n            for (size_t i = 0; i < newCalibration.size(); ++i) {\n                auto diff = mCounters[i];\n                if (newCalibration[i] > diff) {\n                    newCalibration[i] = diff;\n                }\n            }\n        }\n\n        mCalibratedOverhead = std::move(newCalibration);\n\n        {\n            // calibrate loop overhead. For branches & instructions this makes sense, not so much for everything else like cycles.\n            // marsaglia's xorshift: mov, sal/shr, xor. Times 3.\n            // This has the nice property that the compiler doesn't seem to be able to optimize multiple calls any further.\n            // see https://godbolt.org/z/49RVQ5\n            uint64_t const numIters = 100000U + (std::random_device{}() & 3);\n            uint64_t n = numIters;\n            uint32_t x = 1234567;\n            auto fn = [&]() {\n                x ^= x << 13;\n                x ^= x >> 17;\n                x ^= x << 5;\n            };\n\n            beginMeasure();\n            while (n-- > 0) {\n                fn();\n            }\n            endMeasure();\n            detail::doNotOptimizeAway(x);\n            auto measure1 = mCounters;\n\n            n = numIters;\n            beginMeasure();\n            while (n-- > 0) {\n                // we now run *twice* so we can easily calculate the overhead\n                fn();\n                fn();\n            }\n            endMeasure();\n            detail::doNotOptimizeAway(x);\n            auto measure2 = mCounters;\n\n            for (size_t i = 0; i < mCounters.size(); ++i) {\n                // factor 2 because we have two instructions per loop\n                auto m1 = measure1[i] > mCalibratedOverhead[i] ? measure1[i] - mCalibratedOverhead[i] : 0;\n                auto m2 = measure2[i] > mCalibratedOverhead[i] ? measure2[i] - mCalibratedOverhead[i] : 0;\n                auto overhead = m1 * 2 > m2 ? m1 * 2 - m2 : 0;\n\n                mLoopOverhead[i] = divRounded(overhead, numIters);\n            }\n        }\n    }\n\nprivate:\n    bool monitor(uint32_t type, uint64_t eventid, Target target);\n\n    std::map<uint64_t, Target> mIdToTarget{};\n\n    // start with minimum size of 3 for read_format\n    std::vector<uint64_t> mCounters{3};\n    std::vector<uint64_t> mCalibratedOverhead{3};\n    std::vector<uint64_t> mLoopOverhead{3};\n\n    uint64_t mTimeEnabledNanos = 0;\n    uint64_t mTimeRunningNanos = 0;\n    int mFd = -1;\n    bool mHasError = false;\n};\nANKERL_NANOBENCH(IGNORE_PADDED_POP)\n\nLinuxPerformanceCounters::~LinuxPerformanceCounters() {\n    if (-1 != mFd) {\n        close(mFd);\n    }\n}\n\nbool LinuxPerformanceCounters::monitor(perf_sw_ids swId, LinuxPerformanceCounters::Target target) {\n    return monitor(PERF_TYPE_SOFTWARE, swId, target);\n}\n\nbool LinuxPerformanceCounters::monitor(perf_hw_id hwId, LinuxPerformanceCounters::Target target) {\n    return monitor(PERF_TYPE_HARDWARE, hwId, target);\n}\n\n// overflow is ok, it's checked\nANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\nvoid LinuxPerformanceCounters::updateResults(uint64_t numIters) {\n    // clear old data\n    for (auto& id_value : mIdToTarget) {\n        *id_value.second.targetValue = UINT64_C(0);\n    }\n\n    if (mHasError) {\n        return;\n    }\n\n    mTimeEnabledNanos = mCounters[1] - mCalibratedOverhead[1];\n    mTimeRunningNanos = mCounters[2] - mCalibratedOverhead[2];\n\n    for (uint64_t i = 0; i < mCounters[0]; ++i) {\n        auto idx = static_cast<size_t>(3 + i * 2 + 0);\n        auto id = mCounters[idx + 1U];\n\n        auto it = mIdToTarget.find(id);\n        if (it != mIdToTarget.end()) {\n\n            auto& tgt = it->second;\n            *tgt.targetValue = mCounters[idx];\n            if (tgt.correctMeasuringOverhead) {\n                if (*tgt.targetValue >= mCalibratedOverhead[idx]) {\n                    *tgt.targetValue -= mCalibratedOverhead[idx];\n                } else {\n                    *tgt.targetValue = 0U;\n                }\n            }\n            if (tgt.correctLoopOverhead) {\n                auto correctionVal = mLoopOverhead[idx] * numIters;\n                if (*tgt.targetValue >= correctionVal) {\n                    *tgt.targetValue -= correctionVal;\n                } else {\n                    *tgt.targetValue = 0U;\n                }\n            }\n        }\n    }\n}\n\nbool LinuxPerformanceCounters::monitor(uint32_t type, uint64_t eventid, Target target) {\n    *target.targetValue = (std::numeric_limits<uint64_t>::max)();\n    if (mHasError) {\n        return false;\n    }\n\n    auto pea = perf_event_attr();\n    std::memset(&pea, 0, sizeof(perf_event_attr));\n    pea.type = type;\n    pea.size = sizeof(perf_event_attr);\n    pea.config = eventid;\n    pea.disabled = 1; // start counter as disabled\n    pea.exclude_kernel = 1;\n    pea.exclude_hv = 1;\n\n    // NOLINTNEXTLINE(hicpp-signed-bitwise)\n    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;\n\n    const int pid = 0;                    // the current process\n    const int cpu = -1;                   // all CPUs\n#        if defined(PERF_FLAG_FD_CLOEXEC) // since Linux 3.14\n    const unsigned long flags = PERF_FLAG_FD_CLOEXEC;\n#        else\n    const unsigned long flags = 0;\n#        endif\n\n    auto fd = static_cast<int>(syscall(__NR_perf_event_open, &pea, pid, cpu, mFd, flags));\n    if (-1 == fd) {\n        return false;\n    }\n    if (-1 == mFd) {\n        // first call: set to fd, and use this from now on\n        mFd = fd;\n    }\n    uint64_t id = 0;\n    // NOLINTNEXTLINE(hicpp-signed-bitwise)\n    if (-1 == ioctl(fd, PERF_EVENT_IOC_ID, &id)) {\n        // couldn't get id\n        return false;\n    }\n\n    // insert into map, rely on the fact that map's references are constant.\n    mIdToTarget.emplace(id, target);\n\n    // prepare readformat with the correct size (after the insert)\n    auto size = 3 + 2 * mIdToTarget.size();\n    mCounters.resize(size);\n    mCalibratedOverhead.resize(size);\n    mLoopOverhead.resize(size);\n\n    return true;\n}\n\nPerformanceCounters::PerformanceCounters()\n    : mPc(new LinuxPerformanceCounters())\n    , mVal()\n    , mHas() {\n\n    mHas.pageFaults = mPc->monitor(PERF_COUNT_SW_PAGE_FAULTS, LinuxPerformanceCounters::Target(&mVal.pageFaults, true, false));\n    mHas.cpuCycles = mPc->monitor(PERF_COUNT_HW_REF_CPU_CYCLES, LinuxPerformanceCounters::Target(&mVal.cpuCycles, true, false));\n    mHas.contextSwitches =\n        mPc->monitor(PERF_COUNT_SW_CONTEXT_SWITCHES, LinuxPerformanceCounters::Target(&mVal.contextSwitches, true, false));\n    mHas.instructions = mPc->monitor(PERF_COUNT_HW_INSTRUCTIONS, LinuxPerformanceCounters::Target(&mVal.instructions, true, true));\n    mHas.branchInstructions =\n        mPc->monitor(PERF_COUNT_HW_BRANCH_INSTRUCTIONS, LinuxPerformanceCounters::Target(&mVal.branchInstructions, true, false));\n    mHas.branchMisses = mPc->monitor(PERF_COUNT_HW_BRANCH_MISSES, LinuxPerformanceCounters::Target(&mVal.branchMisses, true, false));\n    // mHas.branchMisses = false;\n\n    mPc->start();\n    mPc->calibrate([] {\n        auto before = ankerl::nanobench::Clock::now();\n        auto after = ankerl::nanobench::Clock::now();\n        (void)before;\n        (void)after;\n    });\n\n    if (mPc->hasError()) {\n        // something failed, don't monitor anything.\n        mHas = PerfCountSet<bool>{};\n    }\n}\n\nPerformanceCounters::~PerformanceCounters() {\n    if (nullptr != mPc) {\n        delete mPc;\n    }\n}\n\nvoid PerformanceCounters::beginMeasure() {\n    mPc->beginMeasure();\n}\n\nvoid PerformanceCounters::endMeasure() {\n    mPc->endMeasure();\n}\n\nvoid PerformanceCounters::updateResults(uint64_t numIters) {\n    mPc->updateResults(numIters);\n}\n\n#    else\n\nPerformanceCounters::PerformanceCounters() = default;\nPerformanceCounters::~PerformanceCounters() = default;\nvoid PerformanceCounters::beginMeasure() {}\nvoid PerformanceCounters::endMeasure() {}\nvoid PerformanceCounters::updateResults(uint64_t) {}\n\n#    endif\n\nANKERL_NANOBENCH(NODISCARD) PerfCountSet<uint64_t> const& PerformanceCounters::val() const noexcept {\n    return mVal;\n}\nANKERL_NANOBENCH(NODISCARD) PerfCountSet<bool> const& PerformanceCounters::has() const noexcept {\n    return mHas;\n}\n\n// formatting utilities\nnamespace fmt {\n\n// adds thousands separator to numbers\nNumSep::NumSep(char sep)\n    : mSep(sep) {}\n\nchar NumSep::do_thousands_sep() const {\n    return mSep;\n}\n\nstd::string NumSep::do_grouping() const {\n    return \"\\003\";\n}\n\n// RAII to save & restore a stream's state\nStreamStateRestorer::StreamStateRestorer(std::ostream& s)\n    : mStream(s)\n    , mLocale(s.getloc())\n    , mPrecision(s.precision())\n    , mWidth(s.width())\n    , mFill(s.fill())\n    , mFmtFlags(s.flags()) {}\n\nStreamStateRestorer::~StreamStateRestorer() {\n    restore();\n}\n\n// sets back all stream info that we remembered at construction\nvoid StreamStateRestorer::restore() {\n    mStream.imbue(mLocale);\n    mStream.precision(mPrecision);\n    mStream.width(mWidth);\n    mStream.fill(mFill);\n    mStream.flags(mFmtFlags);\n}\n\nNumber::Number(int width, int precision, int64_t value)\n    : mWidth(width)\n    , mPrecision(precision)\n    , mValue(static_cast<double>(value)) {}\n\nNumber::Number(int width, int precision, double value)\n    : mWidth(width)\n    , mPrecision(precision)\n    , mValue(value) {}\n\nstd::ostream& Number::write(std::ostream& os) const {\n    StreamStateRestorer restorer(os);\n    os.imbue(std::locale(os.getloc(), new NumSep(',')));\n    os << std::setw(mWidth) << std::setprecision(mPrecision) << std::fixed << mValue;\n    return os;\n}\n\nstd::string Number::to_s() const {\n    std::stringstream ss;\n    write(ss);\n    return ss.str();\n}\n\nstd::string to_s(uint64_t n) {\n    std::string str;\n    do {\n        str += static_cast<char>('0' + static_cast<char>(n % 10));\n        n /= 10;\n    } while (n != 0);\n    std::reverse(str.begin(), str.end());\n    return str;\n}\n\nstd::ostream& operator<<(std::ostream& os, Number const& n) {\n    return n.write(os);\n}\n\nMarkDownColumn::MarkDownColumn(int w, int prec, std::string const& tit, std::string const& suff, double val)\n    : mWidth(w)\n    , mPrecision(prec)\n    , mTitle(tit)\n    , mSuffix(suff)\n    , mValue(val) {}\n\nstd::string MarkDownColumn::title() const {\n    std::stringstream ss;\n    ss << '|' << std::setw(mWidth - 2) << std::right << mTitle << ' ';\n    return ss.str();\n}\n\nstd::string MarkDownColumn::separator() const {\n    std::string sep(static_cast<size_t>(mWidth), '-');\n    sep.front() = '|';\n    sep.back() = ':';\n    return sep;\n}\n\nstd::string MarkDownColumn::invalid() const {\n    std::string sep(static_cast<size_t>(mWidth), ' ');\n    sep.front() = '|';\n    sep[sep.size() - 2] = '-';\n    return sep;\n}\n\nstd::string MarkDownColumn::value() const {\n    std::stringstream ss;\n    auto width = mWidth - 2 - static_cast<int>(mSuffix.size());\n    ss << '|' << Number(width, mPrecision, mValue) << mSuffix << ' ';\n    return ss.str();\n}\n\n// Formats any text as markdown code, escaping backticks.\nMarkDownCode::MarkDownCode(std::string const& what) {\n    mWhat.reserve(what.size() + 2);\n    mWhat.push_back('`');\n    for (char c : what) {\n        mWhat.push_back(c);\n        if ('`' == c) {\n            mWhat.push_back('`');\n        }\n    }\n    mWhat.push_back('`');\n}\n\nstd::ostream& MarkDownCode::write(std::ostream& os) const {\n    return os << mWhat;\n}\n\nstd::ostream& operator<<(std::ostream& os, MarkDownCode const& mdCode) {\n    return mdCode.write(os);\n}\n} // namespace fmt\n} // namespace detail\n\n// provide implementation here so it's only generated once\nConfig::Config() = default;\nConfig::~Config() = default;\nConfig& Config::operator=(Config const&) = default;\nConfig& Config::operator=(Config&&) = default;\nConfig::Config(Config const&) = default;\nConfig::Config(Config&&) noexcept = default;\n\n// provide implementation here so it's only generated once\nResult::~Result() = default;\nResult& Result::operator=(Result const&) = default;\nResult& Result::operator=(Result&&) = default;\nResult::Result(Result const&) = default;\nResult::Result(Result&&) noexcept = default;\n\nnamespace detail {\ntemplate <typename T>\ninline constexpr typename std::underlying_type<T>::type u(T val) noexcept {\n    return static_cast<typename std::underlying_type<T>::type>(val);\n}\n} // namespace detail\n\n// Result returned after a benchmark has finished. Can be used as a baseline for relative().\nResult::Result(Config const& benchmarkConfig)\n    : mConfig(benchmarkConfig)\n    , mNameToMeasurements{detail::u(Result::Measure::_size)} {}\n\nvoid Result::add(Clock::duration totalElapsed, uint64_t iters, detail::PerformanceCounters const& pc) {\n    using detail::d;\n    using detail::u;\n\n    double dIters = d(iters);\n    mNameToMeasurements[u(Result::Measure::iterations)].push_back(dIters);\n\n    mNameToMeasurements[u(Result::Measure::elapsed)].push_back(d(totalElapsed) / dIters);\n    if (pc.has().pageFaults) {\n        mNameToMeasurements[u(Result::Measure::pagefaults)].push_back(d(pc.val().pageFaults) / dIters);\n    }\n    if (pc.has().cpuCycles) {\n        mNameToMeasurements[u(Result::Measure::cpucycles)].push_back(d(pc.val().cpuCycles) / dIters);\n    }\n    if (pc.has().contextSwitches) {\n        mNameToMeasurements[u(Result::Measure::contextswitches)].push_back(d(pc.val().contextSwitches) / dIters);\n    }\n    if (pc.has().instructions) {\n        mNameToMeasurements[u(Result::Measure::instructions)].push_back(d(pc.val().instructions) / dIters);\n    }\n    if (pc.has().branchInstructions) {\n        double branchInstructions = 0.0;\n        // correcting branches: remove branch introduced by the while (...) loop for each iteration.\n        if (pc.val().branchInstructions > iters + 1U) {\n            branchInstructions = d(pc.val().branchInstructions - (iters + 1U));\n        }\n        mNameToMeasurements[u(Result::Measure::branchinstructions)].push_back(branchInstructions / dIters);\n\n        if (pc.has().branchMisses) {\n            // correcting branch misses\n            double branchMisses = d(pc.val().branchMisses);\n            if (branchMisses > branchInstructions) {\n                // can't have branch misses when there were branches...\n                branchMisses = branchInstructions;\n            }\n\n            // assuming at least one missed branch for the loop\n            branchMisses -= 1.0;\n            if (branchMisses < 1.0) {\n                branchMisses = 1.0;\n            }\n            mNameToMeasurements[u(Result::Measure::branchmisses)].push_back(branchMisses / dIters);\n        }\n    }\n}\n\nConfig const& Result::config() const noexcept {\n    return mConfig;\n}\n\ninline double calcMedian(std::vector<double>& data) {\n    if (data.empty()) {\n        return 0.0;\n    }\n    std::sort(data.begin(), data.end());\n\n    auto midIdx = data.size() / 2U;\n    if (1U == (data.size() & 1U)) {\n        return data[midIdx];\n    }\n    return (data[midIdx - 1U] + data[midIdx]) / 2U;\n}\n\ndouble Result::median(Measure m) const {\n    // create a copy so we can sort\n    auto data = mNameToMeasurements[detail::u(m)];\n    return calcMedian(data);\n}\n\ndouble Result::average(Measure m) const {\n    using detail::d;\n    auto const& data = mNameToMeasurements[detail::u(m)];\n    if (data.empty()) {\n        return 0.0;\n    }\n\n    // create a copy so we can sort\n    return sum(m) / d(data.size());\n}\n\ndouble Result::medianAbsolutePercentError(Measure m) const {\n    // create copy\n    auto data = mNameToMeasurements[detail::u(m)];\n\n    // calculates MdAPE which is the median of percentage error\n    // see https://www.spiderfinancial.com/support/documentation/numxl/reference-manual/forecasting-performance/mdape\n    auto med = calcMedian(data);\n\n    // transform the data to absolute error\n    for (auto& x : data) {\n        x = (x - med) / x;\n        if (x < 0) {\n            x = -x;\n        }\n    }\n    return calcMedian(data);\n}\n\ndouble Result::sum(Measure m) const noexcept {\n    auto const& data = mNameToMeasurements[detail::u(m)];\n    return std::accumulate(data.begin(), data.end(), 0.0);\n}\n\ndouble Result::sumProduct(Measure m1, Measure m2) const noexcept {\n    auto const& data1 = mNameToMeasurements[detail::u(m1)];\n    auto const& data2 = mNameToMeasurements[detail::u(m2)];\n\n    if (data1.size() != data2.size()) {\n        return 0.0;\n    }\n\n    double result = 0.0;\n    for (size_t i = 0, s = data1.size(); i != s; ++i) {\n        result += data1[i] * data2[i];\n    }\n    return result;\n}\n\nbool Result::has(Measure m) const noexcept {\n    return !mNameToMeasurements[detail::u(m)].empty();\n}\n\ndouble Result::get(size_t idx, Measure m) const {\n    auto const& data = mNameToMeasurements[detail::u(m)];\n    return data.at(idx);\n}\n\nbool Result::empty() const noexcept {\n    return 0U == size();\n}\n\nsize_t Result::size() const noexcept {\n    auto const& data = mNameToMeasurements[detail::u(Measure::elapsed)];\n    return data.size();\n}\n\ndouble Result::minimum(Measure m) const noexcept {\n    auto const& data = mNameToMeasurements[detail::u(m)];\n    if (data.empty()) {\n        return 0.0;\n    }\n\n    // here its save to assume that at least one element is there\n    return *std::min_element(data.begin(), data.end());\n}\n\ndouble Result::maximum(Measure m) const noexcept {\n    auto const& data = mNameToMeasurements[detail::u(m)];\n    if (data.empty()) {\n        return 0.0;\n    }\n\n    // here its save to assume that at least one element is there\n    return *std::max_element(data.begin(), data.end());\n}\n\nResult::Measure Result::fromString(std::string const& str) {\n    if (str == \"elapsed\") {\n        return Measure::elapsed;\n    } else if (str == \"iterations\") {\n        return Measure::iterations;\n    } else if (str == \"pagefaults\") {\n        return Measure::pagefaults;\n    } else if (str == \"cpucycles\") {\n        return Measure::cpucycles;\n    } else if (str == \"contextswitches\") {\n        return Measure::contextswitches;\n    } else if (str == \"instructions\") {\n        return Measure::instructions;\n    } else if (str == \"branchinstructions\") {\n        return Measure::branchinstructions;\n    } else if (str == \"branchmisses\") {\n        return Measure::branchmisses;\n    } else {\n        // not found, return _size\n        return Measure::_size;\n    }\n}\n\n// Configuration of a microbenchmark.\nBench::Bench() {\n    mConfig.mOut = &std::cout;\n}\n\nBench::Bench(Bench&&) = default;\nBench& Bench::operator=(Bench&&) = default;\nBench::Bench(Bench const&) = default;\nBench& Bench::operator=(Bench const&) = default;\nBench::~Bench() noexcept = default;\n\ndouble Bench::batch() const noexcept {\n    return mConfig.mBatch;\n}\n\ndouble Bench::complexityN() const noexcept {\n    return mConfig.mComplexityN;\n}\n\n// Set a baseline to compare it to. 100% it is exactly as fast as the baseline, >100% means it is faster than the baseline, <100%\n// means it is slower than the baseline.\nBench& Bench::relative(bool isRelativeEnabled) noexcept {\n    mConfig.mIsRelative = isRelativeEnabled;\n    return *this;\n}\nbool Bench::relative() const noexcept {\n    return mConfig.mIsRelative;\n}\n\nBench& Bench::performanceCounters(bool showPerformanceCounters) noexcept {\n    mConfig.mShowPerformanceCounters = showPerformanceCounters;\n    return *this;\n}\nbool Bench::performanceCounters() const noexcept {\n    return mConfig.mShowPerformanceCounters;\n}\n\n// Operation unit. Defaults to \"op\", could be e.g. \"byte\" for string processing.\n// If u differs from currently set unit, the stored results will be cleared.\n// Use singular (byte, not bytes).\nBench& Bench::unit(char const* u) {\n    if (u != mConfig.mUnit) {\n        mResults.clear();\n    }\n    mConfig.mUnit = u;\n    return *this;\n}\n\nBench& Bench::unit(std::string const& u) {\n    return unit(u.c_str());\n}\n\nstd::string const& Bench::unit() const noexcept {\n    return mConfig.mUnit;\n}\n\nBench& Bench::timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName) {\n    mConfig.mTimeUnit = tu;\n    mConfig.mTimeUnitName = tuName;\n    return *this;\n}\n\nstd::string const& Bench::timeUnitName() const noexcept {\n    return mConfig.mTimeUnitName;\n}\n\nstd::chrono::duration<double> const& Bench::timeUnit() const noexcept {\n    return mConfig.mTimeUnit;\n}\n\n// If benchmarkTitle differs from currently set title, the stored results will be cleared.\nBench& Bench::title(const char* benchmarkTitle) {\n    if (benchmarkTitle != mConfig.mBenchmarkTitle) {\n        mResults.clear();\n    }\n    mConfig.mBenchmarkTitle = benchmarkTitle;\n    return *this;\n}\nBench& Bench::title(std::string const& benchmarkTitle) {\n    if (benchmarkTitle != mConfig.mBenchmarkTitle) {\n        mResults.clear();\n    }\n    mConfig.mBenchmarkTitle = benchmarkTitle;\n    return *this;\n}\n\nstd::string const& Bench::title() const noexcept {\n    return mConfig.mBenchmarkTitle;\n}\n\nBench& Bench::name(const char* benchmarkName) {\n    mConfig.mBenchmarkName = benchmarkName;\n    return *this;\n}\n\nBench& Bench::name(std::string const& benchmarkName) {\n    mConfig.mBenchmarkName = benchmarkName;\n    return *this;\n}\n\nstd::string const& Bench::name() const noexcept {\n    return mConfig.mBenchmarkName;\n}\n\n// Number of epochs to evaluate. The reported result will be the median of evaluation of each epoch.\nBench& Bench::epochs(size_t numEpochs) noexcept {\n    mConfig.mNumEpochs = numEpochs;\n    return *this;\n}\nsize_t Bench::epochs() const noexcept {\n    return mConfig.mNumEpochs;\n}\n\n// Desired evaluation time is a multiple of clock resolution. Default is to be 1000 times above this measurement precision.\nBench& Bench::clockResolutionMultiple(size_t multiple) noexcept {\n    mConfig.mClockResolutionMultiple = multiple;\n    return *this;\n}\nsize_t Bench::clockResolutionMultiple() const noexcept {\n    return mConfig.mClockResolutionMultiple;\n}\n\n// Sets the maximum time each epoch should take. Default is 100ms.\nBench& Bench::maxEpochTime(std::chrono::nanoseconds t) noexcept {\n    mConfig.mMaxEpochTime = t;\n    return *this;\n}\nstd::chrono::nanoseconds Bench::maxEpochTime() const noexcept {\n    return mConfig.mMaxEpochTime;\n}\n\n// Sets the maximum time each epoch should take. Default is 100ms.\nBench& Bench::minEpochTime(std::chrono::nanoseconds t) noexcept {\n    mConfig.mMinEpochTime = t;\n    return *this;\n}\nstd::chrono::nanoseconds Bench::minEpochTime() const noexcept {\n    return mConfig.mMinEpochTime;\n}\n\nBench& Bench::minEpochIterations(uint64_t numIters) noexcept {\n    mConfig.mMinEpochIterations = (numIters == 0) ? 1 : numIters;\n    return *this;\n}\nuint64_t Bench::minEpochIterations() const noexcept {\n    return mConfig.mMinEpochIterations;\n}\n\nBench& Bench::epochIterations(uint64_t numIters) noexcept {\n    mConfig.mEpochIterations = numIters;\n    return *this;\n}\nuint64_t Bench::epochIterations() const noexcept {\n    return mConfig.mEpochIterations;\n}\n\nBench& Bench::warmup(uint64_t numWarmupIters) noexcept {\n    mConfig.mWarmup = numWarmupIters;\n    return *this;\n}\nuint64_t Bench::warmup() const noexcept {\n    return mConfig.mWarmup;\n}\n\nBench& Bench::config(Config const& benchmarkConfig) {\n    mConfig = benchmarkConfig;\n    return *this;\n}\nConfig const& Bench::config() const noexcept {\n    return mConfig;\n}\n\nBench& Bench::output(std::ostream* outstream) noexcept {\n    mConfig.mOut = outstream;\n    return *this;\n}\n\nANKERL_NANOBENCH(NODISCARD) std::ostream* Bench::output() const noexcept {\n    return mConfig.mOut;\n}\n\nstd::vector<Result> const& Bench::results() const noexcept {\n    return mResults;\n}\n\nBench& Bench::render(char const* templateContent, std::ostream& os) {\n    ::ankerl::nanobench::render(templateContent, *this, os);\n    return *this;\n}\n\nBench& Bench::render(std::string const& templateContent, std::ostream& os) {\n    ::ankerl::nanobench::render(templateContent, *this, os);\n    return *this;\n}\n\nstd::vector<BigO> Bench::complexityBigO() const {\n    std::vector<BigO> bigOs;\n    auto rangeMeasure = BigO::collectRangeMeasure(mResults);\n    bigOs.emplace_back(\"O(1)\", rangeMeasure, [](double) {\n        return 1.0;\n    });\n    bigOs.emplace_back(\"O(n)\", rangeMeasure, [](double n) {\n        return n;\n    });\n    bigOs.emplace_back(\"O(log n)\", rangeMeasure, [](double n) {\n        return std::log2(n);\n    });\n    bigOs.emplace_back(\"O(n log n)\", rangeMeasure, [](double n) {\n        return n * std::log2(n);\n    });\n    bigOs.emplace_back(\"O(n^2)\", rangeMeasure, [](double n) {\n        return n * n;\n    });\n    bigOs.emplace_back(\"O(n^3)\", rangeMeasure, [](double n) {\n        return n * n * n;\n    });\n    std::sort(bigOs.begin(), bigOs.end());\n    return bigOs;\n}\n\nRng::Rng()\n    : mX(0)\n    , mY(0) {\n    std::random_device rd;\n    std::uniform_int_distribution<uint64_t> dist;\n    do {\n        mX = dist(rd);\n        mY = dist(rd);\n    } while (mX == 0 && mY == 0);\n}\n\nANKERL_NANOBENCH_NO_SANITIZE(\"integer\")\nuint64_t splitMix64(uint64_t& state) noexcept {\n    uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15));\n    z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);\n    z = (z ^ (z >> 27U)) * UINT64_C(0x94d049bb133111eb);\n    return z ^ (z >> 31U);\n}\n\n// Seeded as described in romu paper (update april 2020)\nRng::Rng(uint64_t seed) noexcept\n    : mX(splitMix64(seed))\n    , mY(splitMix64(seed)) {\n    for (size_t i = 0; i < 10; ++i) {\n        operator()();\n    }\n}\n\n// only internally used to copy the RNG.\nRng::Rng(uint64_t x, uint64_t y) noexcept\n    : mX(x)\n    , mY(y) {}\n\nRng Rng::copy() const noexcept {\n    return Rng{mX, mY};\n}\n\nBigO::RangeMeasure BigO::collectRangeMeasure(std::vector<Result> const& results) {\n    BigO::RangeMeasure rangeMeasure;\n    for (auto const& result : results) {\n        if (result.config().mComplexityN > 0.0) {\n            rangeMeasure.emplace_back(result.config().mComplexityN, result.median(Result::Measure::elapsed));\n        }\n    }\n    return rangeMeasure;\n}\n\nBigO::BigO(std::string const& bigOName, RangeMeasure const& rangeMeasure)\n    : mName(bigOName) {\n\n    // estimate the constant factor\n    double sumRangeMeasure = 0.0;\n    double sumRangeRange = 0.0;\n\n    for (size_t i = 0; i < rangeMeasure.size(); ++i) {\n        sumRangeMeasure += rangeMeasure[i].first * rangeMeasure[i].second;\n        sumRangeRange += rangeMeasure[i].first * rangeMeasure[i].first;\n    }\n    mConstant = sumRangeMeasure / sumRangeRange;\n\n    // calculate root mean square\n    double err = 0.0;\n    double sumMeasure = 0.0;\n    for (size_t i = 0; i < rangeMeasure.size(); ++i) {\n        auto diff = mConstant * rangeMeasure[i].first - rangeMeasure[i].second;\n        err += diff * diff;\n\n        sumMeasure += rangeMeasure[i].second;\n    }\n\n    auto n = static_cast<double>(rangeMeasure.size());\n    auto mean = sumMeasure / n;\n    mNormalizedRootMeanSquare = std::sqrt(err / n) / mean;\n}\n\nBigO::BigO(const char* bigOName, RangeMeasure const& rangeMeasure)\n    : BigO(std::string(bigOName), rangeMeasure) {}\n\nstd::string const& BigO::name() const noexcept {\n    return mName;\n}\n\ndouble BigO::constant() const noexcept {\n    return mConstant;\n}\n\ndouble BigO::normalizedRootMeanSquare() const noexcept {\n    return mNormalizedRootMeanSquare;\n}\n\nbool BigO::operator<(BigO const& other) const noexcept {\n    return std::tie(mNormalizedRootMeanSquare, mName) < std::tie(other.mNormalizedRootMeanSquare, other.mName);\n}\n\nstd::ostream& operator<<(std::ostream& os, BigO const& bigO) {\n    return os << bigO.constant() << \" * \" << bigO.name() << \", rms=\" << bigO.normalizedRootMeanSquare();\n}\n\nstd::ostream& operator<<(std::ostream& os, std::vector<ankerl::nanobench::BigO> const& bigOs) {\n    detail::fmt::StreamStateRestorer restorer(os);\n    os << std::endl << \"|   coefficient |   err% | complexity\" << std::endl << \"|--------------:|-------:|------------\" << std::endl;\n    for (auto const& bigO : bigOs) {\n        os << \"|\" << std::setw(14) << std::setprecision(7) << std::scientific << bigO.constant() << \" \";\n        os << \"|\" << detail::fmt::Number(6, 1, bigO.normalizedRootMeanSquare() * 100.0) << \"% \";\n        os << \"| \" << bigO.name();\n        os << std::endl;\n    }\n    return os;\n}\n\n} // namespace nanobench\n} // namespace ankerl\n\n#endif // ANKERL_NANOBENCH_IMPLEMENT\n#endif // ANKERL_NANOBENCH_H_INCLUDED\n"
  },
  {
    "path": "test.c",
    "content": "#include <stdio.h>\n\n#define AM_BUF_LEN 1 /* touch multiple write code */\n#define AM_DEBUG printf\n#define AM_IMPLEMENTATION\n#include \"amoeba.h\"\n\n#include <assert.h>\n#include <stdlib.h>\n#include <setjmp.h>\n#include <stdarg.h>\n\nstatic jmp_buf jbuf;\nstatic size_t allmem = 0;\nstatic size_t maxmem = 0;\nstatic void *END = NULL;\n\nstatic void *debug_allocf(void **pud, void *ptr, size_t ns, size_t os, am_AllocType ty) {\n    void *newptr = NULL;\n    (void)pud, (void)ty;\n    allmem += ns;\n    allmem -= os;\n    if (maxmem < allmem) maxmem = allmem;\n    if (ns == 0) free(ptr);\n    else {\n        newptr = realloc(ptr, ns);\n        if (newptr == NULL) longjmp(jbuf, 1);\n    }\n#ifdef DEBUG_MEMORY\n    printf(\"new(%p):\\t+%d, old(%p):\\t-%d\\n\", newptr, (int)ns, ptr, (int)os);\n#endif\n    return newptr;\n}\n\nstatic void am_dumpkey(am_Symbol sym) {\n    int ch = 'v';\n    switch (sym.type) {\n    case AM_EXTERNAL: ch = 'v'; break;\n    case AM_SLACK:    ch = 's'; break;\n    case AM_ERROR:    ch = 'e'; break;\n    case AM_DUMMY:    ch = 'd'; break;\n    }\n    printf(\"%c%d\", ch, (int)sym.id);\n}\n\nstatic void am_dumprow(const am_Row *row) {\n    am_Iterator it = am_itertable(&row->terms);\n    printf(\"%g\", row->constant);\n    while (am_nextentry(&it)) {\n        am_Num *term = am_val(am_Num,it);\n        am_Num multiplier = *term;\n        printf(\" %c \", multiplier > 0.0 ? '+' : '-');\n        if (multiplier < 0.0) multiplier = -multiplier;\n        if (!am_approx(multiplier, 1.0f))\n            printf(\"%g*\", multiplier);\n        am_dumpkey(it.key);\n    }\n    printf(\"\\n\");\n}\n\nstatic void am_dumpsolver(am_Solver *S) {\n    am_Iterator it = am_itertable(&S->rows);\n    int idx = 0;\n    printf(\"-------------------------------\\n\");\n    printf(\"solver: \");\n    am_dumprow(&S->objective);\n    printf(\"rows(%d):\\n\", S->rows.count);\n    while (am_nextentry(&it)) {\n        printf(\"%d. \", ++idx);\n        am_dumpkey(it.key);\n        printf(\" = \");\n        am_dumprow(am_val(am_Row,it));\n    }\n    printf(\"-------------------------------\\n\");\n}\n\nstatic am_Constraint* new_constraint(am_Solver* in_solver, double in_strength,\n        am_Id in_term1, double in_factor1, int in_relation,\n        double in_constant, ...)\n{\n    int result;\n    va_list argp;\n    am_Constraint* c;\n    assert(in_solver && in_term1);\n    c = am_newconstraint(in_solver, (am_Num)in_strength);\n    if(!c) return 0;\n    am_addterm(c, in_term1, (am_Num)in_factor1);\n    am_setrelation(c, in_relation);\n    if(in_constant) am_addconstant(c, (am_Num)in_constant);\n    va_start(argp, in_constant);\n    while(1) {\n        am_Id va_term = va_arg(argp, am_Id);\n        double va_factor = va_arg(argp, double);\n        if(va_term == 0) break;\n        am_addterm(c, va_term, (am_Num)va_factor);\n    }\n    va_end(argp);\n    result = am_add(c);\n    assert(result == AM_OK);\n    return c;\n}\n\nstatic void test_all(void) {\n    am_Solver *S;\n    am_Id xl, xm, xr, xd;\n    am_Num vxl, vxm, vxr, vxd;\n    am_Constraint *c1, *c2, *c3, *c4, *c5, *c6;\n    am_Constraint *cs[50];\n    int i, ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest all\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(NULL, NULL);\n    assert(S != NULL);\n    for (i = 0; i < 50; ++i) {\n        cs[i] = am_newconstraint(S, AM_REQUIRED);\n        assert(cs[i] != NULL);\n    }\n    for (i = 0; i < 50; ++i) \n        am_delconstraint(cs[i]);\n    am_delsolver(S);\n    assert(allmem == 0);\n\n    S = am_newsolver(debug_allocf, NULL);\n    xl = am_newvariable(S, &vxl);\n    xm = am_newvariable(S, &vxm);\n    xr = am_newvariable(S, &vxr);\n\n    assert(!am_hasedit(S, 0));\n    assert(!am_hasedit(S, xl));\n    assert(!am_hasedit(S, xm));\n    assert(!am_hasedit(S, xr));\n    assert(am_varvalue(S, xl, NULL) == &vxl);\n    assert(am_varvalue(S, xm, NULL) == &vxm);\n    assert(am_varvalue(S, xr, NULL) == &vxr);\n    assert(!am_hasconstraint(NULL));\n\n    xd = am_newvariable(S, &vxd);\n    am_delvariable(S, xd);\n\n    assert(am_setrelation(NULL, AM_GREATEQUAL) == AM_FAILED);\n\n    c1 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c1, xl, 1.0);\n    am_setrelation(c1, AM_GREATEQUAL);\n    ret = am_add(c1);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    assert(am_setrelation(c1, AM_GREATEQUAL) == AM_FAILED);\n    assert(AM_REQUIRED - 10 == AM_REQUIRED);\n    assert(am_setstrength(c1, AM_STRONG) == AM_OK);\n    assert(am_setstrength(c1, AM_REQUIRED) == AM_OK);\n\n    assert(am_hasconstraint(c1));\n    assert(!am_hasedit(S, xl));\n\n    c2 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c2, xl, 1.0);\n    am_setrelation(c2, AM_EQUAL);\n    ret = am_add(c2);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    am_resetsolver(S);\n    am_delconstraint(c1);\n    am_delconstraint(c2);\n    am_dumpsolver(S);\n\n    /* c1: 2*xm == xl + xr */\n    c1 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c1, xm, 2.0);\n    am_setrelation(c1, AM_EQUAL);\n    am_addterm(c1, xl, 1.0);\n    am_addterm(c1, xr, 1.0);\n    ret = am_add(c1);\n    printf(\"c1: marker=%d other=%d\\n\", c1->marker.id, c1->other.id);\n    assert(c1->marker.type == AM_DUMMY && c1->other.type == AM_EXTERNAL);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* c2: xl + 10 <= xr */\n    c2 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c2, xl, 1.0);\n    am_addconstant(c2, 10.0);\n    am_setrelation(c2, AM_LESSEQUAL);\n    am_addterm(c2, xr, 1.0);\n    ret = am_add(c2);\n    printf(\"c2: marker=%d other=%d\\n\", c2->marker.id, c2->other.id);\n    assert(c2->marker.type == AM_SLACK && c2->other.type == AM_EXTERNAL);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* c3: xr <= 100 */\n    c3 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c3, xr, 1.0);\n    am_setrelation(c3, AM_LESSEQUAL);\n    am_addconstant(c3, 100.0);\n    ret = am_add(c3);\n    printf(\"c3: marker=%d other=%d\\n\", c3->marker.id, c3->other.id);\n    assert(c3->marker.type == AM_SLACK && c3->other.type == AM_EXTERNAL);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* c4: xl >= 0 */\n    c4 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c4, xl, 1.0);\n    am_setrelation(c4, AM_GREATEQUAL);\n    am_addconstant(c4, 0.0);\n    ret = am_add(c4);\n    printf(\"c4: marker=%d other=%d\\n\", c4->marker.id, c4->other.id);\n    assert(c4->marker.type == AM_SLACK && c4->other.type == AM_EXTERNAL);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    c5 = am_cloneconstraint(c4, AM_REQUIRED);\n    ret = am_add(c5);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n    am_remove(c5);\n\n    c5 = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c5, xl, 1.0);\n    am_setrelation(c5, AM_EQUAL);\n    am_addconstant(c5, 0.0);\n    ret = am_add(c5);\n    assert(ret == AM_OK);\n\n    c6 = am_cloneconstraint(c4, AM_REQUIRED);\n    ret = am_add(c6);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    am_resetconstraint(c6);\n    am_delconstraint(c6);\n\n    am_remove(c1);\n    am_remove(c2);\n    am_remove(c3);\n    am_remove(c4);\n    am_dumpsolver(S);\n    ret |= am_add(c4);\n    ret |= am_add(c3);\n    ret |= am_add(c2);\n    ret |= am_add(c1);\n    assert(ret == AM_OK);\n\n    am_clearedits(S);\n    am_resetsolver(S);\n    printf(\"after reset\\n\");\n\n    am_dumpsolver(S);\n    ret |= am_add(c1);\n    ret |= am_add(c2);\n    ret |= am_add(c3);\n    ret |= am_add(c4);\n    assert(ret == AM_OK);\n\n    printf(\"after initialize\\n\");\n    am_dumpsolver(S);\n    am_updatevars(S);\n    printf(\"xl: %f, xm: %f, xr: %f\\n\", vxl, vxm, vxr);\n\n    am_addedit(S, xm, AM_MEDIUM);\n    am_dumpsolver(S);\n    am_updatevars(S);\n    printf(\"xl: %f, xm: %f, xr: %f\\n\", vxl, vxm, vxr);\n\n    assert(am_hasedit(S, xm));\n    am_clearedits(S);\n\n    printf(\"suggest to 0.0\\n\");\n    am_suggest(S, xm, 0.0);\n    am_dumpsolver(S);\n    am_updatevars(S);\n    printf(\"xl=%f xm=%f xr=%f\\n\", vxl, vxm, vxr);\n    assert(vxl == 0.f && vxm == 5.f && vxr == 10.f);\n\n    /* make other as infeasible */\n    printf(\"suggest to 100.0\\n\");\n    am_suggest(S, xm, 100.0);\n    am_dumpsolver(S);\n    am_updatevars(S);\n    assert(vxl == 90.f && vxm == 95.f && vxr == 100.f);\n\n    printf(\"suggest to 70.0\\n\");\n    am_suggest(S, xm, 70.0);\n    am_updatevars(S);\n    am_dumpsolver(S);\n    assert(vxl == 65.f && vxm == 70.f && vxr == 75.f);\n\n    printf(\"restore xr to 10.0\\n\");\n    am_deledit(S, xm);\n    am_suggest(S, xr, 10.0);\n    am_updatevars(S);\n    am_dumpsolver(S);\n    assert(vxl == 0.f && vxm == 5.f && vxr == 10.f);\n    am_deledit(S, xr);\n\n    printf(\"suggest to 60.0\\n\");\n    am_suggest(S, xm, 60.0);\n    am_updatevars(S);\n    am_dumpsolver(S);\n    assert(vxl == 20.f && vxm == 60.f && vxr == 100.f);\n\n    printf(\"suggest to 50.0\\n\");\n    am_suggest(S, xm, 50.0);\n    am_updatevars(S);\n    am_dumpsolver(S);\n    assert(vxl == 0.f && vxm == 50.f && vxr == 100.f);\n\n    printf(\"suggest to 40.0\\n\");\n    am_suggest(S, xm, 40.0);\n    am_updatevars(S);\n    am_dumpsolver(S);\n    assert(vxl == 0.f && vxm == 40.f && vxr == 80.f);\n\n    am_deledit(S, xm);\n    am_updatevars(S);\n    am_dumpsolver(S);\n\n    printf(\"xl: %f, xm: %f, xr: %f\\n\", vxl, vxm, vxr);\n\n    /* test dead vars */\n    assert(am_refcount(S, xm) == 2); /* xm & c1 */\n    am_suggest(S, xm, 50);\n    assert(am_refcount(S, xm) == 4); /* xm & c1 & edit & dirty */\n    am_deledit(S, xm); \n    assert(am_refcount(S, xm) == 3); /* xm & c1 & dirty */\n    am_delvariable(S, xm);\n    assert(am_refcount(S, xm) == 2); /* c1 & dirty */\n    am_delconstraint(c1);\n    assert(am_refcount(S, xm) == 1); /* dirty */\n    am_updatevars(S);\n    am_dumpsolver(S);\n\n    am_delsolver(S);\n    printf(\"allmem = %d\\n\", (int)allmem);\n    printf(\"maxmem = %d\\n\", (int)maxmem);\n    assert(allmem == 0);\n    maxmem = 0;\n}\n\nstatic void test_binarytree(void) {\n    const int NUM_ROWS = 9;\n    const int X_OFFSET = 0;\n    int nPointsCount, nResult, nRow;\n    int nCurrentRowPointsCount = 1;\n    int nCurrentRowFirstPointIndex = 0;\n    am_Constraint *pC;\n    am_Solver *pSolver;\n    am_Id *arrX, *arrY;\n    am_Num *numX, *numY;\n\n    printf(\"\\n\\n==========\\ntest binarytree\\n\");\n    arrX = (am_Id*)malloc(2048 * sizeof(am_Id));\n    if (arrX == NULL) return;\n    arrY = arrX + 1024;\n\n    numX = (am_Num*)malloc(2048 * sizeof(am_Num));\n    if (numX == NULL) return;\n    numY = numX + 1024;\n\n    /* Create set of rules to distribute vertexes of a binary tree like this one:\n     *      0\n     *     / \\\n     *    /   \\\n     *   1     2\n     *  / \\   / \\\n     * 3   4 5   6\n     */\n\n    pSolver = am_newsolver(debug_allocf, NULL);\n\n    /* Xroot=500, Yroot=10 */\n    arrX[0] = am_newvariable(pSolver, &numX[0]);\n    arrY[0] = am_newvariable(pSolver, &numY[0]);\n    am_addedit(pSolver, arrX[0], AM_STRONG);\n    am_addedit(pSolver, arrY[0], AM_STRONG);\n    am_suggest(pSolver, arrX[0], 500.0f + X_OFFSET);\n    am_suggest(pSolver, arrY[0], 10.0f);\n\n    for (nRow = 1; nRow < NUM_ROWS; nRow++) {\n        int nPreviousRowFirstPointIndex = nCurrentRowFirstPointIndex;\n        int nPoint, nParentPoint = 0;\n        nCurrentRowFirstPointIndex += nCurrentRowPointsCount;\n        nCurrentRowPointsCount *= 2;\n\n        for (nPoint = 0; nPoint < nCurrentRowPointsCount; nPoint++) {\n            int nIndex = nCurrentRowFirstPointIndex + nPoint;\n            arrX[nIndex] = am_newvariable(pSolver, &numX[nIndex]);\n            arrY[nIndex] = am_newvariable(pSolver, &numY[nIndex]);\n\n            /* Ycur = Yprev_row + 15 */\n            pC = am_newconstraint(pSolver, AM_REQUIRED);\n            am_addterm(pC, arrY[nCurrentRowFirstPointIndex + nPoint], 1.0);\n            am_setrelation(pC, AM_EQUAL);\n            am_addterm(pC, arrY[nCurrentRowFirstPointIndex - 1], 1.0);\n            am_addconstant(pC, 15.0);\n            nResult = am_add(pC);\n            assert(nResult == AM_OK);\n\n            if (nPoint > 0) {\n                /* Xcur >= XPrev + 5 */\n                pC = am_newconstraint(pSolver, AM_REQUIRED);\n                am_addterm(pC, arrX[nCurrentRowFirstPointIndex + nPoint], 1.0);\n                am_setrelation(pC, AM_GREATEQUAL);\n                am_addterm(pC, arrX[nCurrentRowFirstPointIndex + nPoint - 1], 1.0);\n                am_addconstant(pC, 5.0);\n                nResult = am_add(pC);\n                assert(nResult == AM_OK);\n            } else {\n                /* When these lines added it crashes at the line 109 */\n                pC = am_newconstraint(pSolver, AM_REQUIRED);\n                am_addterm(pC, arrX[nCurrentRowFirstPointIndex + nPoint], 1.0);\n                am_setrelation(pC, AM_GREATEQUAL);\n                am_addconstant(pC, 0.0);\n                nResult = am_add(pC);\n                assert(nResult == AM_OK);\n            }\n\n            if ((nPoint % 2) == 1) {\n                /* Xparent = 0.5 * Xcur + 0.5 * Xprev */\n                pC = am_newconstraint(pSolver, AM_REQUIRED);\n                am_addterm(pC, arrX[nPreviousRowFirstPointIndex + nParentPoint], 1.0);\n                am_setrelation(pC, AM_EQUAL);\n                am_addterm(pC, arrX[nCurrentRowFirstPointIndex + nPoint], 0.5);\n                am_addterm(pC, arrX[nCurrentRowFirstPointIndex + nPoint - 1], 0.5);\n                /* It crashes here (at the 3rd call of am_add(...))!  */\n                nResult = am_add(pC);\n                assert(nResult == AM_OK);\n\n                nParentPoint++;\n            }\n        }\n    }\n    nPointsCount = nCurrentRowFirstPointIndex + nCurrentRowPointsCount;\n    (void)nPointsCount;\n\n    /*{\n        int i;\n        for (i = 0; i < nPointsCount; i++)\n            printf(\"Point %d: (%f, %f)\\n\", i,\n                    am_value(arrX[i]), am_value(arrY[i]));\n    }*/\n\n    am_delsolver(pSolver);\n    printf(\"allmem = %d\\n\", (int)allmem);\n    printf(\"maxmem = %d\\n\", (int)maxmem);\n    assert(allmem == 0);\n    free(arrX);\n    maxmem = 0;\n}\n\nstatic void test_unbounded(void) {\n    am_Solver *S;\n    am_Id x, y;\n    am_Num vx, vy;\n    am_Constraint *c;\n    int ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest unbounded\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(debug_allocf, NULL);\n    x = am_newvariable(S, &vx);\n    y = am_newvariable(S, &vy);\n\n    /* 10.0 == 0.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addconstant(c, 10.0);\n    am_setrelation(c, AM_EQUAL);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_UNSATISFIED);\n    am_dumpsolver(S);\n\n    /* 0.0 == 0.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addconstant(c, 0.0);\n    am_setrelation(c, AM_EQUAL);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    am_resetsolver(S);\n\n    /* x >= 10.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    ret = am_addterm(c, x, 1.0);\n    printf(\"addterm = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_setrelation(c, AM_GREATEQUAL);\n    am_addconstant(c, 10.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* x == 2*y */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, x, 1.0);\n    am_setrelation(c, AM_EQUAL);\n    am_addterm(c, y, 2.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* y == 3*x */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, y, 1.0);\n    am_setrelation(c, AM_EQUAL);\n    am_addterm(c, x, 3.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_UNBOUND);\n    am_dumpsolver(S);\n\n    am_resetsolver(S);\n\n    /* x >= 10.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, x, 1.0);\n    am_setrelation(c, AM_GREATEQUAL);\n    am_addconstant(c, 10.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* x <= 0.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, x, 1.0);\n    am_setrelation(c, AM_LESSEQUAL);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_UNBOUND);\n    am_dumpsolver(S);\n\n    printf(\"x: %f\\n\", vx);\n\n    am_resetsolver(S);\n\n    /* x == 10.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, x, 1.0);\n    am_setrelation(c, AM_EQUAL);\n    am_addconstant(c, 10.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    /* x == 20.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, x, 1.0);\n    am_setrelation(c, AM_EQUAL);\n    am_addconstant(c, 20.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_UNSATISFIED);\n    am_dumpsolver(S);\n\n    /* x == 10.0 */\n    c = am_newconstraint(S, AM_REQUIRED);\n    am_addterm(c, x, 1.0);\n    am_setrelation(c, AM_EQUAL);\n    am_addconstant(c, 10.0);\n    ret = am_add(c);\n    printf(\"ret = %d\\n\", ret);\n    assert(ret == AM_OK);\n    am_dumpsolver(S);\n\n    am_delsolver(S);\n    printf(\"allmem = %d\\n\", (int)allmem);\n    printf(\"maxmem = %d\\n\", (int)maxmem);\n    assert(allmem == 0);\n    maxmem = 0;\n}\n\nstatic void test_strength(void) {\n    am_Solver *S;\n    am_Id x, y;\n    am_Num vx, vy;\n    am_Constraint *c;\n    int ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest strength\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(debug_allocf, NULL);\n    am_autoupdate(S, 1);\n    x = am_newvariable(S, &vx);\n    y = am_newvariable(S, &vy);\n\n    /* x <= y */\n    new_constraint(S, AM_STRONG, x, 1.0, AM_LESSEQUAL, 0.0,\n            y, 1.0, END);\n    new_constraint(S, AM_MEDIUM, x, 1.0, AM_EQUAL, 50, END);\n    c = new_constraint(S, AM_MEDIUM-10, y, 1.0, AM_EQUAL, 40, END);\n    printf(\"%f, %f\\n\", vx, vy);\n    assert(vx == 50);\n    assert(vy == 50);\n\n    am_setstrength(c, AM_MEDIUM+10);\n    printf(\"%f, %f\\n\", vx, vy);\n    assert(vx == 40);\n    assert(vy == 40);\n\n    am_setstrength(c, AM_MEDIUM-10);\n    printf(\"%f, %f\\n\", vx, vy);\n    assert(vx == 50);\n    assert(vy == 50);\n\n    am_delsolver(S);\n    printf(\"allmem = %d\\n\", (int)allmem);\n    printf(\"maxmem = %d\\n\", (int)maxmem);\n    assert(allmem == 0);\n    maxmem = 0;\n}\n\nstatic void test_suggest(void) {\n#if 1\n    /* This should be valid but fails the (enter.id != 0) assertion in am_dual_optimize() */\n    am_Num strength1 = AM_REQUIRED;\n    am_Num strength2 = AM_REQUIRED;\n    am_Num width = 76;\n#else\n    /* This mostly works, but still insists on forcing left_child_l = 0 which it should not */\n    am_Num strength1 = AM_STRONG;\n    am_Num strength2 = AM_WEAK;\n    am_Num width = 76;\n#endif\n    am_Num delta = 0;\n    am_Num pos;\n    am_Solver *S;\n    am_Id splitter_l,     splitter_w,     splitter_r;\n    am_Id left_child_l,   left_child_w,   left_child_r;\n    am_Id splitter_bar_l, splitter_bar_w, splitter_bar_r;\n    am_Id right_child_l,  right_child_w,  right_child_r;\n    am_Num vsplitter_l,     vsplitter_w,     vsplitter_r;\n    am_Num vleft_child_l,   vleft_child_w,   vleft_child_r;\n    am_Num vsplitter_bar_l, vsplitter_bar_w, vsplitter_bar_r;\n    am_Num vright_child_l,  vright_child_w,  vright_child_r;\n    int ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest suggest\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(debug_allocf, NULL);\n    splitter_l     = am_newvariable(S, &vsplitter_l);\n    splitter_w     = am_newvariable(S, &vsplitter_w);\n    splitter_r     = am_newvariable(S, &vsplitter_r);\n    left_child_l   = am_newvariable(S, &vleft_child_l);\n    left_child_w   = am_newvariable(S, &vleft_child_w);\n    left_child_r   = am_newvariable(S, &vleft_child_r);\n    splitter_bar_l = am_newvariable(S, &vsplitter_bar_l);\n    splitter_bar_w = am_newvariable(S, &vsplitter_bar_w);\n    splitter_bar_r = am_newvariable(S, &vsplitter_bar_r);\n    right_child_l  = am_newvariable(S, &vright_child_l);\n    right_child_w  = am_newvariable(S, &vright_child_w);\n    right_child_r  = am_newvariable(S, &vright_child_r);\n\n    /* splitter_r = splitter_l + splitter_w */\n    /* left_child_r = left_child_l + left_child_w */\n    /* splitter_bar_r = splitter_bar_l + splitter_bar_w */\n    /* right_child_r = right_child_l + right_child_w */\n    new_constraint(S, AM_REQUIRED, splitter_r, 1.0, AM_EQUAL, 0.0,\n            splitter_l, 1.0, splitter_w, 1.0, END);\n    new_constraint(S, AM_REQUIRED, left_child_r, 1.0, AM_EQUAL, 0.0,\n            left_child_l, 1.0, left_child_w, 1.0, END);\n    new_constraint(S, AM_REQUIRED, splitter_bar_r, 1.0, AM_EQUAL, 0.0,\n            splitter_bar_l, 1.0, splitter_bar_w, 1.0, END);\n    new_constraint(S, AM_REQUIRED, right_child_r, 1.0, AM_EQUAL, 0.0,\n            right_child_l, 1.0, right_child_w, 1.0, END);\n\n    /* splitter_bar_w = 6 */\n    /* splitter_bar_l >= splitter_l + delta */\n    /* splitter_bar_r <= splitter_r - delta */\n    /* left_child_r = splitter_bar_l */\n    /* right_child_l = splitter_bar_r */\n    new_constraint(S, AM_REQUIRED, splitter_bar_w, 1.0, AM_EQUAL, 6.0, END);\n    new_constraint(S, AM_REQUIRED, splitter_bar_l, 1.0, AM_GREATEQUAL,\n            delta, splitter_l, 1.0, END);\n    new_constraint(S, AM_REQUIRED, splitter_bar_r, 1.0, AM_LESSEQUAL,\n            -delta, splitter_r, 1.0, END);\n    new_constraint(S, AM_REQUIRED, left_child_r, 1.0, AM_EQUAL, 0.0,\n            splitter_bar_l, 1.0, END);\n    new_constraint(S, AM_REQUIRED, right_child_l, 1.0, AM_EQUAL, 0.0,\n            splitter_bar_r, 1.0, END);\n\n    /* right_child_r >= splitter_r + 1 */\n    /* left_child_w = 256 */\n    new_constraint(S, strength1, right_child_r, 1.0, AM_GREATEQUAL, 1.0,\n            splitter_r, 1.0, END);\n    new_constraint(S, strength2, left_child_w, 1.0, AM_EQUAL, 256.0, END);\n\n    /* splitter_l = 0 */\n    /* splitter_r = 76 */\n    new_constraint(S, AM_REQUIRED, splitter_l, 1.0, AM_EQUAL, 0.0, END);\n    new_constraint(S, AM_REQUIRED, splitter_r, 1.0, AM_EQUAL, width, END);\n\n    printf(\"\\n\\n==========\\ntest suggest\\n\");\n    for(pos = -10; pos < 86; pos++) {\n        am_suggest(S, splitter_bar_l, pos);\n        /* printf(\"pos: %4g | \", pos);\n        printf(\"splitter_l l=%2g, w=%2g, r=%2g | \", vsplitter_l,\n                vsplitter_w, vsplitter_r);\n        printf(\"left_child_l l=%2g, w=%2g, r=%2g | \", vleft_child_l,\n                vleft_child_w, vleft_child_r);\n        printf(\"splitter_bar_l l=%2g, w=%2g, r=%2g | \", vsplitter_bar_l,\n                vsplitter_bar_w, vsplitter_bar_r);\n        printf(\"right_child_l l=%2g, w=%2g, r=%2g | \", vright_child_l,\n                vright_child_w, vright_child_r);\n        printf(\"\\n\"); */\n    }\n\n    am_delsolver(S);\n    printf(\"allmem = %d\\n\", (int)allmem);\n    printf(\"maxmem = %d\\n\", (int)maxmem);\n    assert(allmem == 0);\n    maxmem = 0;\n}\n\nstatic void test_dirty(void) {\n    am_Solver *S;\n    am_Id xl, xr, xw, xwc;\n    am_Num vxl, vxr, vxw, vxwc;\n    am_Constraint *c1, *c2;\n    int ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest dirty\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(debug_allocf, NULL);\n\n    xl  = am_newvariable(S, &vxl);\n    xr  = am_newvariable(S, &vxr);\n    xw  = am_newvariable(S, &vxw);\n    xwc = am_newvariable(S, &vxwc);\n\n    /* c1: xw == xr -xl */\n    c1 = am_newconstraint(S, AM_REQUIRED);\n    ret = 0;\n    ret |= am_addterm(c1, xw, 1.0);\n    ret |= am_setrelation(c1, AM_EQUAL);\n    ret |= am_addterm(c1, xr, 1.0);\n    ret |= am_addterm(c1, xl, -1.0);\n    ret |= am_add(c1);\n    assert(ret == AM_OK);\n\n    am_updatevars(S);\n    printf(\"xl: %f, xr:%f, xw:%f\\n\", vxl, vxr, vxw);\n\n    /* c1: xwc == xw */\n    c2 = am_newconstraint(S, AM_REQUIRED);\n    ret = 0;\n    ret |= am_addterm(c2, xwc, 1.0);\n    ret |= am_setrelation(c2, AM_EQUAL);\n    ret |= am_addterm(c2, xw, 1.0);\n    ret |= am_add(c2);\n    assert(ret == AM_OK);\n\n    /* Sets dirty bit? Related to crash. */\n    am_suggest(S, xwc, 10);\n\n    am_updatevars(S);\n    printf(\"xl:%f, xr:%f, xw:%f, xwc:%f\\n\", vxl, vxr, vxw, vxwc);\n\n    /* Remove xwc and c2 */\n    am_deledit(S, xwc);\n    am_remove(c2);\n    /* Adding an am_updatevars(S); here somehow solves the issue. */\n    am_delconstraint(c2);\n    am_delvariable(S, xwc);\n\n    /* Causes crash: amoeba.h:482: am_sym2var: Assertion `ve != NULL' failed. */\n    am_updatevars(S);\n    printf(\"xl:%f, xr:%f, xw:%f\\n\", vxl, vxr, vxw);\n\n    /* Manual cleanup */\n    am_remove(c1);\n    am_delconstraint(c1);\n\n    am_delvariable(S, xl);\n    am_delvariable(S, xr);\n    am_delvariable(S, xw);\n\n    am_delsolver(S);\n}\n\nvoid test_cycling(void) {\n    am_Solver *S;\n    am_Id va, vb, vc, vd;\n    am_Num vva, vvb, vvc, vvd;\n\n    int ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest cycling\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(debug_allocf, NULL);\n    va = am_newvariable(S, &vva);\n    vb = am_newvariable(S, &vvb);\n    vc = am_newvariable(S, &vvc);\n    vd = am_newvariable(S, &vvd);\n\n    am_addedit(S, va, AM_STRONG);\n    printf(\"after edit\\n\");\n    am_dumpsolver(S);\n\n    /* vb == va */\n    {\n        am_Constraint * c = am_newconstraint(S, AM_REQUIRED);\n        int ret = 0;\n        ret |= am_addterm(c, vb, 1.0);\n        ret |= am_setrelation(c, AM_EQUAL);\n        ret |= am_addterm(c, va, 1.0);\n        ret |= am_add(c);\n        assert(ret == AM_OK);\n        am_dumpsolver(S);\n    }\n\n    /* vb == vc */\n    {\n        am_Constraint * c = am_newconstraint(S, AM_REQUIRED);\n        int ret = 0;\n        ret |= am_addterm(c, vb, 1.0);\n        ret |= am_setrelation(c, AM_EQUAL);\n        ret |= am_addterm(c, vc, 1.0);\n        ret |= am_add(c);\n        assert(ret == AM_OK);\n        am_dumpsolver(S);\n    }\n\n    /* vc == vd */\n    {\n        am_Constraint * c = am_newconstraint(S, AM_REQUIRED);\n        int ret = 0;\n        ret |= am_addterm(c, vc, 1.0);\n        ret |= am_setrelation(c, AM_EQUAL);\n        ret |= am_addterm(c, vd, 1.0);\n        ret |= am_add(c);\n        assert(ret == AM_OK);\n        am_dumpsolver(S);\n    }\n\n    /* vd == va */\n    {\n        am_Constraint * c = am_newconstraint(S, AM_REQUIRED);\n        int ret = 0;\n        ret |= am_addterm(c, vd, 1.0);\n        ret |= am_setrelation(c, AM_EQUAL);\n        ret |= am_addterm(c, va, 1.0);\n        ret |= am_add(c);\n        assert(ret == AM_OK); /* asserts here */\n        am_dumpsolver(S);\n    }\n\n    am_delsolver(S);\n}\n\n#define TEST_BUFSIZE 16384\n\ntypedef struct MyDumper {\n    am_Dumper base;\n    char *p;\n    size_t *len;\n} MyDumper;\n\nstatic const char *dump_varname(am_Dumper *d, unsigned idx, am_Id var, am_Num *value) {\n    const char *names[] = { \"width\", \"height\", \"a\", \"\" };\n    (void)d, (void)var, (void)value;\n    return idx < 4 ? names[idx] : NULL;\n}\n\nstatic const char *dump_consname(am_Dumper *d, unsigned idx, am_Constraint *cons) {\n    const char *name = \"a-very-long-name-that-excceed-32-bytes\";\n    (void)d, (void)idx, (void)cons;\n    return idx == 0 ? name : NULL;\n}\n\nstatic int dump_writer(am_Dumper *d, const void *buf, size_t len) {\n    MyDumper *md = (MyDumper*)d;\n    assert(*md->len + len <= TEST_BUFSIZE);\n    if (*md->len + len > TEST_BUFSIZE) return AM_FAILED;\n    memcpy(md->p + *md->len, buf, len);\n    *md->len += len;\n    return AM_OK;\n}\n\ntypedef struct MyLoader {\n    am_Loader base;\n    am_Num values[37];\n    const char *p;\n    size_t len;\n} MyLoader;\n\nstatic am_Num *load_var(am_Loader *l, const char *name, unsigned i, am_Id var) {\n    MyLoader *ml = (MyLoader*)l;\n    return (void)name, (void)var, &ml->values[i];\n}\n\nvoid load_cons(am_Loader *l, const char *name, unsigned i, am_Constraint *cons)\n{ (void)l, (void)name, (void)i, (void)cons; }\n\nconst char *load_reader(am_Loader *l, size_t *plen) {\n    MyLoader *ml = (MyLoader*)l;\n    /* reads from char by char */\n    if (ml->len == 0) return NULL;\n    ml->len -= 1;\n    return *plen = 1, ml->p++;\n}\n\nstatic void build_solver(am_Solver* S, am_Id width, am_Id height, am_Num *values) {\n    /* Create custom strength */\n    am_Num mmedium = AM_MEDIUM * 1.25;\n    am_Num smedium = AM_MEDIUM * 100;\n\n    /* Create the variable */\n    am_Id left            = am_newvariable(S, &values[0]);\n    am_Id top             = am_newvariable(S, &values[1]);\n    am_Id contents_top    = am_newvariable(S, &values[2]);\n    am_Id contents_bottom = am_newvariable(S, &values[3]);\n    am_Id contents_left   = am_newvariable(S, &values[4]);\n    am_Id contents_right  = am_newvariable(S, &values[5]);\n    am_Id midline         = am_newvariable(S, &values[6]);\n    am_Id ctleft          = am_newvariable(S, &values[7]);\n    am_Id ctheight        = am_newvariable(S, &values[8]);\n    am_Id cttop           = am_newvariable(S, &values[9]);\n    am_Id ctwidth         = am_newvariable(S, &values[10]);\n    am_Id lb1left         = am_newvariable(S, &values[11]);\n    am_Id lb1height       = am_newvariable(S, &values[12]);\n    am_Id lb1top          = am_newvariable(S, &values[13]);\n    am_Id lb1width        = am_newvariable(S, &values[14]);\n    am_Id lb2left         = am_newvariable(S, &values[15]);\n    am_Id lb2height       = am_newvariable(S, &values[16]);\n    am_Id lb2top          = am_newvariable(S, &values[17]);\n    am_Id lb2width        = am_newvariable(S, &values[18]);\n    am_Id lb3left         = am_newvariable(S, &values[19]);\n    am_Id lb3height       = am_newvariable(S, &values[20]);\n    am_Id lb3top          = am_newvariable(S, &values[21]);\n    am_Id lb3width        = am_newvariable(S, &values[22]);\n    am_Id fl1left         = am_newvariable(S, &values[23]);\n    am_Id fl1height       = am_newvariable(S, &values[24]);\n    am_Id fl1top          = am_newvariable(S, &values[25]);\n    am_Id fl1width        = am_newvariable(S, &values[26]);\n    am_Id fl2left         = am_newvariable(S, &values[27]);\n    am_Id fl2height       = am_newvariable(S, &values[28]);\n    am_Id fl2top          = am_newvariable(S, &values[29]);\n    am_Id fl2width        = am_newvariable(S, &values[30]);\n    am_Id fl3left         = am_newvariable(S, &values[31]);\n    am_Id fl3height       = am_newvariable(S, &values[32]);\n    am_Id fl3top          = am_newvariable(S, &values[33]);\n    am_Id fl3width        = am_newvariable(S, &values[34]);\n\n#ifdef __GNUC__\n# pragma GCC diagnostic push\n# pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\n# if __STDC_VERSION__ < 199901L\n#   pragma GCC diagnostic ignored \"-Wc99-extensions\"\n# endif\n#endif\n\n    /* Add the constraints */\n    const struct Info {\n        struct Item {\n            am_Id  var;\n            am_Num mul;\n        } term[5];\n        am_Num constant;\n        int    relation;\n        am_Num strength;\n    } constraints[] = {\n        { {{left}},                                                -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{height}},                                              0,            AM_EQUAL,      AM_MEDIUM   },\n        { {{top}},                                                 -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{width}},                                               -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{height}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{top,-1},{contents_top}},                               -10,          AM_EQUAL,      AM_REQUIRED },\n        { {{lb3height}},                                           -16,          AM_EQUAL,      AM_STRONG   },\n        { {{lb3height}},                                           -16,          AM_GREATEQUAL, AM_STRONG   },\n        { {{ctleft}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{cttop}},                                               -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctwidth}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3left}},                                             0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight}},                                            -24,          AM_GREATEQUAL, smedium     },\n        { {{ctwidth}},                                             -1.67772e+07, AM_LESSEQUAL,  smedium     },\n        { {{ctheight}},                                            -24,          AM_LESSEQUAL,  smedium     },\n        { {{fl3top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1width}},                                            -67,          AM_EQUAL,      AM_WEAK     },\n        { {{lb2width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2width}},                                            -125,         AM_GREATEQUAL, AM_STRONG   },\n        { {{fl2height}},                                           -21,          AM_EQUAL,      AM_STRONG   },\n        { {{fl2height}},                                           -21,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb3top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1width}},                                            -67,          AM_GREATEQUAL, AM_STRONG   },\n        { {{fl2left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2width}},                                            -66,          AM_EQUAL,      AM_WEAK     },\n        { {{lb2width}},                                            -66,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb2height}},                                           -16,          AM_EQUAL,      AM_STRONG   },\n        { {{fl1height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top,-1},{lb3top},{lb2height,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{lb3top,-1},{lb3height,-1},{fl3top}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3top,-1},{lb3height,-1},{fl3top}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_EQUAL,      mmedium     },\n        { {{fl1top},{contents_top,-1}},                            0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1top},{contents_top,-1}},                            0,            AM_EQUAL,      mmedium     },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{left,-1},{width,-1},{contents_right}},                 10,           AM_EQUAL,      AM_REQUIRED },\n        { {{top,-1},{height,-1},{contents_bottom}},                10,           AM_EQUAL,      AM_REQUIRED },\n        { {{left,-1},{contents_left}},                             -10,          AM_EQUAL,      AM_REQUIRED },\n        { {{lb3left},{contents_left,-1}},                          0,            AM_EQUAL,      mmedium     },\n        { {{fl1left},{midline,-1}},                                0,            AM_EQUAL,      AM_STRONG   },\n        { {{fl2left},{midline,-1}},                                0,            AM_EQUAL,      AM_STRONG   },\n        { {{ctleft},{midline,-1}},                                 0,            AM_EQUAL,      AM_STRONG   },\n        { {{fl1top},{fl1height,0.5},{lb1top,-1},{lb1height,-0.5}}, 0,            AM_EQUAL,      AM_STRONG   },\n        { {{lb1left},{contents_left,-1}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left},{contents_left,-1}},                          0,            AM_EQUAL,      mmedium     },\n        { {{lb1left,-1},{fl1left},{lb1width,-1}},                  -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left,-1},{fl1left},{lb1width,-1}},                  -10,          AM_EQUAL,      mmedium     },\n        { {{fl1left,-1},{contents_right},{fl1width,-1}},           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{width}},                                               0,            AM_EQUAL,      AM_MEDIUM   },\n        { {{fl1top,-1},{fl2top},{fl1height,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl1top,-1},{fl2top},{fl1height,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{cttop},{fl2top,-1},{fl2height,-1}},                    -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight,-1},{cttop,-1},{fl3top}},                     -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{cttop},{fl2top,-1},{fl2height,-1}},                    -10,          AM_EQUAL,      mmedium     },\n        { {{fl1left,-1},{contents_right},{fl1width,-1}},           -0,           AM_EQUAL,      mmedium     },\n        { {{lb2top,-1},{lb2height,-0.5},{fl2top},{fl2height,0.5}}, 0,            AM_EQUAL,      AM_STRONG   },\n        { {{contents_left,-1},{lb2left}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{contents_left,-1},{lb2left}},                          0,            AM_EQUAL,      mmedium     },\n        { {{fl2left},{lb2width,-1},{lb2left,-1}},                  -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight,-1},{cttop,-1},{fl3top}},                     -10,          AM_EQUAL,      mmedium     },\n        { {{contents_bottom},{fl3height,-1},{fl3top,-1}},          -0,           AM_EQUAL,      mmedium     },\n        { {{lb1top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2left},{lb2width,-1},{lb2left,-1}},                  -10,          AM_EQUAL,      mmedium     },\n        { {{fl2left,-1},{fl2width,-1},{contents_right}},           -0,           AM_EQUAL,      mmedium     },\n        { {{fl2left,-1},{fl2width,-1},{contents_right}},           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb3left},{contents_left,-1}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctheight,0.5},{cttop},{lb3top,-1},{lb3height,-0.5}},   0,            AM_EQUAL,      AM_STRONG   },\n        { {{ctleft},{lb3left,-1},{lb3width,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctwidth,-1},{ctleft,-1},{contents_right}},             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctleft},{lb3left,-1},{lb3width,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{fl3left},{contents_left,-1}},                          0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl3left},{contents_left,-1}},                          0,            AM_EQUAL,      mmedium     },\n        { {{ctwidth,-1},{ctleft,-1},{contents_right}},             -0,           AM_EQUAL,      mmedium     },\n        { {{fl3left,-1},{contents_right},{fl3width,-1}},           -0,           AM_EQUAL,      mmedium     },\n        { {{contents_top,-1},{lb1top}},                            0,            AM_GREATEQUAL, AM_REQUIRED },\n        { {{contents_top,-1},{lb1top}},                            0,            AM_EQUAL,      mmedium     },\n        { {{fl3left,-1},{contents_right},{fl3width,-1}},           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top},{lb1top,-1},{lb1height,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top,-1},{lb3top},{lb2height,-1}},                   -10,          AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2top},{lb1top,-1},{lb1height,-1}},                   -10,          AM_EQUAL,      mmedium     },\n        { {{fl1height}},                                           -21,          AM_EQUAL,      AM_STRONG   },\n        { {{fl1height}},                                           -21,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb2left}},                                             -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb2height}},                                           -16,          AM_GREATEQUAL, AM_STRONG   },\n        { {{fl2top}},                                              -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{fl2width}},                                            -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{lb1height}},                                           -16,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb1height}},                                           -16,          AM_EQUAL,      AM_STRONG   },\n        { {{fl3width}},                                            -125,         AM_GREATEQUAL, AM_STRONG   },\n        { {{fl3height}},                                           -21,          AM_EQUAL,      AM_STRONG   },\n        { {{fl3height}},                                           -21,          AM_GREATEQUAL, AM_STRONG   },\n        { {{lb3height}},                                           -0,           AM_GREATEQUAL, AM_REQUIRED },\n        { {{ctwidth}},                                             -119,         AM_GREATEQUAL, smedium     },\n        { {{lb3width}},                                            -24,          AM_EQUAL,      AM_WEAK     },\n        { {{lb3width}},                                            -24,          AM_GREATEQUAL, AM_STRONG   },\n        { {{fl1width}},                                            -125,         AM_GREATEQUAL, AM_STRONG   },\n    };\n\n#ifdef __GNUC__\n# pragma GCC diagnostic pop\n#endif\n\n    size_t i;\n    int ret;\n\n    /* Add the edit variables */\n    ret = am_addedit(S, width, AM_STRONG);\n    assert(ret == AM_OK), (void)ret;\n    ret = am_addedit(S, height, AM_STRONG);\n    assert(ret == AM_OK), (void)ret;\n\n    for (i = 0; i < sizeof(constraints)/sizeof(constraints[0]); ++i) {\n        am_Constraint *c = am_newconstraint(S, constraints[i].strength);\n        const struct Item *p;\n        for (p = constraints[i].term; p->var; ++p) {\n            ret = am_addterm(c, p->var, p->mul ? p->mul : 1);\n            assert(ret == AM_OK), (void)ret;\n        }\n        ret = am_addconstant(c, constraints[i].constant);\n        assert(ret == AM_OK), (void)ret;\n        ret = am_setrelation(c, constraints[i].relation);\n        assert(ret == AM_OK), (void)ret;\n        ret = am_add(c);\n        assert(ret == AM_OK), (void)ret;\n    }\n}\n\nstatic void test_dumpload(void) {\n    am_Num w, h, values[35];\n    am_Solver *S;\n    am_Id width, height;\n    char buf[TEST_BUFSIZE];\n    size_t len = 0;\n    int ret = setjmp(jbuf);\n    printf(\"\\n\\n==========\\ntest dumpload\\n\");\n    printf(\"ret = %d\\n\", ret);\n    if (ret < 0) { perror(\"setjmp\"); return; }\n    else if (ret != 0) { printf(\"out of memory!\\n\"); return; }\n\n    S = am_newsolver(debug_allocf, NULL);\n    assert(S != NULL);\n\n    width = am_newvariable(S, &w);\n    height = am_newvariable(S, &h);\n\n    build_solver(S, width, height, values);\n\n    {\n        /* dump converage */\n        MyDumper d;\n        am_DumpCtx ctx;\n        char buf[10];\n        size_t len = 0;\n        d.base.writer = dump_writer;\n        d.p = buf;\n        d.len = &len;\n        ctx.dumper = &d.base;\n        ctx.p = ctx.buf;\n\n        assert(am_writeraw(NULL, 0, 0) == AM_FAILED);\n        assert(am_writeuint32(&ctx, 0xFFFFFFFF) == AM_OK);\n        ctx.ret = dump_writer(&d.base, ctx.buf, (ctx.p - ctx.buf));\n        assert(len == 5);\n        assert(memcmp(buf, \"\\xCE\\xFF\\xFF\\xFF\\xFF\", 5) == 0);\n\n        len = 0;\n        ctx.p = ctx.buf;\n        assert(am_writecount(&ctx, 0xFFFFFFFF) == AM_OK);\n        ctx.ret = dump_writer(&d.base, ctx.buf, (ctx.p - ctx.buf));\n        assert(len == 5);\n        assert(memcmp(buf, \"\\xDD\\xFF\\xFF\\xFF\\xFF\", 5) == 0);\n    }\n\n    {\n        /* load coverage */\n        am_LoadCtx ctx;\n        am_Size value;\n        am_Num flt;\n\n        assert(am_readraw_slow(NULL, NULL, 0) == AM_FAILED);\n        ctx.p = \"\\xCE\\xFF\\xFF\\xFF\\xFF\", ctx.n = 5;\n        assert(am_readuint32(&ctx, &value) == AM_OK);\n        assert(value == 0xFFFFFFFF && ctx.n == 0);\n\n        ctx.p = \"\\xFF\\xFF\", ctx.n = 2;\n        assert(am_readraw16(&ctx, &value) == AM_OK);\n        assert(value == 0xFFFF && ctx.n == 0);\n\n        ctx.p = \"\\xCA\\x0\\x0\\x0\\x0\", ctx.n = 5;\n        assert(am_readfloat(&ctx, &flt) == AM_OK);\n        printf(\"%f %d\\n\", flt, (int)ctx.n);\n        assert(ctx.n == 0);\n\n        ctx.p = \"\\xCC\\x0\\x0\\x0\\x0\", ctx.n = 5;\n        assert(am_readfloat(&ctx, &flt) == AM_FAILED);\n        assert(ctx.n == 4);\n\n        ctx.p = \"\\xDD\\xFF\\xFF\\xFF\\xFF\", ctx.n = 5;\n        assert(am_readcount(&ctx, &value) == AM_OK);\n        assert(value == 0xFFFFFFFF && ctx.n == 0);\n    }\n\n    {\n        MyDumper myd;\n        myd.base.var_name = dump_varname;\n        myd.base.cons_name = dump_consname;\n        myd.base.writer = dump_writer;\n        myd.p = buf, myd.len = &len;\n\n        printf(\"before dump\\n\");\n        ret = am_dump(S, &myd.base);\n        printf(\"after dump: ret=%d\\n\", ret);\n        assert(ret == AM_OK);\n        printf(\"dumpped len=%d\\n\", (int)len);\n        assert(len == 10709 || len == 10779);\n    }\n\n    {\n        FILE *fp = fopen(\"dump.data\", \"wb\");\n        fwrite(buf, 1, len, fp);\n        fclose(fp);\n    }\n\n    {\n        MyLoader myl;\n        am_Solver *S = am_newsolver(debug_allocf, NULL);\n        assert(S != NULL);\n\n        myl.base.load_var  = load_var;\n        myl.base.load_cons = load_cons;\n        myl.base.reader = load_reader;\n        myl.p =buf, myl.len = len;\n\n        printf(\"before load\\n\");\n        ret = am_load(S, &myl.base);\n        assert(ret == AM_OK);\n\n        am_delsolver(S);\n    }\n    \n    am_delsolver(S);\n    printf(\"allmem = %d\\n\", (int)allmem);\n    printf(\"maxmem = %d\\n\", (int)maxmem);\n    assert(allmem == 0);\n    maxmem = 0;\n}\n\nint main(void) {\n    test_binarytree();\n    test_unbounded();\n    test_strength();\n    test_suggest();\n    test_cycling();\n    test_dirty();\n    test_dumpload();\n    test_all();\n    printf(\"constraint: %d\\n\", (int)(sizeof(am_Constraint)));\n    printf(\"table: %d\\n\", (int)(sizeof(am_Table)));\n    printf(\"row: %d\\n\", (int)(sizeof(am_Row)));\n    return 0;\n}\n\n/* cc: flags='-ggdb -Wall -fsanitize=address -fprofile-arcs -ftest-coverage -O3 -Wextra -pedantic -std=c89'\n * cc: run='!rm -f *.gcda; $executable $args' */\n\n"
  },
  {
    "path": "test.lua",
    "content": "package.path = \"\"\nlocal amoeba = require \"amoeba\"\n\nlocal S = amoeba.new(true)\nprint(S)\nlocal xl, xm, xr =\n   S:var \"xl\", S:var \"xm\", S:var \"xr\"\nprint(xl)\nprint(xm)\nprint(xr)\nprint(S:constraint()\n      :add(xl):add(10)\n      :relation \"le\" -- or \"<=\"\n      :add(xr))\nS:addconstraint((xm*2) :eq (xl + xr))\nS:addconstraint(\n   S:constraint()\n      :add(xl):add(10)\n      :relation \"le\" -- or \"<=\"\n      :add(xr)) -- (xl + 10) :le (xr)\nS:addconstraint(\n   S:constraint()(xr) \"<=\" (100)) -- (xr) :le (100)\nS:addconstraint((xl) :ge (0))\nprint(S)\nprint(xl)\nprint(xm)\nprint(xr)\n\nprint('suggest xm to 0')\nS:suggest(xm, 0)\nprint(S)\nprint(xl)\nprint(xm)\nprint(xr)\n\nprint('suggest xm to 70')\nS:suggest(xm, 70)\nprint(S)\nprint(xl)\nprint(xm)\nprint(xr)\n\nprint('delete edit xm')\nS:deledit(xm)\nprint(S)\nprint(xl)\nprint(xm)\nprint(xr)\n\n"
  },
  {
    "path": "yoga_benchmark.cpp",
    "content": "#include <yoga/Yoga.h>\n\n#define ANKERL_NANOBENCH_IMPLEMENT\n#include \"nanobench.h\"\n\nYGNodeRef build_layout(float width, float height) {\n    YGConfigRef config = YGConfigNew();\n    YGConfigSetUseWebDefaults(config, false);\n    \n    YGNodeRef root_node = YGNodeNewWithConfig(config);\n    YGNodeStyleSetWidth(root_node, width);\n    YGNodeStyleSetHeight(root_node, height);\n    YGNodeStyleSetPadding(root_node, YGEdgeAll, 10);\n    YGNodeStyleSetGap(root_node, YGGutterAll, 10);\n    YGNodeStyleSetFlexDirection(root_node, YGFlexDirectionColumn);\n\n    // row 1\n    YGNodeRef row1 = YGNodeNew();\n    YGNodeStyleSetFlexDirection(row1, YGFlexDirectionRow);\n    YGNodeStyleSetGap(row1, YGGutterAll, 10);\n    \n    YGNodeRef row1_left = YGNodeNew();\n    YGNodeStyleSetFlexGrow(row1_left, 1);\n    YGNodeStyleSetFlexBasis(row1_left, 0);\n    YGNodeStyleSetJustifyContent(row1_left, YGJustifyCenter);\n    YGNodeStyleSetAlignItems(row1_left, YGAlignFlexEnd);\n    \n    YGNodeRef lb1 = YGNodeNew();\n    YGNodeStyleSetMinWidth(lb1, 67);\n    YGNodeStyleSetMinHeight(lb1, 16);\n    YGNodeInsertChild(row1_left, lb1, 0);\n    \n    YGNodeRef row1_right = YGNodeNew();\n    YGNodeStyleSetFlexGrow(row1_right, 1);\n    YGNodeStyleSetFlexBasis(row1_right, 0);\n    YGNodeStyleSetJustifyContent(row1_right, YGJustifyCenter);\n    YGNodeStyleSetAlignItems(row1_right, YGAlignFlexStart);\n    \n    YGNodeRef fl1 = YGNodeNew();\n    YGNodeStyleSetWidth(fl1, 125);\n    YGNodeStyleSetMinHeight(fl1, 21);\n    YGNodeInsertChild(row1_right, fl1, 0);\n    \n    YGNodeInsertChild(row1, row1_left, 0);\n    YGNodeInsertChild(row1, row1_right, 1);\n    \n    // row 2\n    YGNodeRef row2 = YGNodeNew();\n    YGNodeStyleSetFlexDirection(row2, YGFlexDirectionRow);\n    YGNodeStyleSetGap(row2, YGGutterAll, 10);\n    \n    YGNodeRef row2_left = YGNodeNew();\n    YGNodeStyleSetFlexGrow(row2_left, 1);\n    YGNodeStyleSetFlexBasis(row2_left, 0);\n    YGNodeStyleSetJustifyContent(row2_left, YGJustifyCenter);\n    YGNodeStyleSetAlignItems(row2_left, YGAlignFlexEnd);\n    \n    YGNodeRef lb2 = YGNodeNew();\n    YGNodeStyleSetMinWidth(lb2, 60);\n    YGNodeStyleSetMinHeight(lb2, 16);\n    YGNodeInsertChild(row2_left, lb2, 0);\n    \n    YGNodeRef row2_right = YGNodeNew();\n    YGNodeStyleSetFlexGrow(row2_right, 1);\n    YGNodeStyleSetFlexBasis(row2_right, 0);\n    YGNodeStyleSetJustifyContent(row2_right, YGJustifyCenter);\n    YGNodeStyleSetAlignItems(row2_right, YGAlignFlexStart);\n    \n    YGNodeRef fl2 = YGNodeNew();\n    YGNodeStyleSetWidth(fl2, 125);\n    YGNodeStyleSetMinHeight(fl2, 21);\n    YGNodeInsertChild(row2_right, fl2, 0);\n    \n    YGNodeInsertChild(row2, row2_left, 0);\n    YGNodeInsertChild(row2, row2_right, 1);\n    \n    // row 3\n    YGNodeRef row3 = YGNodeNew();\n    YGNodeStyleSetFlexDirection(row3, YGFlexDirectionRow);\n    YGNodeStyleSetGap(row3, YGGutterAll, 10);\n\n    YGNodeRef row3_left = YGNodeNew();\n    YGNodeStyleSetFlexGrow(row3_left, 1);\n    YGNodeStyleSetFlexBasis(row3_left, 0);\n    YGNodeStyleSetJustifyContent(row3_left, YGJustifyCenter);\n    YGNodeStyleSetAlignItems(row3_left, YGAlignFlexEnd);\n    \n    YGNodeRef lb3 = YGNodeNew();\n    YGNodeStyleSetMinWidth(lb3, 24);\n    YGNodeStyleSetMinHeight(lb3, 16);\n    YGNodeInsertChild(row3_left, lb3, 0);\n    \n    YGNodeRef row3_right = YGNodeNew();\n    YGNodeStyleSetFlexGrow(row3_right, 1);\n    YGNodeStyleSetFlexBasis(row3_right, 0);\n    YGNodeStyleSetJustifyContent(row3_right, YGJustifyCenter);\n    YGNodeStyleSetAlignItems(row3_right, YGAlignFlexStart);\n    \n    YGNodeRef ct = YGNodeNew();\n    YGNodeStyleSetWidth(ct, 119);\n    YGNodeStyleSetHeight(ct, 24);\n    YGNodeInsertChild(row3_right, ct, 0);\n    \n    YGNodeInsertChild(row3, row3_left, 0);\n    YGNodeInsertChild(row3, row3_right, 1);\n    \n    // fl3 row\n    YGNodeRef fl3_row = YGNodeNew();\n    YGNodeStyleSetFlexDirection(fl3_row, YGFlexDirectionRow);\n    YGNodeStyleSetFlexGrow(fl3_row, 1);\n    YGNodeStyleSetJustifyContent(fl3_row, YGJustifyFlexEnd);\n    YGNodeStyleSetAlignItems(fl3_row, YGAlignFlexEnd);\n    \n    YGNodeRef fl3 = YGNodeNew();\n    YGNodeStyleSetWidth(fl3, 125);\n    YGNodeStyleSetMinHeight(fl3, 21);\n    YGNodeInsertChild(fl3_row, fl3, 0);\n    \n    // makeup root node\n    YGNodeInsertChild(root_node, row1, 0);\n    YGNodeInsertChild(root_node, row2, 1);\n    YGNodeInsertChild(root_node, row3, 2);\n    YGNodeInsertChild(root_node, fl3_row, 3);\n    \n    YGNodeCalculateLayout(root_node, YGUndefined, YGUndefined, YGDirectionLTR);\n    return root_node;\n}\n\nvoid suggest(YGNodeRef root_node, float width, float height) {\n    YGNodeStyleSetWidth(root_node, 300); /* mark dirty */\n    YGNodeStyleSetWidth(root_node, width);\n    YGNodeStyleSetHeight(root_node, 200); /* mark dirty */\n    YGNodeStyleSetHeight(root_node, height);\n    YGNodeCalculateLayout(root_node, width, height, YGDirectionLTR);\n}\n\nint main() {\n    ankerl::nanobench::Bench().minEpochIterations(1000).run(\"building solver\", [&] {\n        YGNodeRef root_node = build_layout(300, 200);\n        ankerl::nanobench::doNotOptimizeAway(root_node);\n        YGNodeFreeRecursive(root_node);\n    });\n\n    struct Size {\n        int width;\n        int height;\n    };\n\n    Size sizes[] = {\n        { 400, 600 },\n        { 600, 400 },\n        { 800, 1200 },\n        { 1200, 800 },\n        { 400, 800 },\n        { 800, 400 }\n    };\n\n    YGNodeRef root_node = build_layout(300, 200);\n\n    for (const Size& size : sizes) {\n        double width = size.width;\n        double height = size.height;\n\n        ankerl::nanobench::Bench().minEpochIterations(100000).run(\"suggest value \" + std::to_string(size.width) + \"x\" + std::to_string(size.height), [&] {\n            suggest(root_node, width, height);\n        });\n    }\n\n    YGNodeFreeRecursive(root_node);\n    return 0;\n}\n\n// cc: flags+='-ggdb -O3 -DNDEBUG -I ~/Work/Sources/yoga -std=c++20'\n// cc: input+='yogaone.cpp'\n"
  },
  {
    "path": "yogaone.cpp",
    "content": "#include \"yoga/YGConfig.cpp\"\n#include \"yoga/YGEnums.cpp\"\n#include \"yoga/YGNode.cpp\"\n#include \"yoga/YGNodeLayout.cpp\"\n#include \"yoga/YGNodeStyle.cpp\"\n#include \"yoga/YGPixelGrid.cpp\"\n#include \"yoga/YGValue.cpp\"\n\n#include \"yoga/algorithm/AbsoluteLayout.cpp\"\n#include \"yoga/algorithm/Baseline.cpp\"\n#include \"yoga/algorithm/Cache.cpp\"\n#include \"yoga/algorithm/CalculateLayout.cpp\"\n#include \"yoga/algorithm/FlexLine.cpp\"\n#include \"yoga/algorithm/PixelGrid.cpp\"\n\n#include \"yoga/config/Config.cpp\"\n\n#include \"yoga/debug/AssertFatal.cpp\"\n\n#include \"yoga/debug/Log.cpp\"\n\n#include \"yoga/node/LayoutResults.cpp\"\n#include \"yoga/node/Node.cpp\"\n\n#define Node Node_\n#include \"yoga/event/event.cpp\"\n#undef Node\n"
  }
]