├── .circleci └── config.yml ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .vscode ├── launch.json └── settings.json ├── DEVELOPMENT.md ├── Dockerfile ├── LICENSE.TXT ├── Makefile ├── README.md ├── emscripten-bindings ├── CJOpus.c ├── EMBindings.cpp ├── anitomyscript.cpp ├── bind-freetype.cpp ├── bind-imgui.cpp ├── bind-libpng.cpp ├── bind-nanovg.cpp ├── binding.cpp ├── ccv_bindings.cpp ├── embind.cpp ├── embind_test.cpp └── opusscript_encoder.cpp ├── examples ├── BermudanSwaption.cpp ├── Bonds.cpp ├── EquityOption.cpp ├── billiontrader-bootstrapping.cpp ├── hello-array.cpp ├── hello-boost.cpp ├── hello-emscripten.cpp ├── hello-quantlib.cpp └── swap-example.cpp ├── index.js ├── main.js ├── package.json ├── quantlib-embind.cpp └── quantlib.test.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: captorab/emscripten-quantlib:1.36.1 6 | 7 | working_directory: ~/repo 8 | 9 | steps: 10 | - checkout 11 | - run: make build_bindings 12 | - run: npm install 13 | - run: npm test 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ["promise", "jasmine", "jest"], 3 | env: { 4 | browser: true, 5 | node: true, 6 | es6: true, 7 | jasmine: true, 8 | jest: true, 9 | "jest/globals": true 10 | }, 11 | parserOptions: { 12 | ecmaVersion: 2016, 13 | sourceType: "module", 14 | ecmaFeatures: { 15 | jsx: true, 16 | modules: true, 17 | experimentalObjectRestSpread: true 18 | } 19 | }, 20 | extends: ["eslint:recommended", "plugin:promise/recommended", "plugin:jasmine/recommended", "plugin:jest/recommended", "prettier"], 21 | rules: { 22 | "max-len": ["warn", 140], 23 | "no-unused-vars": [ 24 | "error", 25 | { 26 | args: "none" 27 | } 28 | ], 29 | "no-console": "off", 30 | "no-undef": "error", // disallow use of undeclared variables unless mentioned in a /*global */ block 31 | "no-undef-init": "error", // disallow use of undefined when initializing variables 32 | "no-undefined": "error", // disallow use of undefined variable (off by default) 33 | "no-unused-vars": "warn", // disallow declaration of variables that are not used in the code 34 | "no-use-before-define": "error", // disallow use of variables before they are defined 35 | "generator-star-spacing": 1, 36 | "array-bracket-spacing": 1, 37 | "arrow-parens": 1, 38 | "no-await-in-loop": 1, 39 | "no-mixed-spaces-and-tabs": "warn", 40 | eqeqeq: "warn" 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Web assemblies 35 | # *.wasm 36 | 37 | *.gz 38 | *.bz2 39 | 40 | dist 41 | 42 | examples/*.js 43 | examples/*.wasm 44 | examples/*.html 45 | 46 | package-lock.json 47 | node_modules 48 | 49 | .vscode/c_cpp_properties.json 50 | 51 | 52 | wip.md 53 | quantlib-wasm*.tgz -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | examples 3 | emscripten-bindings 4 | .eslintrc.js 5 | .circleci/config.yml 6 | 7 | quantlib-embind.cpp 8 | quantlib.test.js 9 | DEVELOPMENT.md 10 | Dockerfile 11 | main.js 12 | Makefile 13 | wip.md -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "main.js", 11 | "program": "${workspaceFolder}/main.js" 12 | }, 13 | { 14 | "type": "node", 15 | "request": "launch", 16 | "name": "Jest All", 17 | "program": "${workspaceFolder}/node_modules/.bin/jest", 18 | "args": ["--runInBand"], 19 | "console": "integratedTerminal", 20 | "internalConsoleOptions": "neverOpen", 21 | "disableOptimisticBPs": true, 22 | "windows": { 23 | "program": "${workspaceFolder}/node_modules/jest/bin/jest" 24 | } 25 | }, 26 | { 27 | "type": "node", 28 | "request": "launch", 29 | "name": "Jest Current File", 30 | "program": "${workspaceFolder}/node_modules/.bin/jest", 31 | "args": ["${relativeFile}"], 32 | "console": "integratedTerminal", 33 | "internalConsoleOptions": "neverOpen", 34 | "disableOptimisticBPs": true, 35 | "windows": { 36 | "program": "${workspaceFolder}/node_modules/jest/bin/jest" 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "memory": "cpp", 4 | "iostream": "cpp", 5 | "system_error": "cpp", 6 | "type_traits": "cpp", 7 | "xlocmon": "cpp", 8 | "xtr1common": "cpp", 9 | "ostream": "cpp", 10 | "cmath": "cpp", 11 | "cstddef": "cpp", 12 | "cstdint": "cpp", 13 | "cstdio": "cpp", 14 | "cstdlib": "cpp", 15 | "cstring": "cpp", 16 | "ctime": "cpp", 17 | "cwchar": "cpp", 18 | "exception": "cpp", 19 | "initializer_list": "cpp", 20 | "iomanip": "cpp", 21 | "ios": "cpp", 22 | "iosfwd": "cpp", 23 | "istream": "cpp", 24 | "limits": "cpp", 25 | "new": "cpp", 26 | "stdexcept": "cpp", 27 | "streambuf": "cpp", 28 | "tuple": "cpp", 29 | "typeinfo": "cpp", 30 | "utility": "cpp", 31 | "vector": "cpp", 32 | "xfacet": "cpp", 33 | "xiosbase": "cpp", 34 | "xlocale": "cpp", 35 | "xlocinfo": "cpp", 36 | "xlocnum": "cpp", 37 | "xloctime": "cpp", 38 | "xmemory": "cpp", 39 | "xmemory0": "cpp", 40 | "xstddef": "cpp", 41 | "xstring": "cpp", 42 | "xutility": "cpp", 43 | "cctype": "cpp", 44 | "functional": "cpp", 45 | "list": "cpp", 46 | "string": "cpp", 47 | "unordered_map": "cpp", 48 | "xhash": "cpp", 49 | "algorithm": "cpp", 50 | "array": "cpp", 51 | "atomic": "cpp", 52 | "bitset": "cpp", 53 | "chrono": "cpp", 54 | "clocale": "cpp", 55 | "complex": "cpp", 56 | "cstdarg": "cpp", 57 | "cwctype": "cpp", 58 | "deque": "cpp", 59 | "filesystem": "cpp", 60 | "fstream": "cpp", 61 | "iterator": "cpp", 62 | "locale": "cpp", 63 | "map": "cpp", 64 | "mutex": "cpp", 65 | "numeric": "cpp", 66 | "optional": "cpp", 67 | "ratio": "cpp", 68 | "set": "cpp", 69 | "sstream": "cpp", 70 | "stack": "cpp", 71 | "thread": "cpp", 72 | "valarray": "cpp", 73 | "xlocbuf": "cpp", 74 | "xlocmes": "cpp", 75 | "xtree": "cpp", 76 | "__bit_reference": "cpp", 77 | "__config": "cpp", 78 | "__debug": "cpp", 79 | "__errc": "cpp", 80 | "__functional_base": "cpp", 81 | "__hash_table": "cpp", 82 | "__locale": "cpp", 83 | "__mutex_base": "cpp", 84 | "__node_handle": "cpp", 85 | "__nullptr": "cpp", 86 | "__split_buffer": "cpp", 87 | "__string": "cpp", 88 | "__threading_support": "cpp", 89 | "__tree": "cpp", 90 | "__tuple": "cpp", 91 | "bit": "cpp", 92 | "string_view": "cpp", 93 | "__functional_base_03": "cpp", 94 | "*.ipp": "cpp", 95 | "__functional_03": "cpp", 96 | "codecvt": "cpp", 97 | "condition_variable": "cpp", 98 | "csignal": "cpp", 99 | "future": "cpp", 100 | "queue": "cpp", 101 | "random": "cpp", 102 | "shared_mutex": "cpp", 103 | "strstream": "cpp", 104 | "typeindex": "cpp", 105 | "unordered_set": "cpp", 106 | "variant": "cpp", 107 | "coroutine": "cpp" 108 | }, 109 | "makefile.configureOnOpen": false 110 | } -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | ### Pull from docker 4 | 5 | ``` 6 | docker pull captorab/emscripten-quantlib:1.16.1 7 | ``` 8 | 9 | ### Run the container 10 | 11 | See note on 'Share folder' (Windows only) below. 12 | 13 | Then: 14 | 15 | ``` 16 | docker run -v ${pwd}:/src -it --rm captorab/emscripten-quantlib:1.16.1 /bin/bash 17 | ``` 18 | 19 | Or continue execute and existing continer. Search with `docker ps -a`. 20 | 21 | ``` 22 | docker exec -it 590 /bin/bash 23 | ``` 24 | 25 | Where `590` is the current container id. 26 | 27 | ### Compile and test an example 28 | 29 | In the container: 30 | 31 | ``` 32 | cd examples 33 | emcc -I${BOOST} -I${QUANTLIB} -o hello-quantlib.js hello-quantlib.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 34 | ``` 35 | 36 | ### Run 37 | 38 | Outside the container: 39 | 40 | ``` 41 | cd examples 42 | node hello-quantlib.js 43 | ``` 44 | 45 | ### Build quantlib-embind 46 | 47 | ``` 48 | emcc --bind -I${EMSCRIPTEN}/system/include -I${QUANTLIB} -I${BOOST} -O3 -s MODULARIZE=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['addOnPostRun']" -s EXPORT_NAME=QuantLib -o quantlib-embind.js quantlib-embind.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 49 | ``` 50 | 51 | ### Windows only: Share folder 52 | 53 | Add a local user to the windows machine called `docker`. In Docker / Settings / Shared Drives, share the disk drive. Use the user `docker`. 54 | 55 | ## Setup the Emscripten container (from docker trzeci/emscripten) 56 | 57 | ### Start from trzeci/emscripten 58 | 59 | Start an emscripten container 60 | 61 | Share folder ´/share´ 62 | 63 | ``` 64 | docker run -v ${pwd}:/share -it -d trzeci/emscripten /bin/bash 65 | docker exec -it ee5 /bin/bash 66 | ``` 67 | 68 | In bash install extra packages. Boost should be installed and build from sourcecode. Link to [libboost](https://packages.debian.org/jessie/libboost-all-dev) 69 | 70 | ``` 71 | apt-get update 72 | apt-get -y dist-upgrade 73 | # apt-get install libboost-all-dev 74 | apt-get -y install automake autoconf libtool 75 | ``` 76 | 77 | ### Download and unzip Boost 78 | 79 | [Getting Started on Unix Variants](https://www.boost.org/doc/libs/1_70_0/more/getting_started/unix-variants.html) [Testing Emscripten with C++11 and Boost](https://gist.github.com/arielm/69a7488172611e74bfd4) 80 | 81 | ``` 82 | cd /tmp 83 | wget -c https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.bz2 84 | mkdir /boost 85 | tar --bzip2 -xf boost_1_70_0.tar.bz2 -C /boost --strip-components=1 86 | ``` 87 | 88 | ### Build Boost with Emscripten 89 | 90 | ```bash 91 | EMSCRIPTEN=/emsdk_portable/sdk 92 | BOOST=/boost 93 | 94 | cd $EMSCRIPTEN 95 | ./embuilder.py build zlib 96 | export NO_BZIP2=1 97 | 98 | cd $BOOST 99 | ./bootstrap.sh 100 | rm -rf stage 101 | ./b2 -a -j8 toolset=clang-emscripten link=static threading=single variant=release --with-system --with-filesystem --with-iostreams stage 102 | 103 | rm -rf lib/emscripten 104 | mkdir -p lib/emscripten 105 | cp stage/lib/*.a lib/emscripten 106 | 107 | unset NO_BZIP2 108 | ``` 109 | 110 | ### Build a Boost hello world 111 | 112 | ```bash 113 | EMSCRIPTEN=/emsdk_portable/sdk 114 | BOOST=/src/boost_1_70_0 115 | cd TestingBoost 116 | c++ -I${BOOST} boost-hello-world.cpp -o boost-hello-world 117 | ``` 118 | 119 | ### Download QuantLib 120 | 121 | In the container (bash). See [QuantLib on Linux](https://www.quantlib.org/install/linux.shtml): 122 | 123 | ```bash 124 | wget https://bintray.com/quantlib/releases/download_file?file_path=QuantLib-1.16.tar.gz -O QuantLib-1.16.tar.gz 125 | tar xzf QuantLib-1.16.tar.gz -C /quantlib 126 | QUANTLIB=/quantlib 127 | cd $QUANTLIB 128 | ``` 129 | 130 | ### Build QuantLib (with default Boost and without Emscripten) 131 | 132 | ``` 133 | ./autogen.sh 134 | ./configure 135 | make 136 | make install 137 | ldconfig 138 | ``` 139 | 140 | ### Build QuantLib with Boost and Emscripten 141 | 142 | How to use emconfigure and emmake, [see](https://emscripten.org/docs/compiling/Building-Projects.html) Also a good [guide](https://adamrehn.com/articles/creating-javascript-bindings-for-c-cxx-libraries-with-emscripten/) 143 | 144 | ``` 145 | cd $QUANTLIB 146 | BOOST=/boost 147 | emconfigure ./configure --with-boost-include=${BOOST} --with-boost-lib=${BOOST}/lib/emscripten 148 | emmake make 149 | emmake make install 150 | ldconfig 151 | ``` 152 | 153 | ### Build and run captorab/quantlib/emscripten docker image 154 | 155 | Build using `Dockerfile` in the same folder. 156 | 157 | ``` 158 | docker build -t docker.io/captorab/emscripten-quantlib:1.16.1 . 159 | ``` 160 | 161 | Run it (update the container id): 162 | 163 | ``` 164 | docker run -v ${pwd}:/src -it quantlib/emscripten /bin/bash 165 | ``` 166 | 167 | On MacOSX 168 | 169 | ``` 170 | docker run --mount type=bind,source="${PWD}",target=/src -it captorab/emscripten-quantlib:1.16.1 /bin/bash 171 | ``` 172 | 173 | ### Compile emscripten with boost 174 | 175 | [Using Boost with Emscripten](https://stackoverflow.com/questions/15724357/using-boost-with-emscripten) 176 | 177 | ## Delete unwanted files 178 | 179 | ``` 180 | du -d1 -h /quantlib/ql 181 | du -d1 -h /boost 182 | du -d1 -h /usr/local/lib 183 | 184 | mkdir /quantlib/libs 185 | mv /quantlib/ql/.libs/libQuantLib.a /quantlib/libs 186 | find /quantlib/ql -type f ! \( -name "_.h" -o -name "_.hpp" \) -delete 187 | mv /quantlib/libs /quantlib/ql/.libs/libQuantLib.a 188 | rm -rf /quantlib/Examples 189 | 190 | find /boost/boost -type f ! \( -name "_.h" -o -name "_.hpp" -o -name "\*.ipp" \) -delete 191 | rm -rf /boost/doc 192 | rm -rf /boost/libs 193 | 194 | rm -rf /usr/local/lib/libQuant*.* 195 | ``` 196 | 197 | ### Build Quantlibs BermudanSwaption example 198 | 199 | ``` 200 | QUANTLIB=/src/QuantLib-1.16 201 | cd QuantLib-1.16/Examples/BermudanSwaption/ 202 | emcc BermudanSwaption.cpp -o BermudanSwaption.js -std=c++11 -I${BOOST} -L${BOOST}/lib/emscripten -I${QUANTLIB} -I${QUANTLIB}/ql 203 | ``` 204 | 205 | Builds .o-file (LLVM bitcode): 206 | 207 | ``` 208 | emcc -DHAVE_CONFIG_H -I. -I../../ql -I../.. -I../.. -I/src/boost_1_70_0 -g -O2 -MT BermudanSwaption.o -MD -MP -MF .deps/BermudanSwaption.Tpo -c -o BermudanSwaption.o BermudanSwaption.cpp 209 | ``` 210 | 211 | [gcc arguments](http://tigcc.ticalc.org/doc/comopts.html) [emcc arguments](https://emscripten.org/docs/tools_reference/emcc.html) 212 | 213 | ``` 214 | emcc -I. -I../../ql -I../.. -I${BOOST} -L${BOOST}/lib/emscripten -O2 -MT BermudanSwaption.o -MD -MP -o BermudanSwaption.js BermudanSwaption.cpp 215 | emcc -I. -I../../ql -I../.. -I${BOOST} -L${BOOST}/lib/emscripten -O2 -o hello-boost.js hello-boost.cpp 216 | ``` 217 | 218 | Output from `make install` 219 | 220 | ``` 221 | /bin/bash ../libtool --mode=install /usr/bin/install -c libQuantLib.la '/usr/local/lib' 222 | libtool: install: /usr/bin/install -c .libs/libQuantLib.so.0.0.0 /usr/local/lib/libQuantLib.so.0.0.0 223 | libtool: install: (cd /usr/local/lib && { ln -s -f libQuantLib.so.0.0.0 libQuantLib.so.0 || { rm -f libQuantLib.so.0 && ln -s libQuantLib.so.0.0.0 libQuantLib.so.0; }; }) 224 | libtool: install: (cd /usr/local/lib && { ln -s -f libQuantLib.so.0.0.0 libQuantLib.so || { rm -f libQuantLib.so && ln -s libQuantLib.so.0.0.0 libQuantLib.so; }; }) 225 | libtool: install: /usr/bin/install -c .libs/libQuantLib.lai /usr/local/lib/libQuantLib.la 226 | libtool: install: /usr/bin/install -c .libs/libQuantLib.a /usr/local/lib/libQuantLib.a 227 | libtool: install: chmod 644 /usr/local/lib/libQuantLib.a 228 | libtool: install: /emsdk_portable/sdk/emranlib /usr/local/lib/libQuantLib.a 229 | libtool: finish: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/sbin" ldconfig -n /usr/local/lib 230 | ldconfig: /usr/local/lib/libQuantLib.so.0.0.0 is not an ELF file - it has the wrong magic bytes at the start. 231 | ldconfig: /usr/local/lib/libQuantLib.so.0 is not an ELF file - it has the wrong magic bytes at the start. 232 | ldconfig: /usr/local/lib/libQuantLib.so is not an ELF file - it has the wrong magic bytes at the start. 233 | ``` 234 | 235 | ``` 236 | BOOST=/src/boost_1_70_0 237 | QUANTLIB=/src/QuantLib-1.16 238 | emcc -I${QUANTLIB} -I${BOOST} -O2 -o hello-boost.js hello-boost.cpp 239 | emcc -I${QUANTLIB} -I${BOOST} -o hello-quantlib.js hello-quantlib.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 240 | ``` 241 | 242 | ### Link error when building QuantLib with Emscripten 243 | 244 | ``` 245 | root@ee5ee9687d4f:/src# emcc -I${QUANTLIB} -I${BOOST} -O2 -o hello-quantlib.js hello-quantlib.cpp 246 | error: undefined symbol: _ZN8QuantLib4DateC1EiNS_5MonthEi 247 | warning: To disable errors for undefined symbols use `-s ERROR_ON_UNDEFINED_SYMBOLS=0` 248 | error: undefined symbol: _ZN8QuantLiblsERNSt3__213basic_ostreamIcNS0_11char_traitsIcEEEERKNS_4DateE 249 | Error: Aborting compilation due to previous errors 250 | shared:ERROR: '/emsdk_portable/node/bin/node /emsdk_portable/sdk/src/compiler.js /tmp/tmpT3Jd9s.txt /emsdk_portable/sdk/src/library_pthread_stub.js' failed (1) 251 | ``` 252 | 253 | Adding the right lib solves the problem: 254 | 255 | ``` 256 | emcc -I${QUANTLIB} -I${BOOST} -o hello-quantlib.js hello-quantlib.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 257 | ``` 258 | 259 | ### BINARYEN_TRAP_MODE=clamp 260 | 261 | This runtime error is handled with `BINARYEN_TRAP_MODE=clamp` 262 | 263 | ``` 264 | exception thrown: RuntimeError: float unrepresentable in integer range,RuntimeError: float unrepresentable in integer range 265 | at wasm-function[2063]:2701 266 | at wasm-function[1140]:1436 267 | at wasm-function[108]:1861 268 | at Object.Module._main (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:6006:33) 269 | at Object.callMain (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:6346:30) 270 | at doRun (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:6404:60) 271 | at run (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:6418:5) 272 | at runCaller (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:6323:29) 273 | at removeRunDependency (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:1517:7) 274 | at receiveInstance (C:\Projects\Nodejs\test\test190425\BermudanSwaption.js:1611:5) 275 | ``` 276 | 277 | ``` 278 | emcc -I${BOOST} -I${QUANTLIB} -s BINARYEN_TRAP_MODE=clamp -s TOTAL_MEMORY=67108864 -o BermudanSwaption.js BermudanSwaption.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 279 | ``` 280 | 281 | ## Build test examples 282 | 283 | ### hello-boost 284 | 285 | ``` 286 | cd examples 287 | emcc -I${BOOST} -o hello-boost.js hello-boost.cpp 288 | ``` 289 | 290 | Expected output: 291 | 292 | ``` 293 | > node hello-boost.js 294 | HELLO 12345 295 | ``` 296 | 297 | ### hello-quantlib 298 | 299 | ``` 300 | cd examples 301 | emcc -I${BOOST} -I${QUANTLIB} -o hello-quantlib.js hello-quantlib.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 302 | ``` 303 | 304 | Expected output: 305 | 306 | ``` 307 | > node hello-quantlib.js 308 | HELLO May 15th, 2019 309 | ``` 310 | 311 | ### hello-emscripten 312 | 313 | ``` 314 | cd examples 315 | emcc -I${EMSCRIPTEN}/system/include --bind -o hello-emscripten.html hello-emscripten.cpp 316 | ``` 317 | 318 | Browse hello-emscripten.html. In the browser's console window 319 | 320 | ``` 321 | > Module.lerp(2,3,0.25) 322 | 2.25 323 | ``` 324 | 325 | Test in from Node.js. `Create main.js` with the following content: 326 | 327 | ``` 328 | var Module = require("./examples/hello-emscripten"); 329 | 330 | Module.onRuntimeInitialized = () => { 331 | console.log(Module.lerp(2, 3, 0.25)); 332 | }; 333 | ``` 334 | 335 | Expected output: 336 | 337 | ``` 338 | > node main.js 339 | 2.25 340 | ``` 341 | 342 | ### hello-array 343 | 344 | Build with: 345 | 346 | ``` 347 | cd examples 348 | emcc -I${EMSCRIPTEN}/system/include --bind -o hello-array.js hello-array.cpp 349 | ``` 350 | 351 | Test in from Node.js. `Create main.js` with the following content: 352 | 353 | ``` 354 | var Module = require("./examples/hello-array"); 355 | 356 | Module.onRuntimeInitialized = () => { 357 | var v = Module.createDoubleVector(5); 358 | for (let i = 0; i < 5; i++) { 359 | v.set(i, i + 1); 360 | } 361 | console.log(Module.sum(v)); 362 | }; 363 | ``` 364 | 365 | Expected output: 366 | 367 | ``` 368 | > node main.js 369 | 15 370 | ``` 371 | 372 | ### BermudanSwaption 373 | 374 | Use either `-s TOTAL_MEMORY=67108864` or `-s ALLOW_MEMORY_GROWTH=1` 375 | 376 | ``` 377 | # emcc -I${BOOST} -I${QUANTLIB} -s BINARYEN_TRAP_MODE=clamp -s TOTAL_MEMORY=67108864 -o BermudanSwaption.js BermudanSwaption.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 378 | cd examples 379 | emcc -I${BOOST} -I${QUANTLIB} -s BINARYEN_TRAP_MODE=clamp -O3 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=1 -std=c++14 -o BermudanSwaption.js BermudanSwaption.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 380 | ``` 381 | 382 | Expected output: 383 | 384 | ``` 385 | > node BermudanSwaption.js 386 | 387 | G2 (analytic formulae) calibration 388 | 1x5: model 10.04552 %, market 11.48000 % (-1.43448 %) 389 | 2x4: model 10.51233 %, market 11.08000 % (-0.56767 %) 390 | 3x3: model 10.70500 %, market 10.70000 % (+0.00500 %) 391 | 4x2: model 10.83816 %, market 10.21000 % (+0.62816 %) 392 | 5x1: model 10.94390 %, market 10.00000 % (+0.94390 %) 393 | calibrated to: 394 | a = 0.050105, sigma = 0.0094504 395 | b = 0.050109, eta = 0.0094505 396 | rho = -0.76333 397 | 398 | Hull-White (analytic formulae) calibration 399 | 1x5: model 10.62037 %, market 11.48000 % (-0.85963 %) 400 | 2x4: model 10.62959 %, market 11.08000 % (-0.45041 %) 401 | 3x3: model 10.63414 %, market 10.70000 % (-0.06586 %) 402 | 4x2: model 10.64428 %, market 10.21000 % (+0.43428 %) 403 | 5x1: model 10.66132 %, market 10.00000 % (+0.66132 %) 404 | calibrated to: 405 | a = 0.046414, sigma = 0.0058693 406 | 407 | Hull-White (numerical) calibration 408 | 1x5: model 10.31185 %, market 11.48000 % (-1.16815 %) 409 | 2x4: model 10.54619 %, market 11.08000 % (-0.53381 %) 410 | 3x3: model 10.66914 %, market 10.70000 % (-0.03086 %) 411 | 4x2: model 10.74020 %, market 10.21000 % (+0.53020 %) 412 | 5x1: model 10.79725 %, market 10.00000 % (+0.79725 %) 413 | calibrated to: 414 | a = 0.055229, sigma = 0.0061063 415 | 416 | Payer bermudan swaption struck at 5.00000 % (ATM) 417 | G2 (tree): 14.111 418 | G2 (fdm) : 14.113 419 | HW (tree): 12.904 420 | HW (fdm) : 12.91 421 | HW (num, tree): 13.158 422 | HW (num, fdm) : 13.157 423 | BK: 13.002 424 | Payer bermudan swaption struck at 6.00000 % (OTM) 425 | G2 (tree): 3.1943 426 | G2 (fdm) : 3.1809 427 | HW (tree): 2.4921 428 | HW (fdm) : 2.4596 429 | HW (num, tree): 2.615 430 | HW (num, fdm): 2.5829 431 | BK: 3.2751 432 | Payer bermudan swaption struck at 4.00000 % (ITM) 433 | G2 (tree): 42.61 434 | G2 (fdm) : 42.706 435 | HW (tree): 42.253 436 | HW (fdm) : 42.215 437 | HW (num, tree): 42.364 438 | HW (num, fdm) : 42.311 439 | BK: 41.825 440 | 441 | Run completed in 1 m 51 s 442 | ``` 443 | 444 | ### Billiontrader Bootstrapping 445 | 446 | See article [here](http://billiontrader.com/2015/02/16/bootstrapping-with-quantlib/) 447 | 448 | ``` 449 | cd examples 450 | emcc -I${BOOST} -I${QUANTLIB} -s BINARYEN_TRAP_MODE=clamp -o billiontrader-bootstrapping.js billiontrader-bootstrapping.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 451 | ``` 452 | 453 | Expected output: 454 | 455 | ``` 456 | > node billiontrader-bootstrapping.js 457 | 0.1375: 0.137499 % Actual/360 simple compounding 458 | 0.1717: 0.171700 % Actual/360 simple compounding 459 | 0.2112: 0.211200 % Actual/360 simple compounding 460 | 0.2581: 0.258100 % Actual/360 simple compounding 461 | 0.25093: 0.251098 % Actual/360 simple compounding 462 | 0.32228: 0.322259 % Actual/360 simple compounding 463 | 0.41111: 0.411112 % Actual/360 simple compounding 464 | 0.51112: 0.511346 % Actual/360 simple compounding 465 | 0.61698: 0.617716 % Actual/360 simple compounding 466 | 0.73036: 0.732486 % Actual/360 Annual compounding 467 | 0.89446: 0.890789 % Actual/360 Annual compounding 468 | 1.23937: 1.237068 % Actual/360 Annual compounding 469 | 1.49085: 1.489769 % Actual/360 Annual compounding 470 | 1.67450: 1.674417 % Actual/360 Annual compounding 471 | discount Rate : 0.919223 472 | Forward Rate : 2.419765 % Actual/360 simple compounding 473 | ``` 474 | 475 | ### swap-example 476 | 477 | ``` 478 | cd examples 479 | emcc -I${BOOST} -I${QUANTLIB} -s BINARYEN_TRAP_MODE=clamp -o swap-example.js swap-example.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 480 | ``` 481 | 482 | Expected output: 483 | 484 | ``` 485 | > node swap-example.js 486 | -11836.3 487 | ``` 488 | 489 | ## How to enable C++ in VS Code 490 | 491 | In VS Code install the C/C++ `ms-vscode.cpptools` extension. 492 | 493 | Add the file `c_cpp_properties.json` to the folder `.vscode` with the following content: 494 | 495 | ``` 496 | { 497 | "configurations": [ 498 | { 499 | "name": "Win32", 500 | "includePath": ["C:\\Repos\\QuantLib", "C:\\Repos\\boost", "C:\\Repos\\emscripten\\system\\include"], 501 | "defines": ["_DEBUG", "UNICODE", "_UNICODE"], 502 | "compilerPath": "C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2019\\\\Community\\\\VC\\\\Tools\\\\MSVC\\\\14.20.27508\\\\bin\\\\Hostx64\\\\x64\\\\cl.exe", 503 | "windowsSdkVersion": "10.0.17763.0", 504 | "intelliSenseMode": "msvc-x64", 505 | "cStandard": "c11", 506 | "cppStandard": "c++17" 507 | } 508 | ], 509 | "version": 4 510 | } 511 | ``` 512 | 513 | `compilerPath` needs to be adjusted to local installation of the c++-compiler. (Visual Studio)[https://visualstudio.microsoft.com/] And `includePath` needs to be adjusted to match local installations of the three projects QuantLib, Boost and Emscripten. 514 | 515 | - Install QuantLib with `git clone https://github.com/lballabio/QuantLib` 516 | - Install Boost from `https://www.boost.org/users/download/` 517 | - Install Emscripten with `git clone https://github.com/emscripten-core/emscripten` 518 | 519 | ## Useful links Emscripten / WebAssembly 520 | 521 | - (Emscripten)[https://emscripten.org/] 522 | - (WebAssmebly)[https://webassembly.eu/] 523 | - (AssemblyScript)[https://docs.assemblyscript.org/] 524 | - (Emscripten Wiki)[https://github.com/emscripten-core/emscripten/wiki] 525 | - (List of other emscripten projects)[https://github.com/emscripten-core/emscripten/wiki/Porting-Examples-and-Demos] 526 | - (WebAssembly Explorer)[https://mbebenita.github.io/WasmExplorer/] 527 | 528 | ## QuantLib 529 | 530 | - (Home page)[https://www.quantlib.org/] 531 | - (Source code)[https://github.com/lballabio/quantlib] 532 | - (Introduction)[https://www.quantlib.org/slides/dima-ql-intro-1.pdf] 533 | 534 | ## Coding details 535 | 536 | - (Using WebAssembly with React)[https://dev.to/brightdevs/using-webassembly-with-react-1led] 537 | - (Memory allocation, mallinfo)[https://linux.die.net/man/3/mallinfo] 538 | - (Using shared pointers)[https://docs.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=vs-2019] 539 | - (Difference in make-shared and normal shared-ptr)[https://stackoverflow.com/questions/20895648/difference-in-make-shared-and-normal-shared-ptr-in-c] 540 | - (Destructors in C++)[https://www.geeksforgeeks.org/destructors-c/] 541 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ## -*- docker-image-name: "emscripten-quantlib" -*- 2 | FROM emscripten/emsdk:3.1.69 3 | 4 | ENV EMSCRIPTEN /emsdk_portable/sdk 5 | 6 | RUN apt-get update && \ 7 | apt-get -y upgrade && \ 8 | apt-get -y install software-properties-common && \ 9 | add-apt-repository ppa:edd/misc && \ 10 | apt-get update && \ 11 | apt-get -y install ng-cjk automake autoconf 12 | 13 | # https://packages.ubuntu.com/search?keywords=quantlib 14 | # https://formulae.brew.sh/formula/quantlib 15 | # https://formulae.brew.sh/formula/boost 16 | 17 | # list distro and version 18 | RUN cat /etc/*-release 19 | # list installed version of boost 20 | #RUN apt-cache policy libboost-dev 21 | # list all versions available of libquantlib0-dev 22 | #RUN apt list -a libquantlib0-dev 23 | #RUN apt-get -y install libquantlib0-dev=${QUANTLIB_VERSION}-1.2204.1 libtool libboost-dev 24 | 25 | ENV BOOST /boost 26 | ENV BOOST_VERSION 1.86 27 | ENV BOOST_UNDERSCORE_VERSION 1_86 28 | # Download and unzip Boost 29 | # Remove unwanted files. Keep Emscripten as is. 30 | # Keep Boost and QuantLib header files and lib files. 31 | 32 | # Boost 33 | 34 | WORKDIR /tmp 35 | RUN wget -c https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}.0/source/boost_${BOOST_UNDERSCORE_VERSION}_0.tar.bz2 && \ 36 | mkdir ${BOOST} && \ 37 | tar --bzip2 -xf boost_${BOOST_UNDERSCORE_VERSION}_0.tar.bz2 -C ${BOOST} --strip-components=1 && \ 38 | rm -f boost_${BOOST_UNDERSCORE_VERSION}_0.tar.bz2 && \ 39 | rm -rf ${BOOST}/doc 40 | 41 | # Build Boost for Emscripten 42 | 43 | # [Getting Started on Unix Variants](https://www.boost.org/doc/libs/1_70_0/more/getting_started/unix-variants.html) 44 | # [Testing Emscripten with C++11 and Boost](https://gist.github.com/arielm/69a7488172611e74bfd4) 45 | 46 | WORKDIR ${EMSCRIPTEN} 47 | RUN embuilder.py build zlib 48 | ENV NO_BZIP2 1 49 | 50 | WORKDIR ${BOOST} 51 | # https://stackoverflow.com/questions/15724357/using-boost-with-emscripten/60550627#60550627 52 | 53 | RUN echo ${PWD} 54 | RUN mkdir -p ${BOOST}/lib/emscripten 55 | RUN ./bootstrap.sh && rm -rf stage && \ 56 | emconfigure ./b2 -a -j8 toolset=emscripten link=static threading=single variant=release \ 57 | --with-date_time --with-system --with-filesystem --with-iostreams --with-timer \ 58 | --with-math --with-random --with-thread stage \ 59 | --prefix=${BOOST}/lib/emscripten --build-dir=./build install && rm -rf ./build 60 | 61 | # We want boost to be installed natively x86_64 also ( for testing code without emscripten ) 62 | # we are not using apt-get install since we would get another version 63 | WORKDIR ${BOOST} 64 | RUN ./bootstrap.sh && rm -rf stage && \ 65 | ./b2 -a -j8 link=static threading=single variant=release \ 66 | --with-date_time --with-system --with-filesystem --with-iostreams --with-timer \ 67 | --with-math --with-random --with-thread stage \ 68 | --build-dir=./build/natively install && rm -rf ./build/natively 69 | 70 | # QuantLib 71 | ENV QUANTLIB /quantlib 72 | ENV QUANTLIB_NATIVE /quantlib_native 73 | ENV QUANTLIB_VERSION 1.36 74 | 75 | WORKDIR /tmp 76 | RUN wget https://github.com/lballabio/QuantLib/releases/download/v${QUANTLIB_VERSION}/QuantLib-${QUANTLIB_VERSION}.tar.gz -O QuantLib-${QUANTLIB_VERSION}.tar.gz && \ 77 | mkdir ${QUANTLIB} && \ 78 | tar xzf QuantLib-${QUANTLIB_VERSION}.tar.gz -C ${QUANTLIB} --strip-components=1 && \ 79 | cp -rf ${QUANTLIB} ${QUANTLIB_NATIVE} && \ 80 | rm -f QuantLib-${QUANTLIB_VERSION}.tar.gz 81 | 82 | # Build QuantLib with Boost and Emscripten 83 | 84 | # How to use emconfigure and emmake, [see](https://emscripten.org/docs/compiling/Building-Projects.html) 85 | # Also a good [guide](https://adamrehn.com/articles/creating-javascript-bindings-for-c-cxx-libraries-with-emscripten/) 86 | 87 | WORKDIR ${QUANTLIB} 88 | RUN echo $PWD 89 | RUN autoreconf --force 90 | # --enable-test-suite=no since it takes to much memory to link 91 | RUN emconfigure ./configure --with-boost-include=${BOOST} --with-boost-lib=${BOOST}/lib/emscripten --disable-shared --enable-test-suite=no --disable-dependency-tracking 92 | RUN emmake make -j1 && \ 93 | rm -rf ${QUANTLIB}/Examples && \ 94 | mv ${QUANTLIB}/ql/.libs/libQuantLib.a /tmp && \ 95 | find ${QUANTLIB}/ql -type f ! \( -name "*.h" -o -name "*.hpp" \) -delete && \ 96 | mv /tmp/libQuantLib.a ${QUANTLIB}/ql/.libs && \ 97 | rm -rf /usr/local/lib/libQuant*.* 98 | 99 | 100 | # We want also to install quantlib natively for x86_64 ( for testing code without emscripten ) 101 | # we are not using apt-get install since we would get another version 102 | # This still generates a to big docker layer, but good enough for now. 103 | # Be carefull with the -j switch. It could use up to much memory and docker will just kill it with "received SIGKILL (-9)" 104 | # Most likely you need to increase the docker.settings memory usage in any case 105 | # Compiling nativly should be last, since else /usr/local/lib/libQuantLib.so is lost 106 | WORKDIR ${QUANTLIB_NATIVE} 107 | RUN autoreconf --force 108 | RUN ./configure --enable-test-suite=no --disable-dependency-tracking 109 | RUN make -j1 && \ 110 | make install && \ 111 | ldconfig && \ 112 | cd / && rm -rf ${QUANTLIB_NATIVE} 113 | 114 | 115 | WORKDIR /src 116 | CMD ["/bin/bash"] 117 | 118 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptorAB/quantlib-wasm/e60f7b95e4d10f0426bf61c38c0e91a61b5b0ad4/LICENSE.TXT -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # this needs to be the fxirst target if target build_bindings_from_unix is going to work 3 | build_bindings: 4 | mkdir -p dist 5 | emcc --bind -I${EMSCRIPTEN}/system/include -I${QUANTLIB} -I${BOOST} -O3 \ 6 | -s MODULARIZE=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['addOnPostRun']" \ 7 | -s EXPORT_NAME=QuantLib -s TOTAL_MEMORY=64MB -o dist/quantlib.js quantlib-embind.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 8 | 9 | build_bindings_from_unix: 10 | docker pull captorab/emscripten-quantlib:1.36.1 11 | docker run --platform linux/amd64 --mount type=bind,source="${PWD}",target=/src -it captorab/emscripten-quantlib:1.36.1 make 12 | 13 | container_shell: 14 | docker pull captorab/emscripten-quantlib:1.36.1 15 | docker run --platform linux/amd64 --mount type=bind,source="${PWD}",target=/src -it --rm captorab/emscripten-quantlib:1.36.1 /bin/bash 16 | 17 | 18 | build_docker_image: 19 | docker build --platform linux/amd64 -t docker.io/captorab/emscripten-quantlib:1.36.1 . 20 | #docker push docker.io/captorab/emscripten-quantlib:1.36.1 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/CaptorAB/quantlib-wasm/tree/master.svg?style=svg)](https://circleci.com/gh/CaptorAB/quantlib-wasm/tree/master) 2 | [![npm version](https://badge.fury.io/js/quantlib-wasm.svg)](https://badge.fury.io/js/quantlib-wasm) 3 | [![install size](https://packagephobia.now.sh/badge?p=quantlib-wasm)](https://packagephobia.now.sh/result?p=quantlib-wasm) 4 | 5 | # quantlib-wasm 6 | 7 | A wrapper of the quantitative finance library [Quantlib](https://www.quantlib.org/). Compiled as a [WebAssembly](https://webassembly.org/) for use in browsers and in Node.JS. 8 | 9 | WARNING: This is work in progress and in alpha mode. 10 | 11 | A live example of quantlib running in the browser can be seen here https://captorab.github.io/quantlib-wasm-demo/ 12 | 13 | ## Install 14 | 15 | ```bash 16 | npm install quantlib-wasm 17 | ``` 18 | 19 | ## Usage with React 20 | 21 | ```typescript 22 | import wasmUrl from "quantlib-wasm/dist/quantlib.wasm?url"; 23 | import quantlibWasm from "quantlib-wasm"; 24 | 25 | 26 | const MyReactComponent = () => { 27 | const [quantLibLoaded, setQuantLibLoaded] = useState(false); 28 | const [QuantLib, setQuantLib] = useState(null); 29 | 30 | useEffect(() => { 31 | if (!quantLibLoaded) { 32 | quantlibWasm({ 33 | locateFile: () => wasmUrl, 34 | }).then((loaded) => { 35 | setQuantLib(loaded); 36 | setQuantLibLoaded(true); 37 | }); 38 | } 39 | }); 40 | 41 | .... 42 | 43 | ``` 44 | 45 | ## Introduction 46 | 47 | [Quantlib](https://www.quantlib.org/) is a quantitative finance library, used for pricing, hedging and valuation of financial sequrities and derivatives. It's open source and widely used. The library is written in C++ and it has been exported to many languages such as Python, Java and C#. 48 | 49 | How about exporting QuantLib to JavaScript? In Node.js there are many ways of importing external libraries, including node-gyp, addons and N-API. None of these techniques work client side in a browser. WebAssembly on the other hand, works both client and server-side. 50 | 51 | [WebAssembly](https://webassembly.org/) (Wasm) is a sandboxed environment running inside the JavaScript virtual machine. High-level languages like C/C++ can be compiled into the Wasm. WebAssembly is supported by four major browsers (Chrome, Firefox, Safari and Edge). Therefore, QuantLib as WebAssembly can be used from JavaScript both client (web browser) and server-side (Node.js). 52 | 53 | ## Working with QuantLib in JavaScript 54 | 55 | QuantLib is an object oriented library, rather than functional oriented. The QuantLib calculations are done with many objects, such as Date, Calendar, Schedule, PricingEngine, YieldCurve and all kind of instrument objects. These objects can be exported and used in JavaScript. The code in JavaScript will be similar to versions in Python or C++. 56 | 57 | Here is a schedule generator example: 58 | 59 | ```js 60 | const { Date, TimeUnit, Schedule, Period, BusinessDayConvention, DateGenerationRule } = QuantLib; 61 | var effectiveDate = Date.fromISOString("2019-08-19"); 62 | var terminationDate = Date.fromISOString("2020-08-19"); 63 | var period = new Period(3, TimeUnit.Months); 64 | var firstDate = new Date(); 65 | var nextToLastDate = new Date(); 66 | var schedule = new Schedule( 67 | effectiveDate, 68 | terminationDate, 69 | period, 70 | QuantLib.TARGET, 71 | BusinessDayConvention.ModifiedFollowing, 72 | BusinessDayConvention.ModifiedFollowing, 73 | DateGenerationRule.Backward, 74 | true, 75 | firstDate, 76 | nextToLastDate 77 | ); 78 | var dates = schedule.dates(); 79 | for (let i = 0; i < dates.size(); i++) { 80 | let d = dates.get(i); 81 | console.log(d.toISOString()); 82 | d.delete(); 83 | } 84 | [effectiveDate, terminationDate, period, firstDate, nextToLastDate, dates, schedule].forEach((d) => d.delete()); 85 | ``` 86 | 87 | ## Emscripten 88 | 89 | This implementation uses Emscripten to compile QuantLib. Emscripten compiles C++ into low level JavaScript called asm.js, which is highly optimizable and can be executed at close to native speed. A [long list of projects](https://github.com/emscripten-core/emscripten/wiki/Porting-Examples-and-Demos) are already using Emscripten to port codebases to JavaScript. 90 | 91 | Embind is used to bind C++ functions and classes to JavaScript. The bindings are done with a few lines of code. The technique for defining bindings is similar to Boost Python. 92 | 93 | The easiest way to run the Emscripten environment is in a prebuild Docker container. `trzeci/emscripten` is a good container and when running it compilations are done with the emcc compiler via the command prompt. The three projects Emscripten, QuantLib and Boost (which is a dependency of QuantLib) and wrapped together in a container called `captorab/emscripten-quantlib`. Running in a docker container saves a lot of time. The operating system issues and the configuration are done once and can easily be shared among developers. 94 | 95 | ## Memory management 96 | 97 | When using Wasm and Embind, there is one catch though. Memory management must be handled in both the JavaScript and the Wasm environment. In JavaScript, objects are destructed automatically, but before leaving a QuantLib object in JavaScript a delete command needs to be sent to the Wasm, to destruct the C++ object. This must be done explicitly since the JavaScript objects do not have any finalizer. This is something high level programmers assume the environment will do automatically. Unfortunately this is not done automatically between the two memory areas, one in JavaScript and one in the Wasm. In the example above delete is called on the last line in the for loop and on the very last line of code. 98 | 99 | Code like the example below cannot be used because it hides the destructor of the Date-object. 100 | 101 | ```js 102 | console.log(Date.fromISOString("2019-08-19").toString()); // This causes a memory leak. 103 | ``` 104 | 105 | Here is the correct equivalent: 106 | 107 | ```js 108 | var date = Date.fromISOString("2019-08-19"); 109 | console.log(date.toString()); 110 | date.delete(); 111 | ``` 112 | 113 | Memory usage can be measured in the Wasm at any time, and memory leaks can be detected. 114 | 115 | ```js 116 | const { Date, mallinfo } = QuantLib; 117 | var m0 = mallinfo(); 118 | var date = Date.fromISOString("2019-08-19"); 119 | console.log(date.toString()); 120 | date.delete(); 121 | var m1 = mallinfo(); 122 | console.log(m1.uordblks - m0.uordblks + (m1.hblkhd - m0.hblkhd)); // Should print 0 123 | ``` 124 | 125 | ## Using the wasm in a React app 126 | 127 | React in itself can easily use the Quantlib wasm. See the method above. When the app is build with `create-react-app`, webpack is used to load and build the source files. By default (version 3.1.1 or earlier of `react-scripts`), doesn't load wasm files. To bypass this problem Facebook's `react-scripts` can be forked and modified. How this is done is explained [here](https://auth0.com/blog/how-to-configure-create-react-app/). 128 | 129 | One fork that loads wasm files is [@captor/react-scripts](https://www.npmjs.com/package/@captor/react-scripts). To create a new app that with the modifies script run: 130 | 131 | ```sh 132 | npx create-react-app --scripts-version @captor/react-scripts 133 | ``` 134 | 135 | Or, in an already existing app, change the installed script reference. 136 | 137 | ```sh 138 | npm uninstall react-scripts 139 | npm install @captor/react-scripts 140 | ``` 141 | 142 | Add `quantlib-wasm` to the app: 143 | 144 | ```sh 145 | npm install quantlib-wasm 146 | ``` 147 | 148 | ## Status 149 | 150 | Which objects and functions are exported? There is no documentation written yet. Until this project turns into alpha mode, the only reliable way is to check the code. In this case the binding file is the right place. It's found [here](https://github.com/CaptorAB/quantlib-wasm/blob/master/quantlib-embind.cpp). 151 | 152 | ## Versioning 153 | 154 | quantlib-wasm does not follow https://semver.org/, but the version from [Quantlib](https://www.quantlib.org/) with an extra number to version the [quantlib-wasm](https://www.npmjs.com/package/quantlib-wasm) package. 155 | 156 | ## Development 157 | 158 | In order to build a new version, when a new Quantlib version is available 159 | 160 | ```bash 161 | git checkout -b $QUANTLIB_VERSION 162 | ``` 163 | 164 | In: 165 | 166 | - .circleci/config.yml 167 | - Dockerfile 168 | - Makefile 169 | - package.json 170 | 171 | update from old version number to "${QUANTLIB_VERSION}"."${version}" 172 | 173 | ```bash 174 | make build_docker_image 175 | make build_bindings_from_unix 176 | npm i 177 | npm test 178 | npm pack 179 | npm publish 180 | ``` 181 | -------------------------------------------------------------------------------- /emscripten-bindings/CJOpus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../opus/include/opus.h" 4 | 5 | #define MAX_FRAME_SIZE 5760 6 | #define MAX_INPUT_BYTES 3840 7 | #define MAX_OUTPUT_BYTES 4000 8 | 9 | typedef struct Encoder { 10 | OpusEncoder* encoder; 11 | OpusDecoder* decoder; 12 | 13 | //I know libopus outputs unsigned chars 14 | //but it's supposed to accept signed shorts, right? 15 | //BUT when you .read() from Node, you get a 16 | //Node Buffer (unsigned char array)... 17 | //I'll just use an opus_int16 (to send to libopus) 18 | //just to be safe. 19 | 20 | //Assign PCM audio data to this array 21 | unsigned char in_pcm[MAX_INPUT_BYTES]; 22 | //Use to_big_endian to convert it and put it in this array 23 | opus_int16 in_big_endian[MAX_INPUT_BYTES]; 24 | //Encode the audio from the last array and put it into this one 25 | unsigned char out_encoded[MAX_OUTPUT_BYTES]; 26 | 27 | //Assign Opus audio data to this array 28 | unsigned char in_opus[MAX_FRAME_SIZE]; 29 | //Decode the Opus audio to PCM audio 30 | opus_int16 in_decoded[MAX_OUTPUT_BYTES]; 31 | //Convert the Big Endian PCM audio to Little Endian and put it into this one 32 | opus_int16 out_little_endian[MAX_OUTPUT_BYTES]; 33 | 34 | int encoder_error; 35 | int decoder_error; 36 | } Encoder; 37 | 38 | void to_big_endian(unsigned char* in, int i, opus_int16* out) { 39 | for (;i--;) { 40 | out[i] = in[2*i+1]<<8|in[2*i]; 41 | } 42 | } 43 | 44 | void to_little_endian(opus_int16* in, int i, opus_int16* out) { 45 | for (;i--;) { 46 | out[2*i]=in[i]&0xFF; 47 | out[2*i+1]=(in[i]>>8)&0xFF; 48 | } 49 | } 50 | 51 | EMSCRIPTEN_KEEPALIVE 52 | Encoder* create_encoder_and_decoder(opus_int32 sample_rate, int channels, int application) { 53 | Encoder* enc = malloc(sizeof(Encoder)); 54 | 55 | enc->encoder = opus_encoder_create( sample_rate, channels, application, &enc->encoder_error ); 56 | enc->decoder = opus_decoder_create( sample_rate, channels, &enc->decoder_error ); 57 | 58 | return enc; 59 | } 60 | 61 | EMSCRIPTEN_KEEPALIVE 62 | int get_encoder_error(Encoder* encoder) { 63 | return encoder->encoder_error; 64 | } 65 | 66 | EMSCRIPTEN_KEEPALIVE 67 | int get_decoder_error(Encoder* encoder) { 68 | return encoder->decoder_error; 69 | } 70 | 71 | EMSCRIPTEN_KEEPALIVE 72 | int encode(Encoder* encoder, int length, int frame_size) { 73 | to_big_endian(encoder->in_pcm, length, encoder->in_big_endian); 74 | return opus_encode( 75 | encoder->encoder, 76 | encoder->in_big_endian, 77 | frame_size, 78 | encoder->out_encoded, 79 | MAX_OUTPUT_BYTES 80 | ); 81 | } 82 | 83 | EMSCRIPTEN_KEEPALIVE 84 | int decode(Encoder* encoder, int length) { 85 | int encoded_frames = opus_decode( 86 | encoder->decoder, 87 | encoder->in_opus,//opus, 88 | length, 89 | encoder->in_decoded, 90 | MAX_FRAME_SIZE, 91 | 0 92 | ); 93 | to_little_endian(encoder->in_decoded, MAX_OUTPUT_BYTES, encoder->out_little_endian); 94 | return encoded_frames; 95 | } 96 | 97 | EMSCRIPTEN_KEEPALIVE 98 | void destroy_encoder(Encoder* encoder) { 99 | opus_encoder_destroy(encoder->encoder); 100 | opus_decoder_destroy(encoder->decoder); 101 | free(encoder); 102 | } 103 | 104 | //Could probably merge these into one with a switch using a number 105 | //But since I'm not worried about space they can be separate 106 | //It's a bit more semantic too, right? 107 | EMSCRIPTEN_KEEPALIVE 108 | unsigned char* get_in_pcm_offset(Encoder* encoder) { 109 | return encoder->in_pcm; 110 | } 111 | 112 | EMSCRIPTEN_KEEPALIVE 113 | unsigned char* get_encoded_offset(Encoder* encoder) { 114 | return encoder->out_encoded; 115 | } 116 | 117 | EMSCRIPTEN_KEEPALIVE 118 | unsigned char* get_in_opus_offset(Encoder* encoder) { 119 | return encoder->in_opus; 120 | } 121 | 122 | EMSCRIPTEN_KEEPALIVE 123 | opus_int16* get_decoded_little_endian_offset(Encoder* encoder) { 124 | return encoder->out_little_endian; 125 | } -------------------------------------------------------------------------------- /emscripten-bindings/EMBindings.cpp: -------------------------------------------------------------------------------- 1 | // EMBindings.cpp 2 | // 3 | // MIT License 4 | // 5 | // Copyright(c) 2017 Rob Grindeland 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files(the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions : 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #include 27 | #include "FastNoise/FastNoise.h" 28 | 29 | using namespace emscripten; 30 | 31 | EMSCRIPTEN_BINDINGS(FastNoise) { 32 | // Module.NoiseType 33 | enum_("NoiseType") 34 | .value("Value", FastNoise::Value) 35 | .value("ValueFractal", FastNoise::ValueFractal) 36 | .value("Perlin", FastNoise::Perlin) 37 | .value("PerlinFractal", FastNoise::PerlinFractal) 38 | .value("Simplex", FastNoise::Simplex) 39 | .value("SimplexFractal", FastNoise::SimplexFractal) 40 | .value("Cellular", FastNoise::Cellular) 41 | .value("WhiteNoise", FastNoise::WhiteNoise) 42 | .value("Cubic", FastNoise::Cubic) 43 | .value("CubicFractal", FastNoise::CubicFractal) 44 | ; 45 | // Module.Interp 46 | enum_("Interp") 47 | .value("Linear", FastNoise::Linear) 48 | .value("Hermite", FastNoise::Hermite) 49 | .value("Quintic", FastNoise::Quintic) 50 | ; 51 | // Module.FractalType 52 | enum_("FractalType") 53 | .value("FBM", FastNoise::FBM) 54 | .value("Billow", FastNoise::Billow) 55 | .value("RigidMulti", FastNoise::RigidMulti) 56 | ; 57 | // Module.CellularDistanceFunction 58 | enum_("CellularDistanceFunction") 59 | .value("Euclidean", FastNoise::Euclidean) 60 | .value("Manhattan", FastNoise::Manhattan) 61 | .value("Natural", FastNoise::Natural) 62 | ; 63 | // Module.CellularReturnType 64 | enum_("CellularReturnType") 65 | .value("CellValue", FastNoise::CellValue) 66 | .value("NoiseLookup", FastNoise::NoiseLookup) 67 | .value("Distance", FastNoise::Distance) 68 | .value("Distance2", FastNoise::Distance2) 69 | .value("Distance2Add", FastNoise::Distance2Add) 70 | .value("Distance2Sub", FastNoise::Distance2Sub) 71 | .value("Distance2Mul", FastNoise::Distance2Mul) 72 | .value("Distance2Div", FastNoise::Distance2Div) 73 | ; 74 | 75 | // Module.FastNoise 76 | class_("FastNoise") 77 | .constructor() 78 | 79 | // properties 80 | .property("seed", &FastNoise::GetSeed, &FastNoise::SetSeed) 81 | .property("frequency", &FastNoise::GetFrequency, &FastNoise::SetFrequency) 82 | .property("interp", &FastNoise::GetInterp, &FastNoise::SetInterp) 83 | .property("noiseType", &FastNoise::GetNoiseType, &FastNoise::SetNoiseType) 84 | .property("interp", &FastNoise::GetInterp, &FastNoise::SetInterp) 85 | .property("fractalOctaves", &FastNoise::GetFractalOctaves, &FastNoise::SetFractalOctaves) 86 | .property("fractalLacunarity", &FastNoise::GetFractalLacunarity, &FastNoise::SetFractalLacunarity) 87 | .property("fractalGain", &FastNoise::GetFractalGain, &FastNoise::SetFractalGain) 88 | .property("fractalType", &FastNoise::GetFractalType, &FastNoise::SetFractalType) 89 | .property("cellularDistanceFunction", &FastNoise::GetCellularDistanceFunction, &FastNoise::SetCellularDistanceFunction) 90 | // .property("cellularNoiseLookup", &FastNoise::GetCellularNoiseLookup, &FastNoise::SetCellularNoiseLookup) 91 | // .property("cellularDistance2Indices", &FastNoise::GetCellularDistance2Indices, &FastNoise::SetCellularDistance2Indices) 92 | .property("cellularJitter", &FastNoise::GetCellularJitter, &FastNoise::SetCellularJitter) 93 | .property("gradientPerturbAmp", &FastNoise::GetGradientPerturbAmp, &FastNoise::SetGradientPerturbAmp) 94 | 95 | // 2D 96 | .function("value2D", select_overload(&FastNoise::GetValue)) 97 | .function("valueFractal2D", select_overload(&FastNoise::GetValueFractal)) 98 | .function("perlin2D", select_overload(&FastNoise::GetPerlin)) 99 | .function("perlinFractal2D", select_overload(&FastNoise::GetPerlinFractal)) 100 | .function("simplex2D", select_overload(&FastNoise::GetSimplex)) 101 | .function("simplexFractal2D", select_overload(&FastNoise::GetSimplexFractal)) 102 | .function("cellular2D", select_overload(&FastNoise::GetCellular)) 103 | .function("whiteNoise2D", select_overload(&FastNoise::GetWhiteNoise)) 104 | .function("whiteNoiseInt2D", select_overload(&FastNoise::GetWhiteNoiseInt)) 105 | .function("cubic2D", select_overload(&FastNoise::GetCubic)) 106 | .function("cubicFractal2D", select_overload(&FastNoise::GetCubicFractal)) 107 | .function("getNoise2D", select_overload(&FastNoise::GetNoise)) 108 | // .function("gradientPerturb2D", select_overload(&FastNoise::GradientPerturb)) 109 | // .function("gradientPerturbFractal2D", select_overload(&FastNoise::GradientPerturbFractal)) 110 | 111 | // 3D 112 | .function("value3D", select_overload(&FastNoise::GetValue)) 113 | .function("valueFractal3D", select_overload(&FastNoise::GetValueFractal)) 114 | .function("perlin3D", select_overload(&FastNoise::GetPerlin)) 115 | .function("perlinFractal3D", select_overload(&FastNoise::GetPerlinFractal)) 116 | .function("simplex3D", select_overload(&FastNoise::GetSimplex)) 117 | .function("simplexFractal3D", select_overload(&FastNoise::GetSimplexFractal)) 118 | .function("cellular3D", select_overload(&FastNoise::GetCellular)) 119 | .function("whiteNoise3D", select_overload(&FastNoise::GetWhiteNoise)) 120 | .function("whiteNoiseInt3D", select_overload(&FastNoise::GetWhiteNoiseInt)) 121 | .function("cubic3D", select_overload(&FastNoise::GetCubic)) 122 | .function("cubicFractal3D", select_overload(&FastNoise::GetCubicFractal)) 123 | .function("getNoise3D", select_overload(&FastNoise::GetNoise)) 124 | // .function("gradientPerturb3D", select_overload(&FastNoise::GradientPerturb)) 125 | // .function("gradientPerturbFractal3D", select_overload(&FastNoise::GradientPerturbFractal)) 126 | 127 | // 4D 128 | .function("simplex4D", select_overload(&FastNoise::GetSimplex)) 129 | .function("whiteNoise4D", select_overload(&FastNoise::GetWhiteNoise)) 130 | .function("whiteNoiseInt4D", select_overload(&FastNoise::GetWhiteNoiseInt)) 131 | ; 132 | } 133 | -------------------------------------------------------------------------------- /emscripten-bindings/anitomyscript.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace emscripten; 6 | using namespace anitomy; 7 | using namespace std; 8 | 9 | Elements ParseSingle(string_t filename) 10 | { 11 | Anitomy an; 12 | an.Parse(filename); 13 | return an.elements(); 14 | } 15 | 16 | vector ParseMultiple(vector filenames) 17 | { 18 | Anitomy an; 19 | vector elements; 20 | for (auto filename : filenames) 21 | { 22 | an.Parse(filename); 23 | elements.emplace_back(an.elements()); 24 | } 25 | return elements; 26 | } 27 | 28 | vector *StringVectorFromPointer(uintptr_t vec) 29 | { 30 | return reinterpret_cast *>(vec); 31 | } 32 | 33 | vector *ElementVectorFromPointer(uintptr_t vec) 34 | { 35 | return reinterpret_cast *>(vec); 36 | } 37 | 38 | EMSCRIPTEN_BINDINGS(Anitomy) 39 | { 40 | emscripten::function("parseSingle", &ParseSingle); 41 | emscripten::function("parseMultiple", &ParseMultiple); 42 | 43 | register_vector("StringVector") 44 | .constructor(&StringVectorFromPointer, allow_raw_pointers()); 45 | 46 | register_vector("ElementVector") 47 | .constructor(&ElementVectorFromPointer, allow_raw_pointers()); 48 | 49 | class_("Elements") 50 | .constructor<>() 51 | .function("size", &Elements::size) 52 | .function("empty_capacity", select_overload(&Elements::empty)) 53 | .function("empty_lookup", select_overload(&Elements::empty)) 54 | .function("get", &Elements::get) 55 | .function("count", &Elements::count) 56 | .function("get_all", &Elements::get_all); 57 | 58 | enum_("ElementCategory") 59 | .value("kElementIterateFirst", ElementCategory::kElementIterateFirst) 60 | .value("kElementAnimeSeason", ElementCategory::kElementAnimeSeason) 61 | .value("kElementAnimeSeasonPrefix", ElementCategory::kElementAnimeSeasonPrefix) 62 | .value("kElementAnimeTitle", ElementCategory::kElementAnimeTitle) 63 | .value("kElementAnimeType", ElementCategory::kElementAnimeType) 64 | .value("kElementAnimeYear", ElementCategory::kElementAnimeYear) 65 | .value("kElementAudioTerm", ElementCategory::kElementAudioTerm) 66 | .value("kElementDeviceCompatibility", ElementCategory::kElementDeviceCompatibility) 67 | .value("kElementEpisodeNumber", ElementCategory::kElementEpisodeNumber) 68 | .value("kElementEpisodeNumberAlt", ElementCategory::kElementEpisodeNumberAlt) 69 | .value("kElementEpisodePrefix", ElementCategory::kElementEpisodePrefix) 70 | .value("kElementEpisodeTitle", ElementCategory::kElementEpisodeTitle) 71 | .value("kElementFileChecksum", ElementCategory::kElementFileChecksum) 72 | .value("kElementFileExtension", ElementCategory::kElementFileExtension) 73 | .value("kElementFileName", ElementCategory::kElementFileName) 74 | .value("kElementLanguage", ElementCategory::kElementLanguage) 75 | .value("kElementOther", ElementCategory::kElementOther) 76 | .value("kElementReleaseGroup", ElementCategory::kElementReleaseGroup) 77 | .value("kElementReleaseInformation", ElementCategory::kElementReleaseInformation) 78 | .value("kElementReleaseVersion", ElementCategory::kElementReleaseVersion) 79 | .value("kElementSource", ElementCategory::kElementSource) 80 | .value("kElementSubtitles", ElementCategory::kElementSubtitles) 81 | .value("kElementVideoResolution", ElementCategory::kElementVideoResolution) 82 | .value("kElementVideoTerm", ElementCategory::kElementVideoTerm) 83 | .value("kElementVolumeNumber", ElementCategory::kElementVolumeNumber) 84 | .value("kElementVolumePrefix", ElementCategory::kElementVolumePrefix) 85 | .value("kElementIterateLast", ElementCategory::kElementIterateLast) 86 | .value("kElementUnknown", ElementCategory::kElementUnknown); 87 | } 88 | -------------------------------------------------------------------------------- /emscripten-bindings/bind-libpng.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define FUNCTION(RET, ARGS, CODE...) \ 6 | emscripten::optional_override([] ARGS -> RET { CODE }) 7 | 8 | #include 9 | 10 | emscripten::val get_mallinfo() { 11 | const auto &i = mallinfo(); 12 | emscripten::val rv(emscripten::val::object()); 13 | rv.set("arena", emscripten::val(i.arena)); 14 | rv.set("ordblks", emscripten::val(i.ordblks)); 15 | rv.set("smblks", emscripten::val(i.smblks)); 16 | rv.set("hblks", emscripten::val(i.hblks)); 17 | rv.set("hblkhd", emscripten::val(i.hblkhd)); 18 | rv.set("usmblks", emscripten::val(i.usmblks)); 19 | rv.set("fsmblks", emscripten::val(i.fsmblks)); 20 | rv.set("uordblks", emscripten::val(i.uordblks)); 21 | rv.set("fordblks", emscripten::val(i.fordblks)); 22 | rv.set("keepcost", emscripten::val(i.keepcost)); 23 | return rv; 24 | } 25 | 26 | EMSCRIPTEN_BINDINGS(mallinfo) { 27 | emscripten::function("mallinfo", &get_mallinfo); 28 | } 29 | 30 | EMSCRIPTEN_BINDINGS(LIBPNG) { 31 | emscripten::constant("PNG_LIBPNG_VER_STRING", emscripten::val(PNG_LIBPNG_VER_STRING)); 32 | // emscripten::constant("PNG_LIBPNG_VER_MAJOR", emscripten::val(PNG_LIBPNG_VER_MAJOR)); 33 | // emscripten::constant("PNG_LIBPNG_VER_MINOR", emscripten::val(PNG_LIBPNG_VER_MINOR)); 34 | // emscripten::constant("PNG_LIBPNG_VER_RELEASE", emscripten::val(PNG_LIBPNG_VER_RELEASE)); 35 | // emscripten::constant("PNG_LIBPNG_VER", emscripten::val(PNG_LIBPNG_VER)); 36 | emscripten::function("png_access_version_number", &png_access_version_number); 37 | } 38 | 39 | static std::string _png_error; 40 | static void _error_fn(png_structp png_ptr, png_const_charp error) { 41 | _png_error = error; 42 | png_longjmp(png_ptr, 1); 43 | } 44 | static void _warn_fn(png_structp png_ptr, png_const_charp error) { 45 | } 46 | static emscripten::val _get_error() { 47 | emscripten::val error = emscripten::val::global("Error").new_(_png_error); 48 | _png_error.clear(); 49 | return error; 50 | } 51 | 52 | static std::vector _png_file; 53 | static unsigned long _png_cursor = 0; 54 | 55 | static void _write_fn(png_structp png_ptr, unsigned char *data, unsigned long size) { 56 | // printf("write %p %lu\n", data, size); 57 | _png_file.insert(_png_file.end(), data, data + size); 58 | _png_cursor += size; 59 | } 60 | 61 | static void _read_fn(png_structp png_ptr, unsigned char *data, unsigned long size) { 62 | // printf("read %p %lu\n", data, size); 63 | memcpy(data, &_png_file[_png_cursor], size); 64 | _png_cursor += size; 65 | } 66 | 67 | EMSCRIPTEN_BINDINGS(encode) { 68 | emscripten::function("encode", FUNCTION(emscripten::val, (emscripten::val image_data), { 69 | _png_error.clear(); 70 | _png_file.clear(); 71 | _png_cursor = 0; 72 | 73 | png_uint_32 width = image_data["width"].as(); 74 | png_uint_32 height = image_data["height"].as(); 75 | int bytes_per_pixel = 4; 76 | std::vector data; 77 | data.resize(width * height * bytes_per_pixel); 78 | emscripten::val(emscripten::typed_memory_view(data.size(), data.data())).call("set", image_data["data"]); 79 | 80 | int bit_depth = 8; 81 | int color_type = PNG_COLOR_TYPE_RGBA; 82 | int interlace_type = PNG_INTERLACE_NONE; 83 | int compression_method = PNG_COMPRESSION_TYPE_DEFAULT; 84 | int filter_method = PNG_FILTER_TYPE_DEFAULT; 85 | 86 | png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, _error_fn, _warn_fn); 87 | if (png_ptr == NULL) { 88 | return _get_error(); 89 | } 90 | png_infop info_ptr = png_create_info_struct(png_ptr); 91 | if (info_ptr == NULL) { 92 | png_destroy_write_struct(&png_ptr, NULL); 93 | return _get_error(); 94 | } 95 | if (setjmp(png_jmpbuf(png_ptr))) { 96 | _png_file.clear(); 97 | _png_cursor = 0; 98 | png_destroy_write_struct(&png_ptr, &info_ptr); 99 | return _get_error(); 100 | } 101 | png_set_write_fn(png_ptr, NULL, _write_fn, NULL); 102 | png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, compression_method, filter_method); 103 | png_write_info(png_ptr, info_ptr); 104 | png_bytep row_pointers[height]; 105 | for (int row = 0; row < height; row++) { 106 | row_pointers[row] = (png_bytep) data.data() + row * png_get_rowbytes(png_ptr, info_ptr); 107 | } 108 | png_write_image(png_ptr, row_pointers); 109 | png_write_end(png_ptr, info_ptr); 110 | png_destroy_write_struct(&png_ptr, &info_ptr); 111 | 112 | emscripten::val png_file = emscripten::val::global("Uint8Array").new_(emscripten::typed_memory_view(_png_file.size(), _png_file.data())); 113 | 114 | _png_file.clear(); 115 | _png_cursor = 0; 116 | _png_error.clear(); 117 | 118 | return png_file; 119 | })); 120 | } 121 | 122 | EMSCRIPTEN_BINDINGS(decode) { 123 | emscripten::function("decode", FUNCTION(emscripten::val, (emscripten::val png_file), { 124 | _png_error.clear(); 125 | _png_file.clear(); 126 | _png_cursor = 0; 127 | 128 | _png_file.resize(png_file["length"].as()); 129 | emscripten::val(emscripten::typed_memory_view(_png_file.size(), _png_file.data())).call("set", png_file); 130 | _png_cursor = 0; 131 | 132 | png_uint_32 width = 0; 133 | png_uint_32 height = 0; 134 | int bytes_per_pixel = 4; 135 | std::vector data; 136 | 137 | int bit_depth = 0; 138 | int color_type = 0; 139 | int interlace_type = 0; 140 | int compression_method = 0; 141 | int filter_method = 0; 142 | 143 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, _error_fn, _warn_fn); 144 | if (png_ptr == NULL) { 145 | return _get_error(); 146 | } 147 | png_infop info_ptr = png_create_info_struct(png_ptr); 148 | if (info_ptr == NULL) { 149 | png_destroy_read_struct(&png_ptr, NULL, NULL); 150 | return _get_error(); 151 | } 152 | if (setjmp(png_jmpbuf(png_ptr))) { 153 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 154 | _png_file.clear(); 155 | _png_cursor = 0; 156 | return _get_error(); 157 | } 158 | png_set_read_fn(png_ptr, NULL, _read_fn); 159 | png_read_info(png_ptr, info_ptr); 160 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_method, &filter_method); 161 | #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED 162 | png_set_scale_16(png_ptr); 163 | #else 164 | png_set_strip_16(png_ptr); 165 | #endif 166 | if (color_type == PNG_COLOR_TYPE_PALETTE) { 167 | png_set_palette_to_rgb(png_ptr); 168 | } 169 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { 170 | png_set_expand_gray_1_2_4_to_8(png_ptr); 171 | } 172 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) != 0) { 173 | png_set_tRNS_to_alpha(png_ptr); 174 | } 175 | if (color_type == PNG_COLOR_TYPE_RGB) { 176 | png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); 177 | } 178 | data.resize(height * png_get_rowbytes(png_ptr, info_ptr)); 179 | png_bytep row_pointers[height]; 180 | for (int row = 0; row < height; row++) { 181 | row_pointers[row] = (png_bytep) data.data() + row * png_get_rowbytes(png_ptr, info_ptr); 182 | } 183 | png_read_image(png_ptr, row_pointers); 184 | png_read_end(png_ptr, info_ptr); 185 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 186 | 187 | _png_file.clear(); 188 | _png_cursor = 0; 189 | _png_error.clear(); 190 | 191 | emscripten::val ImageData = emscripten::val::global("ImageData"); 192 | if (!ImageData.isUndefined()) { 193 | return ImageData.new_( 194 | emscripten::val::global("Uint8ClampedArray").new_(emscripten::typed_memory_view(data.size(), data.data())), 195 | emscripten::val(width), 196 | emscripten::val(height)); 197 | } else { 198 | emscripten::val image_data = emscripten::val::object(); 199 | image_data.set("width", emscripten::val(width)); 200 | image_data.set("height", emscripten::val(height)); 201 | image_data.set("data", emscripten::val::global("Uint8ClampedArray").new_(emscripten::typed_memory_view(data.size(), data.data()))); 202 | return image_data; 203 | } 204 | })); 205 | } 206 | -------------------------------------------------------------------------------- /emscripten-bindings/binding.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dlib/dlib/image_processing.h" 3 | 4 | using namespace emscripten; 5 | using namespace dlib; 6 | 7 | void array2d_set( 8 | array2d &arr, 9 | const unsigned &y, 10 | const unsigned &x, 11 | const rgb_pixel &value 12 | ) { 13 | arr[y][x] = value; 14 | } 15 | 16 | void start_track( 17 | correlation_tracker &correlation_tracker, 18 | const array2d &img, 19 | const drectangle &bounding_box 20 | ) { 21 | return correlation_tracker.start_track(img, bounding_box); 22 | } 23 | 24 | array2d read_image_data( 25 | uintptr_t image_data_ptr_r, 26 | const unsigned &width, 27 | const unsigned &height 28 | ) { 29 | const unsigned char *image_data_ptr = reinterpret_cast(image_data_ptr_r); 30 | array2d output_arr(height, width); 31 | int x, y; 32 | for (y = 0; y < height; y++) { 33 | for (x = 0; x < width; x++) { 34 | unsigned int i = ((y * width) + x) * 4; 35 | output_arr[y][x] = rgb_pixel( 36 | image_data_ptr[i], 37 | image_data_ptr[i + 1], 38 | image_data_ptr[i + 2] 39 | ); 40 | } 41 | } 42 | return output_arr; 43 | } 44 | 45 | drectangle update( 46 | correlation_tracker &correlation_tracker, 47 | const array2d &img 48 | ) { 49 | correlation_tracker.update(img); 50 | return correlation_tracker.get_position(); 51 | } 52 | 53 | drectangle update_guess( 54 | correlation_tracker &correlation_tracker, 55 | const array2d &img, 56 | const drectangle &bounding_box 57 | ) { 58 | correlation_tracker.update(img, bounding_box); 59 | return correlation_tracker.get_position(); 60 | } 61 | 62 | EMSCRIPTEN_BINDINGS(my_module) { 63 | value_array("rgb_pixel") 64 | .element(&rgb_pixel::red) 65 | .element(&rgb_pixel::green) 66 | .element(&rgb_pixel::blue) 67 | ; 68 | 69 | function("readImageData", &read_image_data); 70 | 71 | class_>("Array2D") 72 | .constructor<>() 73 | .constructor() 74 | .function("setSize", &array2d::set_size) 75 | .function("set", &array2d_set) 76 | .property("width", &array2d::nc) 77 | .property("height", &array2d::nr) 78 | ; 79 | 80 | class_("Rectangle") 81 | .constructor() 82 | .property("left", select_const(&drectangle::left)) 83 | .property("top", select_const(&drectangle::top)) 84 | .property("right", select_const(&drectangle::right)) 85 | .property("bottom", select_const(&drectangle::bottom)) 86 | .property("width", &drectangle::width) 87 | .property("height", &drectangle::height) 88 | ; 89 | 90 | class_("CorrelationTracker") 91 | .constructor<>() 92 | .constructor() 93 | .constructor() 94 | .constructor() 95 | .constructor() 96 | .constructor() 97 | .constructor() 98 | .constructor() 99 | .constructor() 100 | .function("startTrack", &start_track) 101 | .function("predict", &update) 102 | .function("update", &update_guess) 103 | .function("getPosition", &correlation_tracker::get_position) 104 | ; 105 | } 106 | -------------------------------------------------------------------------------- /emscripten-bindings/embind.cpp: -------------------------------------------------------------------------------- 1 | //https://github.com/ntotani/hime 2 | #include 3 | #include 4 | 5 | #include "emscripten/bind.h" 6 | 7 | #include "hime/session.h" 8 | 9 | namespace 10 | { 11 | 12 | using emscripten::base; 13 | using emscripten::class_; 14 | using emscripten::enum_; 15 | using emscripten::function; 16 | using emscripten::pure_virtual; 17 | using emscripten::register_vector; 18 | using emscripten::wrapper; 19 | using hime::MasterPiece; 20 | using hime::OwnedPiece; 21 | using hime::Parameter; 22 | using hime::PieceAction; 23 | using hime::Planet; 24 | using hime::Session; 25 | using hime::SessionContext; 26 | using hime::SessionContextImpl; 27 | using hime::SessionPiece; 28 | using hime::Skill; 29 | using std::make_shared; 30 | using std::make_unique; 31 | using std::shared_ptr; 32 | using std::string; 33 | using std::unique_ptr; 34 | using std::vector; 35 | 36 | EMSCRIPTEN_BINDINGS(hime) 37 | { 38 | enum_("Planet") 39 | .value("kSun", Planet::kSun) 40 | .value("kMoon", Planet::kMoon) 41 | .value("kMars", Planet::kMars) 42 | .value("kMercury", Planet::kMercury) 43 | .value("kJupiter", Planet::kJupiter) 44 | .value("kVenus", Planet::kVenus) 45 | .value("kSaturn", Planet::kSaturn); 46 | enum_("PieceAction") 47 | .value("kPhysical", PieceAction::kPhysical) 48 | .value("kMagical", PieceAction::kMagical) 49 | .value("kHeal", PieceAction::kHeal); 50 | class_("Skill") 51 | .smart_ptr_constructor("shared_ptr", 52 | &make_shared) 54 | .property("id", &Skill::id) 55 | .property("name", &Skill::name) 56 | .property("desc", &Skill::desc) 57 | .property("rate", &Skill::rate); 58 | } 59 | 60 | EMSCRIPTEN_BINDINGS(piece) 61 | { 62 | class_("Parameter") 63 | .constructor() 64 | .property("power", &Parameter::power) 65 | .property("defense", &Parameter::defense) 66 | .property("resist", &Parameter::resist); 67 | class_("MasterPiece") 68 | .smart_ptr_constructor("shared_ptr", 69 | &make_shared, shared_ptr, Parameter>) 72 | .property("id", &MasterPiece::id) 73 | .property("name", &MasterPiece::name) 74 | .property("planet", &MasterPiece::planet) 75 | .property("action", &MasterPiece::action) 76 | .property("active_skill", &MasterPiece::active_skill) 77 | .property("passive_skill", &MasterPiece::passive_skill) 78 | .property("param", &MasterPiece::param); 79 | class_("OwnedPiece") 80 | .smart_ptr_constructor("shared_ptr", 81 | &make_shared, const string &>) 83 | .property("master", &OwnedPiece::master) 84 | .property("id", &OwnedPiece::id); 85 | class_("SessionPiece") 86 | .property("id", &SessionPiece::id); 87 | } 88 | 89 | struct SessionContextWrapper : public wrapper 90 | { 91 | EMSCRIPTEN_WRAPPER(SessionContextWrapper); 92 | int random() 93 | { 94 | return call("random"); 95 | } 96 | }; 97 | 98 | unique_ptr session_factory(int seed, int player_num, 99 | int board_id, int deck_id, 100 | const vector>> &pieces) 101 | { 102 | return make_unique(make_unique(seed), 103 | player_num, board_id, deck_id, pieces); 104 | } 105 | 106 | EMSCRIPTEN_BINDINGS(session) 107 | { 108 | class_("SessionContext") 109 | .function("random", &SessionContext::random, pure_virtual()) 110 | .allow_subclass("SessionContextWrapper"); 111 | class_>("SessionContextImpl") 112 | .function("random", &SessionContextImpl::random); 113 | register_vector>("OwnedPieceVector"); 114 | register_vector>>( 115 | "OwnedPieceVectorVector"); 116 | class_("Session") 117 | .property("player_num", &Session::player_num) 118 | .property("owned_pieces", &Session::owned_pieces); 119 | function("session_factory", &session_factory); 120 | } 121 | 122 | } // namespace 123 | -------------------------------------------------------------------------------- /emscripten-bindings/opusscript_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../opus-native/include/opus.h" 4 | 5 | #define APPLICATION OPUS_APPLICATION_AUDIO 6 | #define MAX_PACKET_SIZE 1276 * 3 7 | #define MAX_FRAME_SIZE 960 * 6 8 | 9 | using namespace emscripten; 10 | 11 | class OpusScriptHandler { 12 | private: 13 | int application; 14 | int channels; 15 | opus_int32 sampling_rate; 16 | 17 | OpusEncoder* encoder; 18 | OpusDecoder* decoder; 19 | 20 | opus_int16* out_pcm; 21 | public: 22 | OpusScriptHandler(opus_int32 sampling_rate, int channels, int application): 23 | sampling_rate(sampling_rate), channels(channels), application(application) { 24 | 25 | out_pcm = new opus_int16[MAX_FRAME_SIZE * channels * 2]; 26 | 27 | int encoder_error; 28 | encoder = opus_encoder_create(sampling_rate, channels, application, &encoder_error); 29 | if(encoder_error < 0) { 30 | throw encoder_error; 31 | } 32 | 33 | int decoder_error; 34 | decoder = opus_decoder_create(sampling_rate, channels, &decoder_error); 35 | if(decoder_error < 0) { 36 | throw decoder_error; 37 | } 38 | } 39 | 40 | ~OpusScriptHandler() { 41 | opus_encoder_destroy(encoder); 42 | opus_decoder_destroy(decoder); 43 | delete out_pcm; 44 | } 45 | 46 | int _encode(int input_buffer, int bytes, int output_buffer, int frame_size) { 47 | opus_int16* input = reinterpret_cast(input_buffer); 48 | unsigned char* output = reinterpret_cast(output_buffer); 49 | 50 | for(int i = 0; i < bytes; i++) { 51 | input[i] = input[2 * i + 1] << 8 | input[2 * i]; 52 | } 53 | 54 | return opus_encode(encoder, input, frame_size, output, MAX_PACKET_SIZE); 55 | } 56 | 57 | int _decode(int input_buffer, int bytes, int output_buffer) { 58 | unsigned char* input = reinterpret_cast(input_buffer); 59 | short* pcm = reinterpret_cast(output_buffer); 60 | 61 | int len = opus_decode(decoder, input, bytes, out_pcm, MAX_FRAME_SIZE, 0); 62 | 63 | for(int i = 0; i < len * channels; i++) { 64 | pcm[2 * i] = out_pcm[i] & 0xFF; 65 | pcm[2 * i + 1] = (out_pcm[i] >> 8) & 0xFF; 66 | } 67 | 68 | return len; 69 | } 70 | 71 | int _encoder_ctl(int ctl, int arg) { 72 | return opus_encoder_ctl(encoder, ctl, arg); 73 | } 74 | 75 | int _decoder_ctl(int ctl, int arg) { 76 | return opus_decoder_ctl(decoder, ctl, arg); 77 | } 78 | 79 | static void destroy_handler(OpusScriptHandler *handler) { 80 | delete handler; 81 | } 82 | }; 83 | 84 | EMSCRIPTEN_BINDINGS(OpusScriptHandler) { 85 | class_("OpusScriptHandler") 86 | .constructor() 87 | .function("_encode", &OpusScriptHandler::_encode) 88 | .function("_decode", &OpusScriptHandler::_decode) 89 | .function("_encoder_ctl", &OpusScriptHandler::_encoder_ctl) 90 | .function("_decoder_ctl", &OpusScriptHandler::_decoder_ctl) 91 | .class_function("destroy_handler", &OpusScriptHandler::destroy_handler, allow_raw_pointers()); 92 | } -------------------------------------------------------------------------------- /examples/BermudanSwaption.cpp: -------------------------------------------------------------------------------- 1 | // Compile with: 2 | // emcc -I${BOOST} -I${QUANTLIB} -s BINARYEN_TRAP_MODE=clamp -O3 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=1 -std=c++14 -o BermudanSwaption.js BermudanSwaption.cpp ${QUANTLIB}/ql/.libs/libQuantLib.a 3 | 4 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 5 | 6 | /*! 7 | Copyright (C) 2002, 2003 Sadruddin Rejeb 8 | Copyright (C) 2004 Ferdinando Ametrano 9 | Copyright (C) 2005, 2006, 2007 StatPro Italia srl 10 | 11 | This file is part of QuantLib, a free-software/open-source library 12 | for financial quantitative analysts and developers - http://quantlib.org/ 13 | 14 | QuantLib is free software: you can redistribute it and/or modify it 15 | under the terms of the QuantLib license. You should have received a 16 | copy of the license along with this program; if not, please email 17 | . The license is also available online at 18 | . 19 | 20 | This program is distributed in the hope that it will be useful, but WITHOUT 21 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 22 | FOR A PARTICULAR PURPOSE. See the license for more details. 23 | */ 24 | 25 | #include 26 | #ifdef BOOST_MSVC 27 | #include 28 | #endif 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | using namespace QuantLib; 52 | 53 | #if defined(QL_ENABLE_SESSIONS) 54 | namespace QuantLib 55 | { 56 | 57 | Integer sessionId() { return 0; } 58 | 59 | } // namespace QuantLib 60 | #endif 61 | 62 | //Number of swaptions to be calibrated to... 63 | 64 | Size numRows = 5; 65 | Size numCols = 5; 66 | 67 | Integer swapLenghts[] = { 68 | 1, 2, 3, 4, 5}; 69 | Volatility swaptionVols[] = { 70 | 0.1490, 0.1340, 0.1228, 0.1189, 0.1148, 71 | 0.1290, 0.1201, 0.1146, 0.1108, 0.1040, 72 | 0.1149, 0.1112, 0.1070, 0.1010, 0.0957, 73 | 0.1047, 0.1021, 0.0980, 0.0951, 0.1270, 74 | 0.1000, 0.0950, 0.0900, 0.1230, 0.1160}; 75 | 76 | void calibrateModel( 77 | const ext::shared_ptr &model, 78 | const std::vector> &helpers) 79 | { 80 | 81 | LevenbergMarquardt om; 82 | model->calibrate(helpers, om, 83 | EndCriteria(400, 100, 1.0e-8, 1.0e-8, 1.0e-8)); 84 | 85 | // Output the implied Black volatilities 86 | for (Size i = 0; i < numRows; i++) 87 | { 88 | Size j = numCols - i - 1; // 1x5, 2x4, 3x3, 4x2, 5x1 89 | Size k = i * numCols + j; 90 | Real npv = helpers[i]->modelValue(); 91 | Volatility implied = helpers[i]->impliedVolatility(npv, 1e-4, 92 | 1000, 0.05, 0.50); 93 | Volatility diff = implied - swaptionVols[k]; 94 | 95 | std::cout << i + 1 << "x" << swapLenghts[j] 96 | << std::setprecision(5) << std::noshowpos 97 | << ": model " << std::setw(7) << io::volatility(implied) 98 | << ", market " << std::setw(7) 99 | << io::volatility(swaptionVols[k]) 100 | << " (" << std::setw(7) << std::showpos 101 | << io::volatility(diff) << std::noshowpos << ")\n"; 102 | } 103 | } 104 | 105 | int main(int, char *[]) 106 | { 107 | 108 | try 109 | { 110 | 111 | boost::timer timer; 112 | std::cout << std::endl; 113 | 114 | Date todaysDate(15, February, 2002); 115 | Calendar calendar = TARGET(); 116 | Date settlementDate(19, February, 2002); 117 | Settings::instance().evaluationDate() = todaysDate; 118 | 119 | // flat yield term structure impling 1x5 swap at 5% 120 | ext::shared_ptr flatRate(new SimpleQuote(0.04875825)); 121 | Handle rhTermStructure( 122 | ext::make_shared( 123 | settlementDate, Handle(flatRate), 124 | Actual365Fixed())); 125 | 126 | // Define the ATM/OTM/ITM swaps 127 | Frequency fixedLegFrequency = Annual; 128 | BusinessDayConvention fixedLegConvention = Unadjusted; 129 | BusinessDayConvention floatingLegConvention = ModifiedFollowing; 130 | DayCounter fixedLegDayCounter = Thirty360(Thirty360::European); 131 | Frequency floatingLegFrequency = Semiannual; 132 | VanillaSwap::Type type = VanillaSwap::Payer; 133 | Rate dummyFixedRate = 0.03; 134 | ext::shared_ptr indexSixMonths(new Euribor6M(rhTermStructure)); 135 | 136 | Date startDate = calendar.advance(settlementDate, 1, Years, 137 | floatingLegConvention); 138 | Date maturity = calendar.advance(startDate, 5, Years, 139 | floatingLegConvention); 140 | Schedule fixedSchedule(startDate, maturity, Period(fixedLegFrequency), 141 | calendar, fixedLegConvention, fixedLegConvention, 142 | DateGeneration::Forward, false); 143 | Schedule floatSchedule(startDate, maturity, Period(floatingLegFrequency), 144 | calendar, floatingLegConvention, floatingLegConvention, 145 | DateGeneration::Forward, false); 146 | 147 | ext::shared_ptr swap(new VanillaSwap( 148 | type, 1000.0, 149 | fixedSchedule, dummyFixedRate, fixedLegDayCounter, 150 | floatSchedule, indexSixMonths, 0.0, 151 | indexSixMonths->dayCounter())); 152 | swap->setPricingEngine(ext::shared_ptr( 153 | new DiscountingSwapEngine(rhTermStructure))); 154 | Rate fixedATMRate = swap->fairRate(); 155 | Rate fixedOTMRate = fixedATMRate * 1.2; 156 | Rate fixedITMRate = fixedATMRate * 0.8; 157 | 158 | ext::shared_ptr atmSwap(new VanillaSwap( 159 | type, 1000.0, 160 | fixedSchedule, fixedATMRate, fixedLegDayCounter, 161 | floatSchedule, indexSixMonths, 0.0, 162 | indexSixMonths->dayCounter())); 163 | ext::shared_ptr otmSwap(new VanillaSwap( 164 | type, 1000.0, 165 | fixedSchedule, fixedOTMRate, fixedLegDayCounter, 166 | floatSchedule, indexSixMonths, 0.0, 167 | indexSixMonths->dayCounter())); 168 | ext::shared_ptr itmSwap(new VanillaSwap( 169 | type, 1000.0, 170 | fixedSchedule, fixedITMRate, fixedLegDayCounter, 171 | floatSchedule, indexSixMonths, 0.0, 172 | indexSixMonths->dayCounter())); 173 | 174 | // defining the swaptions to be used in model calibration 175 | std::vector swaptionMaturities; 176 | swaptionMaturities.push_back(Period(1, Years)); 177 | swaptionMaturities.push_back(Period(2, Years)); 178 | swaptionMaturities.push_back(Period(3, Years)); 179 | swaptionMaturities.push_back(Period(4, Years)); 180 | swaptionMaturities.push_back(Period(5, Years)); 181 | 182 | std::vector> swaptions; 183 | 184 | // List of times that have to be included in the timegrid 185 | std::list