pax_global_header00006660000000000000000000000064152267352620014524gustar00rootroot0000000000000052 comment=04cdb66878130c19446adf19b75bda91f2b24133 zgimbutas-mwrap-04cdb66/000077500000000000000000000000001522673526200152675ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/.github/000077500000000000000000000000001522673526200166275ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/.github/workflows/000077500000000000000000000000001522673526200206645ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/.github/workflows/ci.yml000066400000000000000000000073601522673526200220100ustar00rootroot00000000000000name: Continuous Integration on: push: branches: [ master ] pull_request: branches: [ master ] jobs: makefile-build: name: Makefile build and test runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential \ bison \ flex \ zlib1g-dev \ octave \ octave-dev - name: Build run: make bin - name: Run tests run: make test cmake-build: name: CMake build and test runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential \ cmake \ bison \ flex \ zlib1g-dev - name: Configure CMake run: cmake -S . -B build -DBUILD_TESTING=ON - name: Build run: cmake --build build --target mwrap - name: Run tests run: cmake --build build --target mwrap_tests matlab-examples: name: Build and test MATLAB examples runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential \ cmake \ bison \ flex \ zlib1g-dev - name: Set up MATLAB uses: matlab-actions/setup-matlab@v2 - name: Configure with CMake run: | cmake -S . -B build-matlab \ -DMWRAP_BUILD_EXAMPLES=ON \ -DMWRAP_COMPILE_MEX=ON \ -DBUILD_TESTING=ON - name: Build examples and tests run: | cmake --build build-matlab --target mwrap_examples cmake --build build-matlab --target mwrap_testing_mex - name: Run MATLAB tests uses: matlab-actions/run-command@v2 with: command: addpath('testing'); run_matlab_tests('build-matlab', '.') octave-examples: name: Build and test Octave examples runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential \ cmake \ bison \ flex \ zlib1g-dev \ octave \ octave-dev - name: Configure examples with CMake run: | cmake -S . -B build-octave \ -DMWRAP_BUILD_EXAMPLES=ON \ -DMWRAP_COMPILE_MEX=ON \ -DMWRAP_MEX_BACKEND=OCTAVE \ -DBUILD_TESTING=ON - name: Build Octave examples run: cmake --build build-octave --target mwrap_examples - name: Run Octave example tests run: ctest --test-dir build-octave --output-on-failure -R '^octave-' python-tests: name: Python mwrap tests runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential \ cmake \ bison \ flex \ zlib1g-dev - name: Build C++ mwrap run: | cmake -S . -B build cmake --build build --target mwrap - name: Run Python tests run: bash testing/test_python.sh build/src/mwrap python/mwrap zgimbutas-mwrap-04cdb66/CLAUDE.md000066400000000000000000000131571522673526200165550ustar00rootroot00000000000000# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview MWrap is a MEX interface generator: from annotated `.mw` interface files it produces a C MEX gateway (`-c out.c`) and MATLAB/Octave caller scripts (`-m`/`-mb`) for wrapping C/C++/Fortran/CUDA functions. There are two implementations that must produce **byte-identical output**: - `src/` — the original C++ implementation (Flex/Bison) - `python/` — a pure-Python port (Python 3.7+, no dependencies, no build step) The processing pipeline in both is: Lexer → Parser → Type Checker → Code Generator. File correspondence: | C++ | Python | |---|---| | `src/mwrap.l` (flex) | `python/mwrap_lexer.py` | | `src/mwrap.y.in` → `mwrap.y` (bison grammar + `main`) | `python/mwrap_parser.py`, `python/mwrap` | | `src/mwrap-typecheck.cc` | `python/mwrap_typecheck.py` | | `src/mwrap-cgen.cc` | `python/mwrap_cgen.py` | | `src/mwrap-mgen.cc` | `python/mwrap_mgen.py` | | `src/mwrap-ast.{h,cc}` | `python/mwrap_ast.py` | | `src/mwrap-support.c` (runtime helpers) | `python/mwrap_support.c` | ## Critical invariants — read before editing 1. **Generator parity.** Any change to emitted text in `src/mwrap-cgen.cc` or `src/mwrap-mgen.cc` must be mirrored byte-for-byte in `python/mwrap_cgen.py` / `python/mwrap_mgen.py` (and vice versa), including whitespace. The oracle is: ```bash bash testing/test_python.sh ./mwrap python/mwrap ``` All checks must pass after any generator or typecheck change. 2. **Support-code sync.** `src/mwrap-support.c` and `python/mwrap_support.c` must remain **byte-identical** (`diff` them after editing either). The C++ build embeds this code via `src/mwrap-support.h`, which is **generated** (not tracked) by `src/stringify` from `mwrap-support.c` — never edit the `.h`. The Python port reads `python/mwrap_support.c` at runtime. 3. **Version single-sourcing.** The version lives in exactly two constants: `src/mwrap-version.h` (`MWRAP_VERSION`) and `python/mwrap_version.py` (`__version__`). Both the `--help` banner and the `/* Code generated by mwrap X.Y.Z */` stamp in every generated file derive from them; the doc title is substituted at build time from the header. Because the stamp is part of the generated output, the parity suite fails if the two constants ever disagree. A release bumps these two files plus the NEWS heading — nothing else. 4. **Grammar file generation.** `src/mwrap.y` is generated from `src/mwrap.y.in` (a `sed` substitutes `@ERROR_VERBOSE@` for the local Bison version). Edit `mwrap.y.in`, then run `make bin` to regenerate; both files are currently tracked, so commit them together. `src/mwrap.cc`, `src/lex.yy.c`, `src/mwrap.hh` are bison/flex outputs and gitignored. 5. **Typecheck error messages** are pinned by `testing/test_typecheck.ref` and must be identical in both implementations. To regenerate a `.ref` after an intentional change, run from the `testing/` directory (paths appear in the output): ```bash cd testing && ../mwrap -cppcomplex test_typecheck.mw 2> test_typecheck.ref ``` The same applies to `test_syntax.ref` (note: Bison ≥ 3.6 prints "end of file" where the ref says `$end`; the CMake and Python harnesses normalize this). ## Build ### Makefile (primary for development) ```bash make # or `make bin`: builds ./mwrap (requires flex + bison) make test # builds mwrap, then generates, compiles (mkoctfile --mex), # and runs the test suite under Octave ``` `make.inc` selects the MEX backend (MATLAB `mex` vs Octave `mkoctfile --mex`) and flags. ### CMake ```bash mkdir -p build && cd build && cmake .. && cmake --build . ctest # test_syntax + test_typecheck log tests ``` Options: `MWRAP_ENABLE_MATLAB_CLASSDEF` (adds `-DR2008OO` to MEX wrappers compiled via `cmake/MwrapAddMex.cmake`), `MWRAP_BUILD_EXAMPLES`, `MWRAP_COMPILE_MEX`, `MWRAP_MEX_BACKEND=MATLAB|OCTAVE|ALL`. Complex support is not a build option — it is selected per run with the `-c99complex` / `-cppcomplex` command-line flags. ### Python version No build. Run directly: `python/mwrap -mex name -c out.c -m out.m input.mw` ## Tests - `bash testing/test_python.sh ./mwrap python/mwrap` — the parity suite: reference-file checks (`test_syntax`, `test_typecheck`) plus byte-diff of C++ vs Python generated output for every feature area. Run this after any change to either implementation. - `make test` (repo root) — Octave end-to-end: generates wrappers, compiles them with `mkoctfile --mex`, runs `testing/test_all.m`. Failures print `Failure: ...` lines (grep for them; the run itself does not exit nonzero on tassert failures). - MATLAB: `testing/run_matlab_tests.m` (used by CI), or build the examples via CMake with `-DMWRAP_COMPILE_MEX=ON`. - GPU tests (`testing/test_gpu*.mw`) need MATLAB + Parallel Computing Toolbox + CUDA, but `mwrap -gpu` **code generation** needs nothing special. ## Behavior contracts worth knowing - mwrap exits nonzero and suppresses the C output file when any input file is unreadable or contains syntax or type errors; an unwritable `-m`/`-c` target is a hard error. Bison error recovery still reports every error in a file before failing. - The generated MEX gateway trusts the generated `.m` wrappers to pass the right argument counts (no `nrhs` validation in stubs yet). - Wrapped MEX files support both the pre-2018a split-complex API and the R2018a interleaved API (`MX_HAS_INTERLEAVED_COMPLEX`); generator changes must emit correct code for **both** halves of every `#if` pair. Octave compiles the old-API half only. zgimbutas-mwrap-04cdb66/CMakeLists.txt000066400000000000000000000077561522673526200200460ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) project(mwrap LANGUAGES C CXX) include(CTest) option(MWRAP_ENABLE_MATLAB_CLASSDEF "Compile generated MEX wrappers with -DR2008OO (MATLAB classdef support)" OFF) option(MWRAP_BUILD_EXAMPLES "Build the MATLAB/Octave examples" OFF) option(MWRAP_COMPILE_MEX "Compile generated wrappers into MEX binaries" OFF) set(MWRAP_MEX_BACKEND "MATLAB" CACHE STRING "Select the MEX backend to use (MATLAB, OCTAVE, or ALL)") set_property(CACHE MWRAP_MEX_BACKEND PROPERTY STRINGS MATLAB OCTAVE ALL) set(MWRAP_MEX_BACKENDS "" CACHE INTERNAL "Resolved list of MEX backends to configure" FORCE) unset(MWRAP_OCTAVE_MKOCTFILE_EXECUTABLE CACHE) if(MWRAP_COMPILE_MEX) string(TOUPPER "${MWRAP_MEX_BACKEND}" _mwrap_mex_backend_upper) if(_mwrap_mex_backend_upper STREQUAL "ALL") set(_mwrap_resolved_backends MATLAB OCTAVE) elseif(_mwrap_mex_backend_upper STREQUAL "MATLAB") set(_mwrap_resolved_backends MATLAB) elseif(_mwrap_mex_backend_upper STREQUAL "OCTAVE") set(_mwrap_resolved_backends OCTAVE) else() message(FATAL_ERROR "Invalid MWRAP_MEX_BACKEND value '${MWRAP_MEX_BACKEND}'. " "Allowed values are MATLAB, OCTAVE, or ALL.") endif() set(MWRAP_MEX_BACKENDS "${_mwrap_resolved_backends}" CACHE INTERNAL "Resolved list of MEX backends to configure" FORCE) set(_mwrap_enable_matlab OFF) set(_mwrap_enable_octave OFF) foreach(_mwrap_backend IN LISTS _mwrap_resolved_backends) if(_mwrap_backend STREQUAL "MATLAB") set(_mwrap_enable_matlab ON) elseif(_mwrap_backend STREQUAL "OCTAVE") set(_mwrap_enable_octave ON) endif() endforeach() if(_mwrap_enable_matlab) find_package(Matlab COMPONENTS MEX_COMPILER MX_LIBRARY REQUIRED) endif() if(_mwrap_enable_octave) set(_mwrap_octave_mkoctfile "") set(_mwrap_octave_executable "") find_package(Octave QUIET COMPONENTS Octave_MKOCTFILE) if(Octave_FOUND) foreach(_mwrap_candidate_var IN ITEMS Octave_OCTAVE_MKOCTFILE_EXECUTABLE Octave_MKOCTFILE_EXECUTABLE Octave_MKOCTFILE) if(NOT _mwrap_octave_mkoctfile AND DEFINED ${_mwrap_candidate_var} AND ${_mwrap_candidate_var}) set(_mwrap_octave_mkoctfile "${${_mwrap_candidate_var}}") endif() endforeach() foreach(_mwrap_candidate_var IN ITEMS Octave_OCTAVE_EXECUTABLE Octave_EXECUTABLE Octave_OCTAVECLI_EXECUTABLE Octave_OCTAVE_CLI_EXECUTABLE) if(NOT _mwrap_octave_executable AND DEFINED ${_mwrap_candidate_var} AND ${_mwrap_candidate_var}) set(_mwrap_octave_executable "${${_mwrap_candidate_var}}") endif() endforeach() endif() if(NOT _mwrap_octave_mkoctfile) unset(_mwrap_octave_mkoctfile CACHE) unset(_mwrap_octave_mkoctfile) find_program(_mwrap_octave_mkoctfile mkoctfile) endif() if(NOT _mwrap_octave_mkoctfile) message(FATAL_ERROR "MWRAP_MEX_BACKEND requested Octave support but 'mkoctfile' was not found.") endif() set(MWRAP_OCTAVE_MKOCTFILE_EXECUTABLE "${_mwrap_octave_mkoctfile}" CACHE FILEPATH "Path to the Octave mkoctfile executable" FORCE) if(NOT _mwrap_octave_executable) unset(_mwrap_octave_executable CACHE) unset(_mwrap_octave_executable) find_program(_mwrap_octave_executable NAMES octave-cli octave) endif() if(_mwrap_octave_executable) set(MWRAP_OCTAVE_EXECUTABLE "${_mwrap_octave_executable}" CACHE FILEPATH "Path to the Octave interpreter" FORCE) else() unset(MWRAP_OCTAVE_EXECUTABLE CACHE) endif() else() unset(MWRAP_OCTAVE_MKOCTFILE_EXECUTABLE CACHE) unset(MWRAP_OCTAVE_EXECUTABLE CACHE) endif() else() set(MWRAP_MEX_BACKENDS "" CACHE INTERNAL "Resolved list of MEX backends to configure" FORCE) unset(MWRAP_OCTAVE_MKOCTFILE_EXECUTABLE CACHE) unset(MWRAP_OCTAVE_EXECUTABLE CACHE) endif() add_subdirectory(src) if(MWRAP_BUILD_EXAMPLES) add_subdirectory(example) endif() if(BUILD_TESTING) add_subdirectory(testing) endif() zgimbutas-mwrap-04cdb66/COPYING000066400000000000000000000025271522673526200163300ustar00rootroot00000000000000mwrap -- MEX file generation for MATLAB and Octave Copyright (c) 2007-2008 David Bindel MIT License, with an additional clause permitting redistribution of mwrap-generated source code under any license of your choice. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. You may distribute a work that contains part or all of the source code generated by mwrap under the terms of your choice. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. zgimbutas-mwrap-04cdb66/Makefile000066400000000000000000000004551522673526200167330ustar00rootroot00000000000000include make.inc .PHONY: bin doc test demo clean realclean bin: (cd src; make) doc: (cd doc; make) test: bin (cd testing; make) demo: (cd example; make) clean: rm -f mwrap (cd src; make clean) (cd example; make clean) (cd testing; make clean) realclean: clean (cd src; make realclean) zgimbutas-mwrap-04cdb66/NEWS000066400000000000000000000140511522673526200157670ustar00rootroot00000000000000Recent updates: Version 1.3.5 (Jul 17, 2026): Single-source the version: mwrap --help and the "Code generated by mwrap" stamp in generated files now show the full patch version, taken from src/mwrap-version.h and python/mwrap_version.py Fix allocator mismatch in -i8 integer promotion (strdup freed by delete[]) and add -i8 coverage to the Python/C++ parity suite Remove the no-op MWRAP_ENABLE_C99_COMPLEX/MWRAP_ENABLE_CPP_COMPLEX CMake options; complex support is selected with -c99complex/-cppcomplex flags Fix missing newline in the generated 1-D array size check Use snprintf instead of sprintf in the generator (silences deprecation warnings on modern toolchains; snprintf is within the existing C99/C++11 requirement) Make doc/test/demo phony targets in the root Makefile, have make test build mwrap first, and gate the test_syntax reference comparison Fix crash when an @include appears in the second or later input file Fix stack buffer overflow on type or class names longer than 127 characters Reject gpu qualifier on non-array arguments that crashed the generator or produced non-compiling code; object/string inputs are still accepted with a warning, and the qualifier is cleared so the generated code is genuinely unaffected (interfaces using gpu on object/string inputs must regenerate the .m and .c outputs together, since the dispatch signature for those arguments changed) Exit with status 1 when flags are given but no input files Exit with an error when an input file cannot be read or an output file cannot be written, instead of silently succeeding Exit with an error and suppress C output when an input file contains syntax errors, instead of exiting 0 after Bison error recovery Reject inout array references ([]&), which generated non-compiling code Unpack argument dimensions even when the return array also carries dimensions; the generated size check read an uninitialized dimension Python port: match the flex lexer on invalid characters, @include line numbers and filename parsing, indented $] terminators, one-line $[, and block-C state across includes; require Python 3.7+ Fix duplicate-call bookkeeping: the first parsed call line no longer emits a redundant second stub when repeated, and "Also at" comments and the profiler report now list every duplicate occurrence, not just the first Fix unbraced loop body in the interleaved-complex mxWrapCopyDef, which wrote one element past the end of the array and left imaginary parts unset (backported from the v1.4 development branch) Fix crash when a real (non-complex) scalar is passed to a complex argument with the Matlab R2018a interleaved complex API Reject empty arrays passed to complex scalar arguments instead of crashing Reject complex arrays passed to real double/float array input arguments with the Matlab R2018a interleaved complex API instead of crashing (pre-2018a builds continue to use the real part silently, as before) Fix generated '*profile report*' and '*profile log*' commands crashing Matlab/Octave when called before '*profile on*' Exit with status 1 on any error instead of the raw error count, which wrapped to a success status at exact multiples of 256 Version 1.3 (Feb 23, 2026): Added CMake build system and GitHub Actions CI Validate input files before processing to prevent data loss Added #include for int64_t/uint64_t on Windows msys2/mingw64 Added Python mwrap implementation Version 1.2 (Apr 10, 2025): Cope with error verbose directive in both versions 2 and 3 of Bison Added support for gpuArray Added support for char scalar Version 1.1 (Jun 7, 2022): Added support for gfortran -fno-underscoring flag Version 1.0 (Aug 4, 2020): Added support for 64-bit Matlab and gcc-4.6 Added support for gcc 7.3+ Added support for Matlab R2018a complex interleaved API Added support for C99 int32_t, int64_t, uint32_t, uint64_t Allow single precision Matlab inputs and outputs Version 0.32 (Nov 5, 2008): Added support for new single-class style MATLAB objects; an example is in the manual, as well as in examples/eventq. Version 0.31 (Jun 19, 2008): Added line to license to clarify that code generated by MWrap may be redistributed under terms of the user's choice. Version 0.30 (Apr 29, 2008): Added more complete support for auto-converted objects, allowing them to be used as inout or output parameters and updating the documentation accordingly. Corrected bug in C call line checking feature (added in 0.28); corrected missing virtual destructor for Quad2d in FEM example. Version 0.29 (Apr 27, 2008): Reworked internal error handling logic. Added typedef mxArray support to allow automatic conversion of mxArrays to temporary objects on input. Version 0.28 (Mar 4, 2008): Added a check against ending a block of C call lines without finishing a statement (e.g. missing a semicolon on an isolated call line). Added support for C++ end-of-line comments inside C call lines. Added partial recovery on syntax errors. Version 0.27 (Feb 19, 2008): Added const arguments. Version 0.26 (Feb 18, 2008): Updated demos. Version 0.25 (Feb 8, 2008): Corrected code to add bounds checks on complex arrays. Added nonzero return codes on error. Version 0.24 (Feb 7, 2008): Added comment lines. Version 0.23 (Feb 6, 2008): Added logging support for profiling / coverage testing. Version 0.22 (Jan 29, 2008): Added @include support. Version 0.21 (Jan 7, 2008): Added support for string literal arguments. Added support for @function redirections. Corrected line number counting in presence of $[ $] code blocks. Version 0.20 (Sep 20, 2007): Added $[ $] delimiters for C code blocks. Version 0.19 (Sep 4, 2007): Added array references. Version 0.18 (August 15, 2007): Added interpretation of empty input array arguments as NULL. Version 0.17 (July 31, 2007): Added scoped names for access to static methods, namespaces. Version 0.16 (July 9, 2007): Added flags to set default complex support to C++ or C99 complex types Version 0.15 (July 5, 2007): Corrected complex support (needs setz macro) Version 0.14 (July 3, 2007): Added complex support Version 0.13 (June 30, 2007): Added FORTRAN bindings zgimbutas-mwrap-04cdb66/README.md000066400000000000000000000156331522673526200165560ustar00rootroot00000000000000MWrap ===== MWrap is an interface generation system in the spirit of SWIG or matwrap. **It makes wrapping C/C++/Fortran/CUDA from MATLAB almost pleasant!** From a set of augmented MATLAB script files, it generates a MEX gateway to the desired C/C++/Fortran/CUDA function calls, and also generates MATLAB function files to access that gateway. It hides the details of converting to and from MATLAB's data structures, and of allocating and freeing temporary storage. It is compatible with modern GNU Octave via `mkoctfile --mex`. It also includes `gpuArray` support (for MATLAB/CUDA only). MWrap was originally created by David Bindel in 2009. Some simple (including OpenMP) demos are to be found at [MWrapDemo](https://github.com/ahbarnett/mwrapdemo). MWrap is now used in many projects including [FINUFFT](https://finufft.readthedocs.io), [FMM2D](https://fmm2d.readthedocs.io), [FMM3D](https://fmm3d.readthedocs.io), and [FMM3DBIE](https://fmm3dbie.readthedocs.io). Users may also be interested in the new MATLAB package manager [mip](https://mip.sh/) which includes MEX interface generation. Dependencies ------------ Building MWrap needs the following tools: * A C compiler with C99 support and a C++ compiler with C++11 support * [Bison](https://www.gnu.org/software/bison/) and [Flex](https://github.com/westes/flex) * (Optional) [CMake](https://cmake.org/) 3.16 or newer (for the CMake build path) * (Optional) [MATLAB](https://www.mathworks.com/products/matlab.html) with MEX build tooling when compiling wrappers with CMake * (Optional) MATLAB Parallel Computing Toolbox, and CUDA Toolkit, if you want to wrap `gpuArray` objects. If you are using a Debian or Ubuntu based system, the required packages can be installed via ``` sudo apt-get install build-essential bison flex cmake ``` Makefile build -------------- Edit `make.inc` (this decides whether `MEX` uses MATLAB vs Octave build), and then run `make`. The output will be a standalone executable (`mwrap`) in the main directory. Bison and Flex are required for this build path. Then to make the examples use `make demo`, then cd to subdirectories of `example/` and try each out using MATLAB or Octave as appropriate. CMake build ----------- Alternatively, create a build directory and run CMake: ``` mkdir -p build cd build cmake .. cmake --build . ``` To compile generated MEX wrappers with MATLAB `classdef` support (`-DR2008OO`), configure with: ``` cmake -DMWRAP_ENABLE_MATLAB_CLASSDEF=ON .. ``` Note that C99/C++ complex support is not a build option: it is selected per run with the `-c99complex` or `-cppcomplex` command-line flags when invoking the `mwrap` executable. ### Building the MATLAB/Octave examples The example wrappers under `example/` can be generated through CMake by turning on the `MWRAP_BUILD_EXAMPLES` option. This configuration runs `mwrap` to produce the C++ gateway sources (and any requested MATLAB scaffolding). By default the build stops after source generation, mirroring the legacy Makefiles. The resulting files live under the build tree in per-example subdirectories. ``` cmake -DMWRAP_BUILD_EXAMPLES=ON .. cmake --build . --target mwrap_examples ``` To have CMake invoke MATLAB's compiler and produce loadable MEX binaries, add `-DMWRAP_COMPILE_MEX=ON` when configuring. CMake will build the generated sources into MEX targets, attach them to the `mwrap_examples` aggregate target, and skip executing the binaries during the build. When running the MATLAB smoke tests locally (for example, `ctest -R matlab-`), build the `mwrap_examples` target beforehand so that the generated wrappers and MEX binaries are available to the test runner. The `MWRAP_MEX_BACKEND` cache entry selects which toolchain to use. MATLAB backends require CMake's `FindMatlab` module to locate MATLAB's development components, while Octave backends require the `mkoctfile` executable to be available. Choosing `ALL` emits binaries for both environments (and therefore requires both toolchains). ``` cmake -DMWRAP_BUILD_EXAMPLES=ON -DMWRAP_COMPILE_MEX=ON -DMWRAP_MEX_BACKEND=MATLAB .. cmake -DMWRAP_BUILD_EXAMPLES=ON -DMWRAP_COMPILE_MEX=ON -DMWRAP_MEX_BACKEND=OCTAVE .. cmake -DMWRAP_BUILD_EXAMPLES=ON -DMWRAP_COMPILE_MEX=ON -DMWRAP_MEX_BACKEND=ALL .. ``` If you leave `MWRAP_COMPILE_MEX` disabled, you can still invoke MATLAB or Octave's `mex` tool manually using the produced `.cc` file and any support sources that your project requires. The `MWRAP_ENABLE_MATLAB_CLASSDEF` option applies to MEX wrappers compiled through CMake (`-DMWRAP_COMPILE_MEX=ON`). Example usage ------------- David Bindel's user's guide (`mwrap.pdf`) describes MWrap in detail; you can also look at the example subdirectories and the testing subdirectory to get some idea of how MWrap is used. Alex Barnett also maintains a set of minimally complete tutorial examples of calling C/Fortran libraries (including OpenMP) from MATLAB/Octave, using MWrap, at https://github.com/ahbarnett/mwrapdemo The `mwrap.1` man page was written by Nicolas Bourdaud. It should be manually placed in `/usr/local/share/man/man1/`, for instance. Contributors and version history -------------------------------- MWrap was originally written by David Bindel, c. 2009. He hosts his old version at https://www.cs.cornell.edu/~bindel/sw/mwrap This is unsupported; we recommend that you use the current version. It was moved to github in around 2015 in order to add new features, and is now maintained by Zydrunas Gimbutas, Alex Barnett, Libin Lu, Manas Rachh, Rafael Laboissière, and Marco Barbone. **Version 0.33** (c. 2009) Author: David Bindel - Initial revision, clone David's repository (c. 2015) **Version 1.0** (c. 2020) Contributors: Zydrunas Gimbutas, Alex Barnett, Libin Lu. - Add support for 64-bit Matlab and gcc-4.6 - Add support for gcc 7.3+ - Add support for Matlab R2018a complex interleaved API - Add support for C99 int32_t, int64_t, uint32_t, uint64_t - Allow single precision Matlab inputs and outputs **Version 1.1** (2022) Contributors: Manas Rachh, Zydrunas Gimbutas. - Add support for gfortran -fno-underscoring flag **Version 1.2** (2025) Contributors: Libin Lu, Rafael Laboissière, Zydrunas Gimbutas. - Cope with error verbose directive in both versions 2 and 3 of Bison - Add support for Matlab gpuArray - Add support for char scalar **Version 1.3** (2026) Contributors: Marco Barbone, Zydrunas Gimbutas. - Add CMake build system and GitHub Actions CI - Validate input files before processing to prevent data loss - Add `#include ` for int64_t/uint64_t on Windows msys2/mingw64 - Add Python mwrap implementation **Version 1.3.5** (2026) Contributors: Zydrunas Gimbutas, with Claude Code (Anthropic). - Bug-fix release: fix crashes in generated code with the Matlab R2018a interleaved complex API, fix mwrap generator crashes and silent-success exit codes, align the Python port with the flex lexer, and stamp generated files with the full patch version; see NEWS for details Also see https://github.com/zgimbutas/mwrap/tags zgimbutas-mwrap-04cdb66/cmake/000077500000000000000000000000001522673526200163475ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/cmake/MwrapAddMex.cmake000066400000000000000000000251641522673526200215320ustar00rootroot00000000000000# mwrap_add_mex.cmake - helper to generate MATLAB/Octave wrapper sources with mwrap # # Usage: # mwrap_add_mex( # MW_FILES [...] # [MEX_NAME ] # [CC_FILENAME ] # [M_FILENAME ] # [CLASSDEF_NAME ] # [MWRAP_FLAGS [...]] # [WORK_DIR ] # [EXTRA_DEPENDS [...]] # [COMPILE_DEFINITIONS [...]] # [OUTPUT_VAR ] # ) # # The function wraps the mwrap executable produced by the current build to # generate a C/C++ source file (and optional MATLAB scaffolding). It creates a # custom target named after that depends on the generated source file. # Downstream projects can request the absolute path to the generated source via # OUTPUT_VAR and add it to their own targets. set(_MWRAP_ADD_MEX_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}) function(mwrap_add_mex target_name) if(NOT target_name) message(FATAL_ERROR "mwrap_add_mex requires a target name") endif() set(options) set(oneValueArgs MEX_NAME CC_FILENAME M_FILENAME CLASSDEF_NAME WORK_DIR OUTPUT_VAR) set(multiValueArgs MW_FILES MWRAP_FLAGS EXTRA_DEPENDS EXTRA_SOURCES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS) cmake_parse_arguments(MAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(MWRAP_ENABLE_MATLAB_CLASSDEF) list(APPEND MAM_COMPILE_DEFINITIONS R2008OO) list(REMOVE_DUPLICATES MAM_COMPILE_DEFINITIONS) endif() if(NOT MAM_MW_FILES) message(FATAL_ERROR "mwrap_add_mex(${target_name}) requires MW_FILES to be specified") endif() if(NOT TARGET mwrap) message(FATAL_ERROR "mwrap_add_mex requires the mwrap executable target to exist") endif() if(NOT MAM_MEX_NAME) set(MAM_MEX_NAME "${target_name}") endif() if(NOT MAM_CC_FILENAME) if(MAM_MEX_NAME MATCHES "\\.(c|cc|cpp)$") set(MAM_CC_FILENAME "${MAM_MEX_NAME}") else() set(MAM_CC_FILENAME "${MAM_MEX_NAME}.cc") endif() endif() if(MAM_WORK_DIR) set(mwrap_binary_dir "${MAM_WORK_DIR}") else() set(mwrap_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${target_name}") endif() file(MAKE_DIRECTORY "${mwrap_binary_dir}") set(cc_output "${mwrap_binary_dir}/${MAM_CC_FILENAME}") get_filename_component(cc_basename "${cc_output}" NAME) set(mwrap_command $ -mex ${MAM_MEX_NAME} -c "${cc_output}") set(mwrap_depends ${MAM_EXTRA_DEPENDS}) set(mwrap_working_dir "${mwrap_binary_dir}") set(mwrap_inputs) set(mw_absolute_inputs) set(mw_parent_dirs) set(mwrap_stage_commands) foreach(mw IN LISTS MAM_MW_FILES) if(IS_ABSOLUTE "${mw}") set(mw_abs "${mw}") else() set(mw_abs "${CMAKE_CURRENT_SOURCE_DIR}/${mw}") endif() list(APPEND mw_absolute_inputs "${mw_abs}") get_filename_component(mw_parent "${mw_abs}" DIRECTORY) list(APPEND mw_parent_dirs "${mw_parent}") endforeach() list(REMOVE_DUPLICATES mw_parent_dirs) if(mw_parent_dirs) list(LENGTH mw_parent_dirs mw_parent_dir_count) if(mw_parent_dir_count EQUAL 1) foreach(mw_abs IN LISTS mw_absolute_inputs) get_filename_component(mw_basename "${mw_abs}" NAME) list(APPEND mwrap_inputs "${mw_basename}") list(APPEND mwrap_stage_commands COMMAND ${CMAKE_COMMAND} -E copy_if_different "${mw_abs}" "${mwrap_binary_dir}/${mw_basename}") endforeach() else() foreach(mw_abs IN LISTS mw_absolute_inputs) file(RELATIVE_PATH mw_rel "${mwrap_binary_dir}" "${mw_abs}") list(APPEND mwrap_inputs "${mw_rel}") endforeach() endif() endif() if(MAM_CLASSDEF_NAME) set(classdef_dir "${mwrap_binary_dir}/@${MAM_CLASSDEF_NAME}") endif() if(NOT mwrap_inputs) set(mwrap_inputs ${MAM_MW_FILES}) endif() if(MAM_M_FILENAME) list(APPEND mwrap_command -m "${mwrap_binary_dir}/${MAM_M_FILENAME}") endif() if(MAM_MWRAP_FLAGS) list(APPEND mwrap_command ${MAM_MWRAP_FLAGS}) endif() foreach(mw IN LISTS mwrap_inputs) list(APPEND mwrap_command "${mw}") endforeach() list(APPEND mwrap_depends ${mw_absolute_inputs}) list(APPEND mwrap_depends mwrap) set(pre_commands COMMAND ${CMAKE_COMMAND} -E make_directory "${mwrap_binary_dir}") if(classdef_dir) list(APPEND pre_commands COMMAND ${CMAKE_COMMAND} -E make_directory "${classdef_dir}") endif() list(APPEND pre_commands ${mwrap_stage_commands}) add_custom_command( OUTPUT "${cc_output}" ${pre_commands} COMMAND ${mwrap_command} WORKING_DIRECTORY "${mwrap_working_dir}" DEPENDS ${mwrap_depends} COMMENT "Generating ${cc_basename} with mwrap" VERBATIM COMMAND_EXPAND_LISTS ) set_source_files_properties("${cc_output}" PROPERTIES GENERATED TRUE) add_custom_target(${target_name} DEPENDS "${cc_output}") add_dependencies(${target_name} mwrap) if(MAM_OUTPUT_VAR) set(${MAM_OUTPUT_VAR} "${cc_output}" PARENT_SCOPE) endif() set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_SOURCE "${cc_output}") set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_DIRECTORY "${mwrap_binary_dir}") set_property(TARGET ${target_name} PROPERTY MWRAP_MEX_NAME "${MAM_MEX_NAME}") if(MAM_EXTRA_SOURCES) set_property(TARGET ${target_name} PROPERTY MWRAP_EXTRA_SOURCES "${MAM_EXTRA_SOURCES}") endif() if(MAM_INCLUDE_DIRECTORIES) set_property(TARGET ${target_name} PROPERTY MWRAP_INCLUDE_DIRECTORIES "${MAM_INCLUDE_DIRECTORIES}") endif() if(MAM_COMPILE_DEFINITIONS) set_property(TARGET ${target_name} PROPERTY MWRAP_COMPILE_DEFINITIONS "${MAM_COMPILE_DEFINITIONS}") endif() if(MAM_M_FILENAME) set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_M_FILE "${mwrap_binary_dir}/${MAM_M_FILENAME}") endif() if(classdef_dir) set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_CLASSDEF_DIR "${classdef_dir}") endif() endfunction() function(_mwrap_compile_mex target_name) set(options) set(oneValueArgs OUTPUT_VAR) set(multiValueArgs) cmake_parse_arguments(MCC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT TARGET ${target_name}) message(FATAL_ERROR "_mwrap_compile_mex was asked to process unknown target '${target_name}'") endif() if(NOT MWRAP_MEX_BACKENDS) message(FATAL_ERROR "_mwrap_compile_mex requires at least one configured backend") endif() get_target_property(cc_output ${target_name} MWRAP_OUTPUT_SOURCE) if(NOT cc_output) message(FATAL_ERROR "Target '${target_name}' does not have generated source metadata") endif() get_target_property(mwrap_binary_dir ${target_name} MWRAP_OUTPUT_DIRECTORY) if(NOT mwrap_binary_dir OR mwrap_binary_dir STREQUAL "NOTFOUND") message(FATAL_ERROR "Target '${target_name}' is missing its output directory metadata") endif() get_target_property(mex_name ${target_name} MWRAP_MEX_NAME) if(NOT mex_name) set(mex_name ${target_name}) endif() get_target_property(extra_sources ${target_name} MWRAP_EXTRA_SOURCES) if(NOT extra_sources OR extra_sources STREQUAL "NOTFOUND") unset(extra_sources) endif() set(mex_sources "${cc_output}") if(extra_sources) list(APPEND mex_sources ${extra_sources}) endif() get_target_property(include_dirs ${target_name} MWRAP_INCLUDE_DIRECTORIES) if(include_dirs STREQUAL "NOTFOUND") unset(include_dirs) endif() get_target_property(compile_defs ${target_name} MWRAP_COMPILE_DEFINITIONS) if(compile_defs STREQUAL "NOTFOUND") unset(compile_defs) endif() set(_mwrap_mex_targets) set(_mwrap_mex_paths) foreach(_mwrap_backend IN LISTS MWRAP_MEX_BACKENDS) if(_mwrap_backend STREQUAL "MATLAB") if(NOT COMMAND matlab_add_mex) message(FATAL_ERROR "MATLAB backend requested, but matlab_add_mex() is unavailable") endif() set(mex_target "${target_name}_mex") matlab_add_mex(NAME ${mex_target} SRC ${mex_sources} OUTPUT_NAME "${mex_name}") add_dependencies(${mex_target} ${target_name}) if(include_dirs) target_include_directories(${mex_target} PRIVATE ${include_dirs}) endif() if(compile_defs) target_compile_definitions(${mex_target} PRIVATE ${compile_defs}) endif() list(APPEND _mwrap_mex_targets "${mex_target}") list(APPEND _mwrap_mex_paths "$") elseif(_mwrap_backend STREQUAL "OCTAVE") if(NOT MWRAP_OCTAVE_MKOCTFILE_EXECUTABLE) message(FATAL_ERROR "Octave backend requested, but no mkoctfile executable was configured") endif() set(mex_target "${target_name}_mex_octave") set(octave_output "${mwrap_binary_dir}/${mex_name}.mex") set(octave_sources) foreach(oct_src IN LISTS mex_sources) if(IS_ABSOLUTE "${oct_src}") set(oct_src_abs "${oct_src}") else() get_filename_component(oct_src_abs "${oct_src}" ABSOLUTE) endif() list(APPEND octave_sources "${oct_src_abs}") endforeach() set(octave_include_args) if(include_dirs) foreach(inc_dir IN LISTS include_dirs) if(NOT IS_ABSOLUTE "${inc_dir}") get_filename_component(inc_dir "${inc_dir}" ABSOLUTE) endif() list(APPEND octave_include_args "-I${inc_dir}") endforeach() endif() set(octave_define_args) if(compile_defs) foreach(definition IN LISTS compile_defs) list(APPEND octave_define_args "-D${definition}") endforeach() endif() add_custom_command( OUTPUT "${octave_output}" COMMAND ${CMAKE_COMMAND} -E make_directory "${mwrap_binary_dir}" COMMAND ${MWRAP_OCTAVE_MKOCTFILE_EXECUTABLE} --mex -o "${octave_output}" ${octave_include_args} ${octave_define_args} ${octave_sources} DEPENDS ${octave_sources} ${target_name} COMMENT "Building Octave MEX ${mex_name}" VERBATIM COMMAND_EXPAND_LISTS ) add_custom_target(${mex_target} DEPENDS "${octave_output}") add_dependencies(${mex_target} ${target_name}) list(APPEND _mwrap_mex_targets "${mex_target}") list(APPEND _mwrap_mex_paths "${octave_output}") else() message(FATAL_ERROR "Unknown MEX backend '${_mwrap_backend}' requested") endif() endforeach() if(_mwrap_mex_targets) list(GET _mwrap_mex_targets 0 _mwrap_primary_target) list(GET _mwrap_mex_paths 0 _mwrap_primary_path) set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_MEX_TARGET "${_mwrap_primary_target}") set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_MEX_PATH "${_mwrap_primary_path}") set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_MEX_TARGETS "${_mwrap_mex_targets}") set_property(TARGET ${target_name} PROPERTY MWRAP_OUTPUT_MEX_PATHS "${_mwrap_mex_paths}") endif() if(MCC_OUTPUT_VAR) set(${MCC_OUTPUT_VAR} "${_mwrap_mex_targets}" PARENT_SCOPE) endif() endfunction() zgimbutas-mwrap-04cdb66/cmake/run_stringify.cmake000066400000000000000000000011351522673526200222530ustar00rootroot00000000000000if(NOT DEFINED STRINGIFY) message(FATAL_ERROR "STRINGIFY executable location is required") endif() if(NOT DEFINED INPUT) message(FATAL_ERROR "INPUT file is required") endif() if(NOT DEFINED OUTPUT) message(FATAL_ERROR "OUTPUT file is required") endif() if(NOT DEFINED NAME) message(FATAL_ERROR "String symbol NAME is required") endif() execute_process( COMMAND "${STRINGIFY}" "${NAME}" INPUT_FILE "${INPUT}" OUTPUT_FILE "${OUTPUT}" RESULT_VARIABLE _stringify_result ) if(NOT _stringify_result EQUAL 0) message(FATAL_ERROR "stringify failed with exit code ${_stringify_result}") endif() zgimbutas-mwrap-04cdb66/doc/000077500000000000000000000000001522673526200160345ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/doc/Makefile000066400000000000000000000006771522673526200175060ustar00rootroot00000000000000all: mwrap.pdf RELEASE_DATE = $(shell grep "^Version " ../NEWS | head -n1 | sed "s/^Version .*(\(.*\)):/\1/") VERSION = $(shell sed -n 's/^\#define MWRAP_VERSION "\(.*\)"/\1/p' ../src/mwrap-version.h) mwrap.tex: mwrap.tex.in ../src/mwrap-version.h sed -e "s/@RELEASE_DATE@/$(RELEASE_DATE)/" -e "s/@VERSION@/$(VERSION)/" < mwrap.tex.in > mwrap.tex mwrap.pdf: mwrap.tex pdflatex mwrap.tex clean: rm -f mwrap.aux mwrap.log mwrap.tex mwrap.pdf zgimbutas-mwrap-04cdb66/doc/mwrap.1000066400000000000000000000050501522673526200172440ustar00rootroot00000000000000.TH MWRAP 1 2012 "mwrap" "MWRAP manpage" .SH NAME mwrap - Octave/MATLAB mex generator .SH SYNOPSIS .SY mwrap .OP \-mex \fIoutputmex\fP .OP \-m \fIoutput.m\fP .OP \-c \fIoutputmex.c\fP .OP \-mb .OP \-list .OP \-catch .OP \-i8 .OP \-c99complex .OP \-cppcomplex .OP \-gpu \fIinfile1\fP \fIinfile2\fP ... .br .SH DESCRIPTION .LP \fBmwrap\fP is an interface generation system in the spirit of SWIG or matwrap. From a set of augmented Octave/MATLAB script files, \fBmwrap\fP will generate a MEX gateway to desired C/C++ function calls and \.m function files to access that gateway. The details of converting to and from Octave's or MATLAB's data structures, and of allocating and freeing temporary storage, are hidden from the user. .SH OPTIONS .TP .B \-mex specifies the name of the MEX function that the generated functions will call. This name will generally be the same as the prefix for the C/C++ output file name. . .TP .B \-m specifies the name of the Octave/MATLAB script to be generated. . .TP .B \-c specifies the name of the C MEX file to be generated. The MEX file may contain stubs corresponding to several different generated files. . .TP .B \-mb redirect Octave/MATLAB function output to files named in the input. In this mode, the processor will change Octave/MATLAB function output files whenever it encounters a line beginning with @. If @ occurs alone on a line, the output will be turned off; if the line begins with @function, the line will be treated as the first line of a function, and the m-file name will be deduced from the function name; and otherwise, the characters after @ (up to the next set of white space) will be treated as a filename, and \fBmwrap\fP will try to write to that file. . .TP .B \-list print to the standard output the names of all files that would be generated from redirect output by the \-mb flag. . .TP .B \-catch surround library calls in try/catch blocks in order to intercept C++ exceptions. . .TP .B \-i8 convert \fBint\fP, \fBlong\fP, \fBuint\fP, \fBulong\fP types to \fBint64_t\fP, \fBuint64_t\fP. This provides a convenient way to interface with \fB-fdefault-integer-8\fP and \fB-i8\fP flags used by Fortran compilers. . .TP .B \-c99complex use the C99 complex floating point types as the default \fBdcomplex\fP and \fBfcomplex\fP types. . .TP .B \-cppcomplex use the C++ complex floating point types as the default \fBdcomplex\fP and \fBfcomplex\fP types. . .TP .B \-gpu add support code for MATLAB gpuArray. .SH AUTHORS .LP \fBmwrap\fP is written by David Bindel and Zydrunas Gimbutas. This manual page was written by Nicolas Bourdaud. zgimbutas-mwrap-04cdb66/doc/mwrap.tex.in000066400000000000000000001013101522673526200203050ustar00rootroot00000000000000\documentclass[12pt]{article} \usepackage{amsmath} \usepackage{fancyheadings} \setlength{\oddsidemargin}{0.0in} \setlength{\textwidth}{6.5in} \setlength{\topmargin}{-0.5in} \setlength{\textheight}{9.0in} \bibliographystyle{unsrt} \renewcommand{\baselinestretch}{1.0} \newcommand{\mwrap}{\textsc{MWrap}} \newcommand{\gOR}{ $|$ } \title{\mwrap\ User Guide\\ Version @VERSION@} \author{D.~Bindel, A.~Barnett, Z.~Gimbutas, M.~Rachh, L.~Lu, R.~Laboissi\`ere} \date{@RELEASE_DATE@} \pagestyle{fancy} %%\headrulewidth 1.5pt %%\footrulewidth 1.5pt \chead{\mwrap} \lhead{} \rhead{} \cfoot{\thepage} \begin{document} \maketitle \section{Introduction} \mwrap\ is an interface generation system in the spirit of SWIG or matwrap. From a set of augmented MATLAB script files, \mwrap\ will generate a MEX gateway to desired C/C++ function calls and MATLAB function files to access that gateway. The details of converting to and from MATLAB's data structures, and of allocating and freeing temporary storage, are hidden from the user. \mwrap\ files look like MATLAB scripts with specially formatted extra lines that describe calls to C/C++. For example, the following code is converted into a call to the C {\tt strncat} function: \begin{verbatim} $ #include function foobar; s1 = 'foo'; s2 = 'bar'; # strncat(inout cstring[128] s1, cstring s2, int 127); fprintf('Should be foobar: %s\n', s1); \end{verbatim} There are two structured comments in this file. The line beginning with \verb|$| tells \mwrap\ that this line is C/C++ support code, and the line beginning with \verb|#| describes a call to the C {\tt strncat} function. The call description includes information about the C type to be used for the input arguments, and also describes which parameters are used for input and for output. From this input file, \mwrap\ produces two output files: a C file which will be compiled with the MATLAB {\tt mex} script, and a MATLAB script which calls that MEX file. If the above file is {\tt foobar.mw}, for example, we would generate an interface file {\tt foobar.m} and a MEX script {\tt fbmex.mex} with the lines \begin{verbatim} mwrap -mex fbmex -m foobar.m foobar.mw mwrap -mex fbmex -c fbmex.c foobar.mw mex fbmex.c \end{verbatim} At this point, we can run the {\tt foobar} script from the MATLAB prompt: \begin{verbatim} >> foobar Should be foobar: foobar \end{verbatim} Versions of GNU Octave released after mid-2006 support much of MATLAB's MEX interface. Simple {\mwrap}-generated code should work with GNU Octave. To compile for GNU Octave, use \texttt{mkoctfile --mex} instead of invoking \texttt{mex}. GNU Octave does not implement MATLAB's object model, so code that uses MATLAB's object-oriented facilities will not function with GNU Octave. \section{\mwrap\ command line} The {\tt mwrap} command line has the following form: \begin{verbatim} mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] [-mb] [-list] [-catch] [-i8] [-c99complex] [-cppcomplex] [-gpu] infile1 infile2 ... \end{verbatim} where \begin{itemize} \item {\tt -mex} specifies the name of the MEX function that the generated MATLAB functions will call. This name will generally be the same as the prefix for the C/C++ output file name. \item {\tt -m} specifies the name of the MATLAB script to be generated. \item {\tt -c} specifies the name of the C MEX file to be generated. The MEX file may contain stubs corresponding to several different generated MATLAB files. \item {\tt -mb} tells \mwrap\ to redirect MATLAB function output to files named in the input. In this mode, the processor will change MATLAB function output files whenever it encounters a line beginning with \verb|@|. If \verb|@| occurs alone on a line, MATLAB output will be turned off; if the line begins with \verb|@function|, the line will be treated as the first line of a function, and the m-file name will be deduced from the function name; and otherwise, the characters after \verb|@| (up to the next set of white space) will be treated as a filename, and \mwrap\ will try to write to that file. \item {\tt -list} tells \mwrap\ to print to the standard output the names of all files that would be generated from redirect output by the {\tt -mb} flag. \item {\tt -catch} tells \mwrap\ to surround library calls in try/catch blocks in order to intercept C++ exceptions. \item {\tt -i8} tells \mwrap\ to convert {\tt int}, {\tt long}, {\tt uint}, {\tt ulong} types to {\tt int64\_t}, {\tt uint64\_t}. This provides a convenient way to interface with {\tt -fdefault-integer-8} and {\tt -i8} flags used by Fortran compilers. \item {\tt -c99complex} tells \mwrap\ to use the C99 complex floating point types as the default {\tt dcomplex} and {\tt fcomplex} types. \item {\tt -cppcomplex} tells \mwrap\ to use the C++ complex floating point types as the default {\tt dcomplex} and {\tt fcomplex} types. \item {\tt -gpu} tells \mwrap\ to add support code for MATLAB gpuArray. \end{itemize} \section{Interface syntax} \subsection{Input line types} \mwrap\ recognizes six types of lines, based on the first non-space characters at the start of the line: \begin{itemize} \item C support lines begin with \verb|$|. These lines are copied into the C code that makes up the MEX file. Typically, such support lines are used to include any necessary header files; they can also be used to embed short functions. \item Blocks of C support can be opened by the line \verb|$[| and closed by the line \verb|$]|. Like lines beginning with \verb|$|, lines that fall between the opening and closing markers are copied into the C code that makes up the MEX file. \item C call lines begin with \verb|#|. These lines are parsed in order to generate an interface function as part of the MEX file and a MATLAB call line to invoke that interface function. C call lines can refer to variables declared in the local MATLAB environment. \item Input redirection lines (include lines) begin with \verb|@include|. The input file name should not be quoted in any way. \item Output redirection lines begin with \verb|@|. % Output redirection is used to specify several generated MATLAB scripts with a single input file. \item Comment lines begin with \verb|//|. Comment lines are not included in any output file. \item All other lines are treated as ordinary MATLAB code, and are passed through to a MATLAB output file without further processing. \end{itemize} \subsection{C call syntax} The complete syntax for \mwrap\ call statements is given in Figure~\ref{mwrap-syntax-fig}. Each statement makes a function or method call, and optionally assigns the output to a variable. For each argument or return variable, we specify the type and also say whether the variable is being used for input, for output, or for both. Variables are given by names which should be valid identifiers in the local MATLAB environment where the call is to be made. Input arguments can also be given numeric values, though it is still necessary to provide type information. There are three types of call specifications. Ordinary functions not attached to a class can be called by name: \begin{verbatim} # do_something(); \end{verbatim} To create a new C++ object instance, we use the {\tt new} command \begin{verbatim} # Thermometer* therm = new Thermometer(double temperature0); \end{verbatim} And once we have a handle to an object, we can invoke methods on it \begin{verbatim} # double temperature = therm->Thermometer.get_temperature(); \end{verbatim} Object deletion is handled just like an ordinary function call \begin{verbatim} # delete(Thermometer* therm); \end{verbatim} Intrinsic operators like {\tt sizeof} can also be invoked in this manner. The type specifications are \emph{only} used to determine how \mwrap\ should handle passing data between MATLAB and a C/C++ statement; the types specified in the call sequence should be compatible with a corresponding C/C++ definition, but they need not be identical to the types in a specific function or method declaration. An \mwrap\ type specification consists of three parts. The first (optional) part says whether the given variable will be used for input ({\tt input}), for output ({\tt output}), or for both. The second part gives the basic type name for the variable; this may be an intrinsic type like {\tt int} or {\tt double}, a string, an object type, or a MATLAB intrinsic (see Section~\ref{type-section}). Finally, there may be modifiers to specify that this is a pointer, a reference, or an array. Array and string arguments may also have explicitly provided size information. In the example from the introduction, for example the argument declaration \begin{verbatim} inout cstring[128] s1 \end{verbatim} tells \mwrap\ that {\tt s1} is a C string which is used for input and output, and that the buffer used should be at least 128 characters long. Identifiers in \mwrap\ may include C++ scope specifications to indicate that a function or method belongs to some namespace or that it is a static member of a class. That is, it is valid to write something like \begin{verbatim} std::ostream* os = foo->get_ostream(); \end{verbatim} Scoped names may be used for types or for method names, but it is an unchecked error to use a scoped name for a parameter or return variable. \begin{figure} \begin{center} \begin{tabular}{l@{ := }l} statement & returnvar {\tt = } func {\tt (} args {\tt );} \\ & func {\tt (} args {\tt );} \\ & {\tt typedef numeric} {\it type-id} {\tt ;} \\ & {\tt typedef dcomplex} {\it type-id} {\tt ;} \\ & {\tt typedef fcomplex} {\it type-id} {\tt ;} \\ & {\tt class} {\it child-id} {\tt :} {\it parent-id} {\tt ,} {\it parent-id} {\tt ,} $\ldots$ \vspace{5mm} \\ func & {\it function-id} \\ & {\tt FORTRAN} {\it function-id} \\ & {\it this-id} {\tt .} {\it class-id} {\tt ->} {\it method-id}\\ & {\tt new} {\it class-id} \vspace{5mm} \\ args & arg {\tt ,} arg {\tt ,} $\ldots$ \gOR\ $\epsilon$ \\ arg & devicespec iospec type {\it var-id} \\ & devicespec ispec type {\it value} \\ returnvar & type {\it var-id} \vspace{5mm} \\ devicespec & {\tt cpu} \gOR\ {\tt gpu} \gOR\ $\epsilon$ \\ iospec & {\tt input} \gOR\ {\tt output} \gOR\ {\tt inout} \gOR\ $\epsilon$ \\ ispec & {\tt input} \gOR\ $\epsilon$ \\ type & {\it type-id} \gOR {\it type-id} {\tt *} \gOR\ {\it type-id} {\tt \&} \gOR\ {\it type-id} {\tt [} dims {\tt]} \gOR\ {\it type-id} {\tt [} dims {\tt] \&} \\ dims & dim {\tt ,} dim {\tt ,} $\ldots$ \gOR\ $\epsilon$ \\ dim & {\it var-id} \gOR\ {\it number} \end{tabular} \caption{\mwrap\ call syntax} \label{mwrap-syntax-fig} \end{center} \end{figure} \section{Variable types} \label{type-section} \mwrap\ recognizes several different general types of variables as well as constant expressions: \subsection{Numeric types} {\it Scalars} are intrinsic numeric types in C/C++: {\tt double}, {\tt float}, {\tt long}, {\tt int}, {\tt char}, {\tt ulong}, {\tt uint}, {\tt uchar}, {\tt bool}, {\tt int32\_t}, {\tt uint32\_t}, {\tt int64\_t}, {\tt uint64\_t}, {\tt ptrdiff\_t} and {\tt size\_t}. These are the numeric types that \mwrap\ knows about by default, but if necessary, new numeric types can be declared using {\tt typedef} commands. For example, if we wanted to use {\tt float64\_t} as a numeric type, we would need the line \begin{verbatim} # typedef numeric float64_t; \end{verbatim} Ordinary scalars cannot be used as output arguments. {\it Scalar pointers} are pointers to the recognized numeric intrinsics. They are assumed to refer to {\em one} variable; that is, a {\tt double*} in \mwrap\ is a pointer to one double in memory, which is different from a double array ({\tt double[]}). {\it Scalar references} are references to the recognized numeric intrinsics. {\it Arrays} store arrays of recognized numeric intrinsics. They may have explicitly specified dimensions (in the case of pure return arrays and pure output arguments, they \emph{must} have explicitly specified dimensions), but the dimensions can also be automatically determined from the input data. If only one dimension is provided, return and output arrays are allocated as column vectors. If a function is declared to return an array or a scalar pointer and the C return value is NULL, \mwrap\ will pass an empty array back to MATLAB. If an empty array is passed to a function as an input array argument, \mwrap\ will interpret that argument as NULL. {\it Array references} are references to numeric arrays, such as in a function whose C++ prototype looks like \begin{verbatim} void output_array(const double*& data); \end{verbatim} Array references may only be used as output arguments, and the array must have explicitly specified dimensions. If the value of the data pointer returned from the C++ function is NULL, \mwrap\ will pass an empty array back to MATLAB. {\it Complex} scalars pose a special challenge. C++ and C99 provide distinct complex types, and some C89 libraries define complex numbers via structures. If the {\tt -cppcomplex} or {\tt -c99complex} flags are specified, {\tt mwrap} will automatically define complex double and single precision types {\tt dcomplex} and {\tt fcomplex} which are bound to the C++ or C99 double-precision and single-precision complex types. More generally, we allow complex numbers which are conceptually pairs of floats or doubles to be defined using {\tt typedef fcomplex} and {\tt typedef dcomplex} commands. For example, in C++, we might use the following commands to set up a double complex type {\tt cmplx} (which is equivalent to the {\tt dcomplex} type when the {\tt -cppcomplex} flag is used): \begin{verbatim} $ #include $ typedef std::complex cmplx; // Define a complex type $ #define real_cmplx(z) (z).real() // Accessors for real, imag $ #define imag_cmplx(z) (z).imag() // (req'd for complex types) $ #define setz_cmplx(z,r,i) *z = dcomplex(r,i) # typedef dcomplex cmplx; \end{verbatim} The macro definitions {\tt real\_cmplx}, {\tt imag\_cmplx}, and {\tt setz\_cmplz} are used by \mwrap\ to read or write the real and imaginary parts of a number of type {\tt cmplx}. Similar macro definitions must be provided for any other complex type to be used. Other than any setup required to define what will be used as a complex type, complex scalars can be used in all the same ways that real and integer scalars are used. \subsection{Strings} {\it Strings} are C-style null-terminated character strings. They are specified by the \mwrap\ type {\tt cstring}. A {\tt cstring} type is not equivalent to a {\tt char[]} type, since the latter is treated as an array of numbers (represented by a double vector in MATLAB) in which zero is given no particular significance. The dimensions can be of a {\tt cstring} can explicitly specified or they can be implicit. When a C string is used for output, the size of the corresponding character buffer \emph{must} be given; and when a C string is used for input, the size of the corresponding character buffer should not be given. If a function is declared to return a C string and the return value is NULL, \mwrap\ will pass back the scalar 0. \subsection{Objects} {\it Objects} are variables with any base type other than one of the recognized intrinsics (or the {\tt mxArray} pass-through -- see below). This can lead to somewhat startling results when, for example, \mwrap\ decides a {\tt size\_t} is a dynamic object type (this will only give surprises if one tries to pass in a numeric value). If a function or method returns an object, \mwrap\ will make a copy of the return object on the heap and pass back that copy. {\it Object references} are treated the same as objects, except that when a function returns an object reference, \mwrap\ will return the address associated with that reference, rather than making a new copy of the object. {\it Object pointers} may either point to a valid object of the appropriate type or to NULL (represented by zero). This is different from the treatment of objects and object references. When a NULL value is specified for a {\tt this} argument, an object argument, or an object reference argument, \mwrap\ will generate a MATLAB error message. If the wrapped code uses an object hierarchy, you can use \mwrap\ class declarations so that valid casts to parent types will be performed automatically. For example, the declaration \begin{verbatim} # class Child : Parent1, Parent2; \end{verbatim} tells \mwrap\ that an object of type {\tt Child} may be passed in where an object of type {\tt Parent1} is required. The generated code works correctly with C++ multiple inheritance. Objects cannot be declared as output or inout parameters, but that just means that the identity of an object parameter does not change during a call. There's nothing wrong with changing the internal state of the object. By default, \mwrap\ stores non-NULL object references in strings. However, for MATLAB 2008a and onward, \mwrap\ will also interpret as objects any classes with a readable property {\tt mwptr}. This can be used, for example, to implement class wrappers using the new {\tt classdef} keyword. In order to use this feature, the macro {\tt R2008OO} must be defined by adding the argument {\tt -DR2008OO} to the {\tt mex} compile line. \subsection{{\tt mxArray}} The {\it mxArray} type in \mwrap\ refers to MATLAB's basic object type (also called {\tt mxArray}). {\tt mxArray} arguments can be used as input or output arguments (but not as inout arguments), or as return values. On input, {\tt mxArray} is mapped to C type {\tt const mxArray*}; on output, it is mapped to {\tt mxArray**}; and on return, it is mapped to {\tt mxArray*}. For example, the line \begin{verbatim} # mxArray do_something(mxArray in_arg, output mxArray out_arg); \end{verbatim} is compatible with a function defined as \begin{verbatim} mxArray* do_something(const mxArray* in_arg, mxArray** out_arg); \end{verbatim} Note that the header file {\tt mex.h} must be included for this function declaration to make any sense. The primary purpose for the mxArray pass through is to allow specialized routines to read the internal storage of MATLAB sparse matrices (and possibly other structures) for a few routines without giving up the convenience of the \mwrap\ framework elsewhere. \subsection{Auto-converted objects} If there is a natural mapping from some MATLAB data structure to a C/C++ object type, you can use a typedef to tell \mwrap\ to perform automatic conversion. For example, if we wanted {\tt Foo} to be automatically converted from some MATLAB data structure on input, then we would add the line \begin{verbatim} # typedef mxArray Foo; \end{verbatim} With this declaration, {\tt Foo} objects are automatically converted from {\tt mxArray} to the corresponding C++ type on input, and back to {\tt mxArray} objects on output. It is assumed that \mwrap\ {\em owns the argument objects} and {\em does not own the return objects}. This feature should not be used when the C++ side keeps a pointer or reference to a passed object, or when the caller is responsible for deleting a dynamically allocated return object. Auto-converted objects rely on the following user-defined functions: \begin{verbatim} Foo* mxWrapGet_Foo(const mxArray* a, const char** e); mxArray* mxWrapSet_Foo(Foo* o); Foo* mxWrapAlloc_Foo(); void mxWrapFree_Foo(Foo* o); \end{verbatim} Not all functions are needed for all uses of an auto-converted type. The functions play the following roles: \begin{enumerate} \item The \verb|mxWrapGet_Foo| function is used to convert an input argument to the corresponding C++ representation. If an error occurs during conversion, the second argument should be pointed toward an error message string. It is assumed that this conversion function will catch any thrown exceptions. \item The \verb|mxWrapSet_Foo| function is used to convert an output argument or return value to the corresponding C++ representation. \item The \verb|mxWrapAlloc_Foo| function is used to allocate a new temporary for use as an output argument. \item The \verb|mxWrapFree_Foo| function is used to deallocate a temporary created by \verb|mxWrapGet_Foo| or \verb|mxWrapAlloc_Foo|. \end{enumerate} The point of auto-converted objects is to simplify wrapper design for codes that make heavy use of things like C++ vector classes (for example). The system does {\em not} provide the same flexibility as the {\tt mxArray} object, nor is it as flexible as a sequence of \mwrap\ calls to explicitly create and manage temporaries and their conversion to and from MATLAB objects. At present, the behavior when you try to involve an auto-converted object in an inheritance relation is undefined. Don't try it at home. \subsection{Constants} The {\it const} type in \mwrap\ refers to a C/C++ symbolic constant or global variable. The name of the variable is output directly into the compiled code. For example, to print a string to {\tt stderr}, we can write \begin{verbatim} # fprintf(const stderr, cstring s); \end{verbatim} \subsection{{\tt mxSINGLE\_CLASS} and {\tt mxDOUBLE\_CLASS}} By default, {\tt mwrap 0.33} expected all input and output numeric MATLAB variables to be of {\tt mxDOUBLE\_CLASS}. The newest version of {\tt mwrap (0.33.12+)} allows {\tt mxSINGLE\_CLASS} for {\tt float} and {\tt fcomplex} types. An error {\tt 'Invalid array argument, mxSINGLE/DOUBLE\_CLASS expected'} will be issued if a mismatched Matlab variable is detected during runtime. The user is expected to perform the required type conversions manually using {\tt single} or {\tt double} MATLAB commands. \subsection{{\tt mxCHAR\_CLASS}} {\tt mwrap (1.2+)} allows character constants of {\tt mxCHAR\_CLASS} for {\tt char} types and forces type checking. An error {\tt 'Invalid array argument, mxCHAR\_CLASS expected'} will be issued if a mismatched Matlab variable is detected during runtime. The user is expected to perform the required type conversions manually using {\tt char} MATLAB command. \subsection{GPU array passing} \label{gpu-section} When the {\tt -gpu} flag is specified, \mwrap\ adds support for MATLAB {\tt gpuArray} objects. The {\tt gpu} device specifier can be placed before the usual {\tt input}, {\tt output}, or {\tt inout} keywords to indicate that an array argument resides on the GPU: \begin{verbatim} # func(gpu input double[n] x, gpu output double[n] y, int n); \end{verbatim} For GPU input arrays, \mwrap\ verifies that the argument is a {\tt gpuArray}, then obtains a read-only device pointer via {\tt mxGPUGetDataReadOnly}. For GPU output arrays, \mwrap\ creates a new {\tt mxGPUArray} with {\tt mxGPUCreateGPUArray} and obtains a writable device pointer via {\tt mxGPUGetData}. For GPU inout arrays, the input device pointer is reused and the same {\tt mxGPUArray} is returned as output. The {\tt gpu} specifier may be freely combined with ordinary (CPU) arguments in the same function call, allowing mixed host/device interfaces. The {\tt -gpu} flag causes \mwrap\ to emit {\tt \#include } and a call to {\tt mxInitGPU()} in the MEX initialization code. The MEX file must be compiled with MATLAB's GPU-enabled {\tt mex} compiler ({\tt nvmex} or {\tt mex} with CUDA support). The default device specifier is {\tt cpu}, which can be omitted. \section{Example} An event queue stores pairs $(i, t)$ pairs, $i$ is an identifier for some event and $t$ is a time associated with the event. Events can be added to the queue in whatever order, but they are removed in increasing order by time. In this example, we bind to a C++ event queue implementation based on the C++ standard template library priority queue. The example code is in {\tt example/eventq/eventq\_class.mw} and {\tt example/eventq/eventq\_handle.mw}; an alternate version of the code in {\tt example/eventq/eventq\_plain.mw} illustrates a different way of organizing the same interface. The {\tt example/eventq2} subdirectory provides yet another implementation, this one capable of storing arbitrary MATLAB arrays rather than just integers. \subsection{Event queue using old MATLAB OO} We begin by defining an event as a pair (double, int), and an event queue as an STL priority queue of such pairs, sorted in descending order: \begin{verbatim} $ #include $ $ typedef std::pair Event; $ typedef std::priority_queue< Event, $ std::vector, $ std::greater > EventQueue; \end{verbatim} Now we specify the code to wrap the individual methods. For this example, we will take advantage of the object oriented features in MATLAB, and map the methods of the C++ event queue class onto methods of a MATLAB wrapper class called {\tt eventq}. We begin with bindings for the constructor and destructor. We will compile the MATLAB functions for the interface using the {\tt -mb} flag, so that we can specify these functions (and all the others) in the same file: \begin{verbatim} @ @eventq/eventq.m ------------------------------------- function [qobj] = eventq(); qobj = []; # EventQueue* q = new EventQueue(); qobj.q = q; qobj = class(qobj, 'eventq'); @ @eventq/destroy.m ------------------------------------- function destroy(qobj); q = qobj.q; # delete(EventQueue* q); \end{verbatim} The {\tt empty} method returns a {\tt bool}, but \mwrap\ does not know about {\tt bool} variables. A {\tt bool} result can be saved as an integer, though, so we will simply do that: \begin{verbatim} @ @eventq/empty.m ------------------------------------- function [e] = empty(qobj) q = qobj.q; # int e = q->EventQueue.empty(); \end{verbatim} Because {\tt pop\_event} needs to return two values (the event identifier and the time), we use reference arguments to pass out the information. \begin{verbatim} @ @eventq/pop_event.m ------------------------------------- function [id, t] = pop_event(qobj) $ void pop_event(EventQueue* q, int& id, double& t) { $ t = q->top().first; $ id = q->top().second; $ q->pop(); $ } $ q = qobj.q; # pop_event(EventQueue* q, output int& id, output double& t); \end{verbatim} In MATLAB, it may make sense to simultaneously push several events. However, our event queue class only provides basic interfaces to push one event at a time. We could write a MATLAB loop to add events to the queue one at a time, but for illustrating how to use \mwrap, it is better to write the loop in C: \begin{verbatim} @ @eventq/push_event.m ------------------------------------- function push_event(qobj, id, t) $ void push_events(EventQueue* q, int* id, double* t, int m) $ { $ for (int i = 0; i < m; ++i) $ q->push(Event(t[i], id[i])); $ } $ q = qobj.q; m = length(id); # push_events(EventQueue* q, int[m] id, double[m] t, int m); \end{verbatim} \subsection{Event queue using new MATLAB OO} Starting with MATLAB 7.6 (release 2008A), MATLAB supports a new single-file OO programming style. Particularly convenient for writing wrappers is the {\em handle} class system, which allows the user to define destructors that are called automatically when an instance is destroyed by the system (because all references to the instance have gone out of scope). As a programming convenience, \mwrap\ automatically interprets a class with the property {\tt mwptr} as a container for an \mwrap\ object\footnote{This functionality is only enabled when {\tt -DR2008OO} is specified as an argument on the MEX command line. This restriction is in place so that the files generated by \mwrap\ can remain compatible with Octave and with older versions of MATLAB.}. For example, the following file provides an alternate implementation of the event queue class described in the previous section. \begin{verbatim} $ #include $ $ typedef std::pair Event; $ typedef std::priority_queue< Event, $ std::vector, $ std::greater > EventQueue; @ eventqh.m ---------------------------------------------- classdef eventqh < handle properties mwptr end methods function [qobj] = eventqh(obj) # EventQueue* q = new EventQueue(); qobj.mwptr = q; end function delete(q) #delete(EventQueue* q); end function e = empty(q) # int e = q->EventQueue.empty(); end function [id, t] = pop(q) $ void pop_event(EventQueue* q, int& id, double& t) { $ t = q->top().first; $ id = q->top().second; $ q->pop(); $ } # pop_event(EventQueue* q, output int& id, output double& t); end function push(q, id, t) $ void push_events(EventQueue* q, int* id, double* t, int m) $ { $ for (int i = 0; i < m; ++i) $ q->push(Event(t[i], id[i])); $ } m = length(id); # push_events(EventQueue* q, int[m] id, double[m] t, int m); end end end \end{verbatim} This implementation of the event queue class allows for automatic cleanup: \begin{verbatim} q = eventqh(); % Construct a new queue clear q; % The C++ object gets correctly deleted here \end{verbatim} {\bf Warning}: When using MATLAB handle classes for automatic cleanup, be sure to avoid situations where multiple MATLAB handle objects have been given responsible for deleting a single C/C++ object. If you need to have more than one MATLAB handle for a single C/C++ object, I recommend using a reference counted pointer class as an intermediate\footnote{ For more information on reference counted pointer classes, I recommend reading {\em More Effective C++} by Scott Meyers. }. \section{FORTRAN bindings} It is possible to use \mwrap\ to bind FORTRAN functions (though the generated MEX file is still a C/C++ file). FORTRAN bindings can be specified using the {\tt FORTRAN} keyword immediately before a function name; for example: \begin{verbatim} # double sum = FORTRAN dasum(int n, double[n] x, int 1); \end{verbatim} FORTRAN parameters are treated differently from C parameters in the following ways: \begin{enumerate} \item \mwrap\ does not allow objects to be passed into FORTRAN functions. \item Scalar and reference arguments are automatically converted to pointer arguments from the C side to match FORTRAN call-by-reference semantics. \item A warning is generated when passing C strings into FORTRAN. The generated code will work with compilers that produce f2c-compatible code (including g77/g95), but it will not work with all FORTRAN compilers. \item Only simple numeric values can be returned from FORTRAN. A warning is generated when returning complex values, as different FORTRAN compilers follow different protocols when returning complex numbers. The generated code for complex return types will work with some f2c-compatible compilers, but by no means all. \end{enumerate} Internally, \mwrap\ defines macros for different FORTRAN name-mangling conventions, and it declares appropriate prototypes (and protects them from C++ compiler name mangling). By default, \mwrap\ assumes that the f2c name mangling convention is being used (this convention is followed by Sun FORTRAN, g77, and g95); however, the following flags can be passed to the {\tt mex} script to change this behavior: \begin{itemize} \item {\tt -DMWF77\_CAPS} -- Assume the FORTRAN compiler uses all-caps names and no extra underscores. Used by Compaq FORTRAN, and Intel fortran compiler on windows (I think). \item {\tt -DMWF77\_UNDERSCORE1} -- Append a single underscore to an all-lower-case name. Used by the GNU FORTRAN compiler and the Intel fortran compiler on UNIX systems (I think). \item {\tt -DMWF77\_UNDERSCORE0} -- Append no underscore to an all-lower-case name. Used by the GNU fortran with the -fno-underscoring flag \end{itemize} It is possible to use the {\tt typedef numeric} construct to introduce new types corresponding to FORTRAN data types. For example, if the header file {\tt f2c.h} is available (and the types defined therein are appropriate for the compiler) we might have \begin{verbatim} % Use the f2c integer type... $ #include "f2c.h" # typedef numeric integer; # double sum = FORTRAN dasum(integer n, double[n] x, integer 1); \end{verbatim} No attempt is made to automatically produce these type maps, though. \section{Logging} For profiling and coverage testing, it is sometimes useful to track the number of calls that are made through \mwrap. If {\tt mymex} is the name of the generated MEX file, then we can access profile information as follows: \begin{verbatim} % Enable profiler mymex('*profile on*'); % Run some tests here... % Print to screen and to a log file mymex('*profile report*'); mymex('*profile log*', 'log.txt'); % Disable profiler mymex('*profile off*'); \end{verbatim} The MEX file will be locked in memory as long as profiling is active. \end{document} zgimbutas-mwrap-04cdb66/example/000077500000000000000000000000001522673526200167225ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/CMakeLists.txt000066400000000000000000000255511522673526200214720ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) include(${CMAKE_SOURCE_DIR}/cmake/MwrapAddMex.cmake) add_custom_target(mwrap_examples) set(example_source_dir "${CMAKE_CURRENT_SOURCE_DIR}") set(_mwrap_example_targets foobar eventq_plain eventq_handle eventq_class eventq2 zlib fem_interface ) set(fem_interface_support_sources "${example_source_dir}/fem/src/assembler.cc" "${example_source_dir}/fem/src/gauss2by2.cc" "${example_source_dir}/fem/src/mesh.cc" "${example_source_dir}/fem/src/quad2d1.cc" "${example_source_dir}/fem/src/scalar1d.cc" "${example_source_dir}/fem/src/scalar2d.cc" "${example_source_dir}/fem/src/elastic2d1.cc" ) mwrap_add_mex(foobar MEX_NAME fbmex CC_FILENAME fbmex.cc M_FILENAME foobar.m MW_FILES "${example_source_dir}/foobar/foobar.mw" ) mwrap_add_mex(eventq_plain MEX_NAME eventqpmex CC_FILENAME eventqpmex.cc MW_FILES "${example_source_dir}/eventq/eventq_plain.mw" MWRAP_FLAGS -mb ) mwrap_add_mex(eventq_handle MEX_NAME eventqhmex CC_FILENAME eventqhmex.cc MW_FILES "${example_source_dir}/eventq/eventq_handle.mw" MWRAP_FLAGS -mb COMPILE_DEFINITIONS R2008OO ) mwrap_add_mex(eventq_class MEX_NAME eventqcmex CC_FILENAME eventqcmex.cc MW_FILES "${example_source_dir}/eventq/eventq_class.mw" MWRAP_FLAGS -mb CLASSDEF_NAME eventq ) mwrap_add_mex(eventq2 MEX_NAME eventq2mex CC_FILENAME eventq2mex.cc MW_FILES "${example_source_dir}/eventq2/eventq2.mw" MWRAP_FLAGS -mb ) mwrap_add_mex(zlib MEX_NAME gzmex CC_FILENAME gzmex.cc MW_FILES "${example_source_dir}/zlib/gzfile.mw" MWRAP_FLAGS -mb ) mwrap_add_mex(fem_interface MEX_NAME femex CC_FILENAME femex.cc MW_FILES "${example_source_dir}/fem/interface/assembler.mw" "${example_source_dir}/fem/interface/mesh.mw" "${example_source_dir}/fem/interface/elements.mw" MWRAP_FLAGS -mb EXTRA_SOURCES ${fem_interface_support_sources} INCLUDE_DIRECTORIES "${example_source_dir}/fem/src" ) foreach(example_target IN LISTS _mwrap_example_targets) add_dependencies(mwrap_examples ${example_target}) endforeach() if(MWRAP_COMPILE_MEX AND MWRAP_MEX_BACKENDS) foreach(example_target IN LISTS _mwrap_example_targets) _mwrap_compile_mex(${example_target} OUTPUT_VAR _mwrap_mex_targets) get_target_property(_mex_output_dir ${example_target} MWRAP_OUTPUT_DIRECTORY) get_target_property(_mex_target_list ${example_target} MWRAP_OUTPUT_MEX_TARGETS) get_target_property(_mex_path_list ${example_target} MWRAP_OUTPUT_MEX_PATHS) if(NOT _mex_target_list OR _mex_target_list STREQUAL "NOTFOUND") continue() endif() if(NOT _mex_path_list OR _mex_path_list STREQUAL "NOTFOUND") set(_mex_path_list) endif() list(LENGTH _mex_target_list _mex_target_count) list(LENGTH _mex_path_list _mex_path_count) if(NOT _mex_target_count EQUAL _mex_path_count) message(FATAL_ERROR "Mismatch between recorded MEX targets and paths for ${example_target}") endif() math(EXPR _mex_index "0") foreach(_mex_target IN LISTS _mex_target_list) list(GET _mex_path_list ${_mex_index} _mex_path) add_dependencies(mwrap_examples ${_mex_target}) if(_mex_output_dir AND NOT _mex_output_dir STREQUAL "NOTFOUND") set(_mex_should_copy TRUE) if(_mex_path MATCHES "\\$") set(_mex_dest_path "${_mex_output_dir}/${_mex_dest_name}") else() set(_mex_source_path "${_mex_path}") if(NOT IS_ABSOLUTE "${_mex_source_path}") get_filename_component(_mex_source_path "${_mex_source_path}" ABSOLUTE) endif() get_filename_component(_mex_dest_name "${_mex_path}" NAME) set(_mex_dest_path "${_mex_output_dir}/${_mex_dest_name}") if(NOT IS_ABSOLUTE "${_mex_dest_path}") get_filename_component(_mex_dest_path "${_mex_dest_path}" ABSOLUTE) endif() if(_mex_source_path STREQUAL _mex_dest_path) set(_mex_should_copy FALSE) endif() endif() if(_mex_should_copy) add_custom_command(TARGET ${_mex_target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${_mex_output_dir}" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_mex_source_path}" "${_mex_dest_path}" ) endif() endif() math(EXPR _mex_index "${_mex_index} + 1") endforeach() endforeach() endif() if(BUILD_TESTING AND MWRAP_COMPILE_MEX) if(MWRAP_MEX_BACKENDS) list(FIND MWRAP_MEX_BACKENDS "OCTAVE" _mwrap_octave_index) list(FIND MWRAP_MEX_BACKENDS "MATLAB" _mwrap_matlab_index) else() set(_mwrap_octave_index -1) set(_mwrap_matlab_index -1) endif() function(_mwrap_prepare_smoke_test_runner target_name runner_var workdir_var script_basename_var runner_escape_var) set(options) set(oneValueArgs SCRIPT WORKSPACE) set(multiValueArgs EXTRA_PATHS) cmake_parse_arguments(MTEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT TARGET ${target_name}) message(FATAL_ERROR "Unknown target '${target_name}' requested for smoke test") endif() if(NOT MTEST_SCRIPT) message(FATAL_ERROR "SCRIPT must be provided for smoke test on target '${target_name}'") endif() get_target_property(wrapper_dir ${target_name} MWRAP_OUTPUT_DIRECTORY) if(NOT wrapper_dir OR wrapper_dir STREQUAL "NOTFOUND") message(FATAL_ERROR "Target '${target_name}' is missing wrapper directory metadata") endif() get_target_property(classdef_dir ${target_name} MWRAP_OUTPUT_CLASSDEF_DIR) if(NOT classdef_dir OR classdef_dir STREQUAL "NOTFOUND") unset(classdef_dir) endif() set(test_paths ${MTEST_EXTRA_PATHS} "${wrapper_dir}") if(classdef_dir) list(APPEND test_paths "${classdef_dir}") endif() list(REMOVE_DUPLICATES test_paths) set(path_commands "") foreach(test_path IN LISTS test_paths) if(test_path) file(TO_CMAKE_PATH "${test_path}" test_path_cmake) string(REPLACE "'" "''" test_path_escaped "${test_path_cmake}") string(APPEND path_commands "addpath('${test_path_escaped}');\n") endif() endforeach() if(path_commands STREQUAL "") set(path_commands "% No additional paths required\n") endif() if(MTEST_WORKSPACE) set(work_dir "${MTEST_WORKSPACE}") file(MAKE_DIRECTORY "${work_dir}") file(TO_CMAKE_PATH "${work_dir}" work_dir_cmake) string(REPLACE "'" "''" work_dir_escaped "${work_dir_cmake}") set(work_dir_command "cd('${work_dir_escaped}');\n") else() set(work_dir "${CMAKE_CURRENT_BINARY_DIR}") set(work_dir_command "% No staging directory requested\n") file(TO_CMAKE_PATH "${work_dir}" work_dir_cmake) endif() get_filename_component(script_basename "${MTEST_SCRIPT}" NAME_WE) set(runner_file "${CMAKE_CURRENT_BINARY_DIR}/${script_basename}_runner.m") file(TO_CMAKE_PATH "${MTEST_SCRIPT}" script_path_cmake) string(REPLACE "'" "''" script_path_escaped "${script_path_cmake}") set(PATHS "${path_commands}") set(WORK_DIR "${work_dir_command}") set(CALL "${script_path_escaped}") configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/run_example.m.in" "${runner_file}" @ONLY ) unset(PATHS) unset(WORK_DIR) unset(CALL) file(TO_CMAKE_PATH "${runner_file}" runner_path_cmake) string(REPLACE "'" "''" runner_path_escaped "${runner_path_cmake}") set(${runner_var} "${runner_file}" PARENT_SCOPE) set(${workdir_var} "${work_dir}" PARENT_SCOPE) set(${script_basename_var} "${script_basename}" PARENT_SCOPE) set(${runner_escape_var} "${runner_path_escaped}" PARENT_SCOPE) endfunction() set(_mwrap_enable_octave_tests FALSE) if(_mwrap_octave_index GREATER -1 AND DEFINED MWRAP_OCTAVE_EXECUTABLE AND MWRAP_OCTAVE_EXECUTABLE) set(_mwrap_enable_octave_tests TRUE) function(_mwrap_add_octave_smoke_test target_name) _mwrap_prepare_smoke_test_runner(${target_name} runner_file work_dir script_basename runner_path_escaped ${ARGN}) set(test_name "octave-${script_basename}") add_test( NAME ${test_name} COMMAND ${MWRAP_OCTAVE_EXECUTABLE} --no-gui --quiet --eval "run('${runner_path_escaped}')" WORKING_DIRECTORY "${work_dir}" ) endfunction() endif() set(_mwrap_enable_matlab_tests FALSE) if(_mwrap_matlab_index GREATER -1 AND DEFINED Matlab_MAIN_PROGRAM AND Matlab_MAIN_PROGRAM) set(_mwrap_enable_matlab_tests TRUE) function(_mwrap_add_matlab_smoke_test target_name) _mwrap_prepare_smoke_test_runner(${target_name} runner_file work_dir script_basename runner_path_escaped ${ARGN}) set(test_name "matlab-${script_basename}") add_test( NAME ${test_name} COMMAND "${Matlab_MAIN_PROGRAM}" -batch "run('${runner_path_escaped}')" WORKING_DIRECTORY "${work_dir}" ) endfunction() endif() if(_mwrap_enable_octave_tests OR _mwrap_enable_matlab_tests) set(fem_workspace "${CMAKE_CURRENT_BINARY_DIR}/fem_workspace") file(MAKE_DIRECTORY "${fem_workspace}/mwfem") macro(_mwrap_register_example_smoke_tests add_func) cmake_language(CALL "${add_func}" eventq_plain SCRIPT "${example_source_dir}/eventq/testq_plain.m" EXTRA_PATHS "${example_source_dir}/eventq" ) cmake_language(CALL "${add_func}" eventq_handle SCRIPT "${example_source_dir}/eventq/testq_handle.m" EXTRA_PATHS "${example_source_dir}/eventq" ) cmake_language(CALL "${add_func}" eventq_class SCRIPT "${example_source_dir}/eventq/testq_class.m" EXTRA_PATHS "${example_source_dir}/eventq" ) cmake_language(CALL "${add_func}" eventq2 SCRIPT "${example_source_dir}/eventq2/testq2.m" EXTRA_PATHS "${example_source_dir}/eventq2" ) cmake_language(CALL "${add_func}" zlib SCRIPT "${example_source_dir}/zlib/testgz.m" EXTRA_PATHS "${example_source_dir}/zlib" ) cmake_language(CALL "${add_func}" fem_interface SCRIPT "${example_source_dir}/fem/test_simple.m" WORKSPACE "${fem_workspace}" EXTRA_PATHS "${example_source_dir}/fem" "${fem_workspace}/mwfem" ) cmake_language(CALL "${add_func}" fem_interface SCRIPT "${example_source_dir}/fem/test_patch.m" WORKSPACE "${fem_workspace}" EXTRA_PATHS "${example_source_dir}/fem" "${fem_workspace}/mwfem" ) cmake_language(CALL "${add_func}" fem_interface SCRIPT "${example_source_dir}/fem/test_assembler.m" WORKSPACE "${fem_workspace}" EXTRA_PATHS "${example_source_dir}/fem" "${fem_workspace}/mwfem" ) endmacro() endif() if(_mwrap_enable_octave_tests) _mwrap_register_example_smoke_tests(_mwrap_add_octave_smoke_test) endif() if(_mwrap_enable_matlab_tests) _mwrap_register_example_smoke_tests(_mwrap_add_matlab_smoke_test) endif() endif() zgimbutas-mwrap-04cdb66/example/Makefile000066400000000000000000000003411522673526200203600ustar00rootroot00000000000000all: (cd foobar; make) (cd eventq; make) (cd eventq2; make) (cd zlib; make) (cd fem; make) clean: (cd foobar; make clean) (cd eventq; make clean) (cd eventq2; make clean) (cd zlib; make clean) (cd fem; make clean) zgimbutas-mwrap-04cdb66/example/eventq/000077500000000000000000000000001522673526200202245ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/eventq/Makefile000066400000000000000000000007061522673526200216670ustar00rootroot00000000000000include ../../make.inc MW=../../mwrap all: pmex cmex hmex pmex: $(MW) -mex eventqpmex -c eventqpmex.cc -mb eventq_plain.mw $(MEX) eventqpmex.cc hmex: $(MW) -mex eventqhmex -c eventqhmex.cc -mb eventq_handle.mw $(MEX) eventqhmex.cc cmex: mkdir -p \@eventq $(MW) -mex eventqcmex -c eventqcmex.cc -mb eventq_class.mw $(MEX) eventqcmex.cc clean: rm -rf \@eventq/ rm -f EventQ_*.m eventqh.m rm -f eventqcmex.* eventqpmex.* eventqhmex.* *.o *~ zgimbutas-mwrap-04cdb66/example/eventq/README000066400000000000000000000013621522673526200211060ustar00rootroot00000000000000Example of bindings to encapsulate a C++ class. In this case, we bind the methods for an event queue of (id, time) pairs based on the STL priority queue class. This directory contains two different versions of the example. The "plain" version uses the prefix "EventQ_" to disambiguate references to class methods. For example, "EventQ_empty" would refer to the "empty" method of the event queue class. The "class" version uses the object-oriented features in MATLAB to wrap the C++ class in a MATLAB class, so that the "empty" method in C++ can be mapped to the "empty" method in MATLAB without fear of conflict with any other methods of the same name. Because Octave does not support MATLAB-style OO, this latter version does not work with Octave. zgimbutas-mwrap-04cdb66/example/eventq/eventq_class.mw000066400000000000000000000027561522673526200232720ustar00rootroot00000000000000% eventq_class.mw % Simple event queue for use in MATLAB event-driven simulations. % Uses the MATLAB OO system for encapsulating the event queue. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $ #include $ $ typedef std::pair Event; $ typedef std::priority_queue< Event, $ std::vector, $ std::greater > EventQueue; @ @eventq/eventq.m ------------------------------------- function [qobj] = eventq(); qobj = []; # EventQueue* q = new EventQueue(); qobj.q = q; qobj = class(qobj, 'eventq'); @ @eventq/destroy.m ------------------------------------- function destroy(qobj); q = qobj.q; # delete(EventQueue* q); @ @eventq/empty.m ------------------------------------- function [e] = empty(qobj) q = qobj.q; # int e = q->EventQueue.empty(); @ @eventq/pop.m ------------------------------------- function [id, t] = pop(qobj) $ void pop_event(EventQueue* q, int& id, double& t) { $ t = q->top().first; $ id = q->top().second; $ q->pop(); $ } $ q = qobj.q; # pop_event(EventQueue* q, output int& id, output double& t); @ @eventq/push.m ------------------------------------- function push(qobj, id, t) $ void push_events(EventQueue* q, int* id, double* t, int m) $ { $ for (int i = 0; i < m; ++i) $ q->push(Event(t[i], id[i])); $ } $ q = qobj.q; m = length(id); # push_events(EventQueue* q, int[m] id, double[m] t, int m); zgimbutas-mwrap-04cdb66/example/eventq/eventq_handle.mw000066400000000000000000000026201522673526200234060ustar00rootroot00000000000000% eventq_handle.mw % Simple event queue for use in MATLAB event-driven simulations. % Uses the MATLAB 2008+ OO system for encapsulating the event queue. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $ #include $ $ typedef std::pair Event; $ typedef std::priority_queue< Event, $ std::vector, $ std::greater > EventQueue; @ eventqh.m -------------------------------------------- classdef eventqh < handle properties mwptr end methods function [qobj] = eventqh() # EventQueue* q = new EventQueue(); qobj.mwptr = q; end function delete(q) #delete(EventQueue* q); end function e = empty(q) # int e = q->EventQueue.empty(); end function [id, t] = pop(q) $ void pop_event(EventQueue* q, int& id, double& t) { $ t = q->top().first; $ id = q->top().second; $ q->pop(); $ } # pop_event(EventQueue* q, output int& id, output double& t); end function push(q, id, t) $ void push_events(EventQueue* q, int* id, double* t, int m) $ { $ for (int i = 0; i < m; ++i) $ q->push(Event(t[i], id[i])); $ } m = length(id); # push_events(EventQueue* q, int[m] id, double[m] t, int m); end end end zgimbutas-mwrap-04cdb66/example/eventq/eventq_plain.mw000066400000000000000000000020671522673526200232630ustar00rootroot00000000000000% eventq_plain.mw % Simple event queue for use in MATLAB event-driven simulations. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $[ #include typedef std::pair Event; typedef std::priority_queue< Event, std::vector, std::greater > EventQueue; $] @function [q] = EventQ_new(); # EventQueue* q = new EventQueue(); @function EventQ_destroy(q); # delete(EventQueue* q); @function [e] = EventQ_empty(q) # int e = q->EventQueue.empty(); @function [id, t] = EventQ_pop(q) $ void pop_event(EventQueue* q, int& id, double& t) { $ t = q->top().first; $ id = q->top().second; $ q->pop(); $ } $ # pop_event(EventQueue* q, output int& id, output double& t); @function EventQ_push(q, id, t) $ void push_events(EventQueue* q, int* id, double* t, int m) $ { $ for (int i = 0; i < m; ++i) $ q->push(Event(t[i], id[i])); $ } $ m = length(id); # push_events(EventQueue* q, int[m] id, double[m] t, int m); zgimbutas-mwrap-04cdb66/example/eventq/testq_class.m000066400000000000000000000006671522673526200227400ustar00rootroot00000000000000% testq_class.m % Test case / demo of MWrap bindings to a C++ event queue class. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions if ~exist('isobject') fprintf('MATLAB object system not supported\n'); return; end q = eventq(); push(q, 1, 1.5); push(q, 2, 0.4); push(q, 3, 10); push(q, [4, 5], [8, 11]); while ~empty(q) [id,t] = pop(q); fprintf('Time %g: Saw %d\n', t, id); end destroy(q); zgimbutas-mwrap-04cdb66/example/eventq/testq_handle.m000066400000000000000000000007111522673526200230540ustar00rootroot00000000000000% testq_class.m % Test case / demo of MWrap bindings to a C++ event queue class. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions if ~exist('ishandle') | ~exist('isobject') fprintf('MATLAB object system not supported\n'); return; end q = eventqh(); push(q, 1, 1.5); push(q, 2, 0.4); push(q, 3, 10); push(q, [4, 5], [8, 11]); while ~empty(q) [id,t] = pop(q); fprintf('Time %g: Saw %d\n', t, id); end clear q zgimbutas-mwrap-04cdb66/example/eventq/testq_plain.m000066400000000000000000000006241522673526200227270ustar00rootroot00000000000000% testq_plain.m % Test case / demo of MWrap bindings to a C++ event queue class. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions q = EventQ_new(); EventQ_push(q, 1, 1.5); EventQ_push(q, 2, 0.4); EventQ_push(q, 3, 10); EventQ_push(q, [4, 5], [8, 11]); while ~EventQ_empty(q) [id,t] = EventQ_pop(q); fprintf('Time %g: Saw %d\n', t, id); end EventQ_destroy(q); zgimbutas-mwrap-04cdb66/example/eventq2/000077500000000000000000000000001522673526200203065ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/eventq2/Makefile000066400000000000000000000002571522673526200217520ustar00rootroot00000000000000include ../../make.inc MW=../../mwrap mex: $(MW) -mex eventq2mex -c eventq2mex.cc -mb eventq2.mw $(MEX) eventq2mex.cc clean: rm -f EventQ_*.m rm -f eventq2mex.* *.o *~ zgimbutas-mwrap-04cdb66/example/eventq2/README000066400000000000000000000004631522673526200211710ustar00rootroot00000000000000Example of bindings using the mxArray wrapper. In this case, we wrap an STL priority queue of (mxArray, time) pairs. This is a variant of the event queue example in the example/eventq subdirectory, except in this version we can store arbitrary MATLAB data objects in the queue, rather than just integers. zgimbutas-mwrap-04cdb66/example/eventq2/eventq2.mw000066400000000000000000000023531522673526200222420ustar00rootroot00000000000000% eventq_plain.mw % Simple event queue for use in MATLAB event-driven simulations. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $[ #include #include typedef std::pair Event; typedef std::priority_queue< Event, std::vector, std::greater > EventQueue; $] // ---- @function [q] = EventQ_new(); # EventQueue* q = new EventQueue(); // ---- @function EventQ_destroy(q); while ~EventQ_empty(q) EventQ_pop(q) end # delete(EventQueue* q); // ---- @function [e] = EventQ_empty(q) # int e = q->EventQueue.empty(); // ---- @function [data, t] = EventQ_pop(q) $[ mxArray* pop_event(EventQueue* q, double& t) { t = q->top().first; mxArray* data = mxDuplicateArray(q->top().second); mxDestroyArray(q->top().second); q->pop(); return data; } $] # mxArray data = pop_event(EventQueue* q, output double& t); // ---- @function EventQ_push(q, data, t) $[ void push_event(EventQueue* q, const mxArray* a, double t) { mxArray* data = mxDuplicateArray(a); mexMakeArrayPersistent(data); q->push(Event(t, data)); } $] # push_event(EventQueue* q, mxArray data, double t); zgimbutas-mwrap-04cdb66/example/eventq2/testq2.m000066400000000000000000000006501522673526200217070ustar00rootroot00000000000000% testq_plain.m % Test case / demo of MWrap bindings to a C++ event queue class. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions q = EventQ_new(); EventQ_push(q, 1, 1.5); EventQ_push(q, 2, 0.4); EventQ_push(q, 3, 10); EventQ_push(q, 'Mary had a little lamb', 8); while ~EventQ_empty(q) [data,t] = EventQ_pop(q); fprintf('Time %g: Saw ', t); disp(data); end EventQ_destroy(q); zgimbutas-mwrap-04cdb66/example/fem/000077500000000000000000000000001522673526200174715ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/fem/Makefile000066400000000000000000000002571522673526200211350ustar00rootroot00000000000000all: mkdir -p mwfem (cd src; make) (cd interface; make) clean: (cd src; make clean) (cd interface; make clean) rm -rf mwfem realclean: clean (cd src; make realclean) zgimbutas-mwrap-04cdb66/example/fem/README000066400000000000000000000007071522673526200203550ustar00rootroot00000000000000This example illustrates several features of MWrap: - The binding to the finite element assembly class uses mxArrays directly (for sparse matrices). - The binding to element types uses inheritance. - The bindings to the mesh methods illustrate using zero-based to one-based construction. The finite element source files are in src; the MWrap input files are in interface. The MEX file and generated .m files build into the mwfem subdirectory. zgimbutas-mwrap-04cdb66/example/fem/init.m000066400000000000000000000000621522673526200206100ustar00rootroot00000000000000if ~exist('femex'), addpath([pwd, '/mwfem']); end zgimbutas-mwrap-04cdb66/example/fem/interface/000077500000000000000000000000001522673526200214315ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/fem/interface/Makefile000066400000000000000000000003301522673526200230650ustar00rootroot00000000000000include ../../../make.inc MW=../../../mwrap mex: wrap $(MEX) -I../src femex.cc ../src/*.o mv femex.* *.m ../mwfem wrap: $(MW) -mex femex -c femex.cc \ -mb assembler.mw mesh.mw elements.mw clean: rm -f *~ zgimbutas-mwrap-04cdb66/example/fem/interface/assembler.mw000066400000000000000000000041521522673526200237550ustar00rootroot00000000000000% assembler.mw % MWrap bindings for MatrixAssembler (CSC matrix assembly). % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $[ #include "assembler.h" #include #include mxArray* export_matrix(MatrixAssembler* matrix) { matrix->compress(); int m = matrix->get_m(); int n = matrix->get_n(); int nnz = matrix->csc_nnz(); mxArray* result = mxCreateSparse(m, n, nnz, mxREAL); std::copy(matrix->get_jc(), matrix->get_jc()+n+1, mxGetJc(result)); std::copy(matrix->get_ir(), matrix->get_ir()+nnz, mxGetIr(result)); std::copy(matrix->get_pr(), matrix->get_pr()+nnz, mxGetPr(result)); return result; } $] @function aobj = Assembler_create(m, n, nnz) % aobj = Assembler_create(m, n) % % Create an assembly object for m-by-n matrices. if nargin < 2, m = n; end if nargin < 3 # MatrixAssembler* aobj = new MatrixAssembler(int m, int n); else # MatrixAssembler* aobj = new MatrixAssembler(int m, int n, int nnz); end @function Assembler_delete(aobj) % Assembler_delete(aobj) % % Delete a matrix assembler object # delete(MatrixAssembler* aobj); @function Assembler_add(aobj, ii, jj, Aelt) % Assembler_create(aobj, ii, jj, Aelt) % % Perform A(ii,jj) += Aelt. [m,n] = size(Aelt); ii = ii-1; jj = jj-1; # aobj->MatrixAssembler.add_entry(int[m] ii, int[n] jj, double[m,n] Aelt, # int m, int n); @function K = Assembler_get(aobj) % K = Assembler_get(aobj) % % Retrieve a sparse matrix from a matrix assembler. # mxArray K = export_matrix(MatrixAssembler* aobj); @function Assembler_clear(aobj) % Assembler_clear(aobj) % % Clear the matrix assembler in preparation for building a new matrix. # aobj->MatrixAssembler.wipe(); @function [structure_nnz, cached_nnz] = Assembler_stats(aobj) % [structure_nnz, cached_nnz] = Assembler_stats(aobj) % % Get statistics about the nonzeros in the assembler. # int structure_nnz = aobj->MatrixAssembler.csc_nnz(); # int cached_nnz = aobj->MatrixAssembler.cache_nnz(); if nargout == 0 fprintf('NNZ in structure: %d\n', structure_nnz); fprintf('NNZ in cache: %d\n', cached_nnz); end zgimbutas-mwrap-04cdb66/example/fem/interface/elements.mw000066400000000000000000000020271522673526200236130ustar00rootroot00000000000000% elements.mw % MWrap bindings for element types. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $[ #include "mesh.h" #include "scalar1d.h" #include "scalar2d.h" #include "elastic2d.h" $] # class Scalar1D : EType; # class Scalar2D : EType; # class Elastic2D : EType; @function etype = Mesh_add_scalar1d(mobj, k) % material = Mesh_add_scalar1d(mobj, k) % % Add a simple scalar element type to the mesh object. # Scalar1D* etype = new Scalar1D(double k); # mobj->Mesh.add_material(EType* etype); @function etype = Mesh_add_scalar2d(mobj, k) % material = Mesh_add_scalar2d(mobj, k) % % Add a simple scalar element type to the mesh object. # Scalar2D* etype = new Scalar2D(double k); # mobj->Mesh.add_material(EType* etype); @function etype = Mesh_add_elastic2d(mobj, E, nu, which) % material = Mesh_add_elastic2d(mobj, E, nu, which) % % Add a plane strain elastic element type to the mesh. # Elastic2D* etype = new Elastic2D(double E, double nu, cstring which); # mobj->Mesh.add_material(EType* etype); zgimbutas-mwrap-04cdb66/example/fem/interface/mesh.mw000066400000000000000000000202511522673526200227320ustar00rootroot00000000000000% mesh.mw % MWrap bindings for Mesh. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $ #include "mesh.h" // ---- @function mobj = Mesh_create(ndm, maxndf, maxnen) % mobj = Mesh_create(ndm, maxndf, maxnen) % % Create a new mesh object. % ndm - Number of spatial dimensions % maxndf - Maximum number of dofs per node % maxnen - Maximum number of elements per node # Mesh* mobj = new Mesh(int ndm, int maxndf, int maxnen); // ---- @function Mesh_delete(mobj) % Mesh_delete(mobj) % % Delete an existing mesh object. # delete(Mesh* mobj); // ---- @function ndm = Mesh_ndm(mobj) % ndm = Mesh_ndm(mobj) % % Get the number of spatial dimensions for the mesh # int ndm = mobj->Mesh.ndm(); // ---- @function maxndf = Mesh_maxndf(mobj) % maxndf = Mesh_maxndf(mobj) % % Get the maximum number of nodal dofs for the mesh # int maxndf = mobj->Mesh.maxndf(); // ---- @function maxnen = Mesh_maxnen(mobj) % maxnen = Mesh_maxnen(mobj) % % Get the maximum number of nodes per element for the mesh # int maxnen = mobj->Mesh.maxnen(); // ---- @function numelt = Mesh_numelt(mobj) % numelt = Mesh_numelt(mobj) % % Get the number of elements in the mesh. # int numelt = mobj->Mesh.numelt(); // ---- @function numnp = Mesh_numnp(mobj) % numnp = Mesh_numnp(mobj) % % Get the number of nodes in the mesh # int numnp = mobj->Mesh.numnp(); // ---- @function numid = Mesh_numid(mobj) % numid = Mesh_numid(mobj) % % Get the number of active dofs in the mesh # int numid = mobj->Mesh.numid(); // ---- @function x = Mesh_x(mobj, i) % Mesh_x(mobj, i) % % Get the coordinates of mesh node i. If the node number is omitted, % get the coordinates for all nodes in the mesh. if nargin == 2 i = i-1; # int ndm = mobj->Mesh.ndm(); # double[ndm] x = mobj->Mesh.x(int i); else # int numnp = mobj->Mesh.numnp(); # int ndm = mobj->Mesh.ndm(); # double[ndm,numnp] x = mobj->Mesh.x(); end // ---- @function ix = Mesh_ix(mobj, i) % Mesh_ix(mobj, i) % % Get the connectivity of element i. If the element number is omitted, % get the connectivity for all elements in the mesh. if nargin == 2 i = i-1; # int maxnen = mobj->Mesh.maxndf(); # int[maxnen] ix = mobj->Mesh.ix(int i); else # int numelt = mobj->Mesh.numelt(); # int maxnen = mobj->Mesh.maxnen(); # int[maxnen,numelt] ix = mobj->Mesh.ix(); end ix = ix+1; // ---- @function id = Mesh_id(mobj, i) % Mesh_id(mobj, i) % % Get the degrees of freedom for element i. If the element number is omitted, % get the degrees of freedom for all elements in the mesh. if nargin == 2 i = i - 1; # int maxndf = mobj->Mesh.maxndf(); # int[maxndf] id = mobj->Mesh.id(int i); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # int[maxndf,numnp] id = mobj->Mesh.id(); end id = id + 1; // ---- @function u = Mesh_u(mobj, i) % Mesh_u(mobj, i) % % Get the displacement of mesh node i. If the node number is omitted, % get the displacements for all nodes in the mesh. if nargin == 2 i = i-1; # int maxndf = mobj->Mesh.maxndf(); # double[maxndf] u = mobj->Mesh.u(int i); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # double[maxndf,numnp] u = mobj->Mesh.u(); end // ---- @function f = Mesh_f(mobj, i) % Mesh_f(mobj, i) % % Get the forces for mesh node i. If the node number is omitted, % get the forces for all nodes in the mesh. if nargin == 2 i = i-1; # int maxndf = mobj->Mesh.maxndf(); # double[maxndf] f = mobj->Mesh.f(int i); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # double[maxndf,numnp] f = mobj->Mesh.f(); end // ---- @function bc = Mesh_bc(mobj, i) % Mesh_bc(mobj, i) % % Get the boundary codes of mesh node i. If the node number is omitted, % get the boundary codes for all nodes in the mesh. if nargin == 2 i = i-1; # int maxndf = mobj->Mesh.maxndf(); # char[maxndf] bc = mobj->Mesh.bc(int i); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # char[maxndf,numnp] bc = mobj->Mesh.bc(); end // ---- @function bv = Mesh_bv(mobj, i) % Mesh_bv(mobj, i) % % Get the boundary values of mesh node i. If the node number is omitted, % get the boundary values for all nodes in the mesh. if nargin == 2 i = i-1; # int maxndf = mobj->Mesh.maxndf(); # double[maxndf] bv = mobj->Mesh.bv(int i); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # double[maxndf,numnp] bv = mobj->Mesh.bv(); end // ---- @function Mesh_set_ur(mobj, ur) % Mesh_set_ur(mobj, ur) % % Set reduced displacement vector numid = Mesh_numid(mobj); # mobj->Mesh.set_ur(input double[numid] ur); // ---- @function ur = Mesh_get_ur(mobj) % ur = Mesh_get_ur(mobj) % % Get reduced displacement vector numid = Mesh_numid(mobj); # mobj->Mesh.get_ur(output double[numid] ur); // ---- @function Mesh_set_bc(mobj, bc, i, j) % Mesh_set_bc(mobj, bc, i, j) % % Set the boundary codes of mesh node i. If the node number is omitted, % set the boundary codes for all nodes in the mesh. $[ void set_bc(Mesh* mesh, char bc, int i, int j) { mesh->bc(i,j) = bc; } void set_bc(Mesh* mesh, char* bc) { int numnp = mesh->numnp(); int maxndf = mesh->maxndf(); for (int j = 0; j < numnp; ++j) for (int i = 0; i < maxndf; ++i) mesh->bc(i,j) = bc[i+j*maxndf]; } $] if nargin == 4 i = i-1; j = j-1; # int maxndf = mobj->Mesh.maxndf(); # set_bc(Mesh* mobj, char bc, int i, int j); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # set_bc(Mesh* mobj, char[maxndf,numnp] bc); end // ---- @function Mesh_set_bv(mobj, bv, i, j) % Mesh_set_bv(mobj, bv, i, j) % % Set the boundary codes of mesh node i. If the node number is omitted, % set the boundary codes for all nodes in the mesh. $[ void set_bv(Mesh* mesh, double bv, int i, int j) { mesh->bv(i,j) = bv; } void set_bv(Mesh* mesh, double* bv) { int numnp = mesh->numnp(); int maxndf = mesh->maxndf(); for (int j = 0; j < numnp; ++j) for (int i = 0; i < maxndf; ++i) mesh->bv(i,j) = bv[i+j*maxndf]; } $] if nargin == 4 i = i-1; j = j-1; # int maxndf = mobj->Mesh.maxndf(); # set_bv(Mesh* mobj, double bv, int i, int j); else # int numnp = mobj->Mesh.numnp(); # int maxndf = mobj->Mesh.maxndf(); # set_bv(Mesh* mobj, double[maxndf,numnp] bv); end // ---- @function numid = Mesh_initialize(mobj) % numid = Mesh_initialize(mobj) % % Initialize the mesh object after completion of X/IX arrays. # int numid = mobj->Mesh.initialize(); // ---- @function numid = Mesh_assign_ids(mobj) % numid = Mesh_assign_ids(mobj) % % Assign identifiers to active degrees of freedom in the mesh. # int numid = mobj->Mesh.assign_ids(); // ---- @function F = Mesh_assemble_F(mobj) % F = Mesh_assemble_F(mobj) % % Assemble system residual force vector. numid = Mesh_numid(mobj); # mobj->Mesh.assemble_F(); # mobj->Mesh.get_fr(output double[numid] F); // ---- @function K = Mesh_assemble_K(mobj) % K = Mesh_assemble_K(mobj) % % Assemble system stiffness matrix. numid = Mesh_numid(mobj); Ka = Assembler_create(numid, numid); # mobj->Mesh.assemble_K(MatrixAssembler* Ka); K = Assembler_get(Ka); Assembler_delete(Ka); // ---- @function id = Mesh_add_node(mobj, x) % id = Mesh_add_node(mobj, x) % % Build a new node at position x and return the index. # int ndm = mobj->Mesh.ndm(); # int id = mobj->Mesh.add_node(double[ndm] x); id = id + 1; // ---- @function id = Mesh_add_element(mobj, etype, nodes) % id = Mesh_add_element(mobj, etype, nodes) % % Build a new element of type etype connected to the indicated nodes, % and return the index. numnp = length(nodes); nodes = nodes-1; # int id = mobj->Mesh.add_element(EType* etype, int[numnp] nodes, int numnp); id = id + 1; // ---- @function Mesh_load(mobj, etype, x, ix) % Mesh_load(mobj, material, x, ix) % % Add a new batch of elements % material - Material type % x - Coordinates of nodes in new elements % ix - Connectivity array for new elements # int ndm = mobj->Mesh.ndm(); ids = zeros(1,size(x,2)); for j = 1:size(x,2) xj = x(:,j); # int id = mobj->Mesh.add_node(double[ndm] xj); ids(j) = id; end numnp = size(ix,1); for j = 1:size(ix,2) ixj = ids(ix(:,j)); # int id = mobj->Mesh.add_element(EType* etype, int[numnp] ixj, int numnp); end zgimbutas-mwrap-04cdb66/example/fem/src/000077500000000000000000000000001522673526200202605ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/fem/src/Makefile000066400000000000000000000006761522673526200217310ustar00rootroot00000000000000# Targets for object files (we compile with MEX to avoid headaches # about -fPIC and other flag compatibility issues) include ../../../make.inc OBJS= \ assembler.o mesh.o \ gauss2by2.o quad2d1.o \ scalar1d.o scalar2d.o elastic2d1.o all: $(OBJS) .cc.o: $(MEX) -c $*.cc elastic2d1.cc: matexpr elastic2d.cc > elastic2d1.cc quad2d1.cc: matexpr quad2d.cc > quad2d1.cc clean: rm -f *.o *~ realclean: rm -f *.o elastic2d1.cc quad2d1.cc zgimbutas-mwrap-04cdb66/example/fem/src/assembler.cc000066400000000000000000000060071522673526200225470ustar00rootroot00000000000000/* * assembler.cc * Compressed sparse column matrix assembler implementation. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "assembler.h" #include #include #define ME MatrixAssembler using std::sort; using std::fill; /* * Add A(i,j) += Aij. If there's space in the existing compressed * sparse column data structure, add it there; otherwise, stash it in * the coords cache vector. */ void ME::add_entry(int i, int j, double Aij) { if (i < 0 || i >= m || j < 0 || j >= n) return; for (int ii = jc[j]; ii < jc[j+1]; ++ii) { if (ir[ii] == i) { pr[ii] += Aij; return; } } coords.push_back(Coord(i,j,Aij)); } /* * Add an element submatrix. */ void ME::add_entry(const int* i, const int* j, double* Aij, int m_elt, int n_elt) { for (int jj = 0; jj < n_elt; ++jj) for (int ii = 0; ii < m_elt; ++ii) add_entry(i[ii], j[jj], Aij[jj*m_elt+ii]); } /* * Wipe the input matrix. Note that the compressed sparse column index * structure remains intact, so we can re-assemble without doing too much * work. */ void ME::wipe() { coords.resize(0); fill(get_pr(), get_pr()+jc[n], 0); } /* * Sort the entries in the coords cache, and merge (by summing) contributions * to the same position. */ void ME::pack_cache() { if (coords.size() > 0) { sort(coords.begin(), coords.end()); int i_merge = 0; for (int i_coord = 1; i_coord < coords.size(); ++i_coord) { if (coords[i_merge] < coords[i_coord]) coords[++i_merge] = coords[i_coord]; else coords[i_merge].Aij += coords[i_coord].Aij; } coords.resize(i_merge+1); } } /* * Pack the coordinate cache, then merge sort the contents of the compressed * sparse column data structure with the contents of the entry cache. */ void ME::compress() { pack_cache(); ir.resize(ir.size() + coords.size()); pr.resize(pr.size() + coords.size()); int i_coord = coords.size()-1; // Next input coord to process int i_csc = jc[n]-1; // Next input CSC entry to process int i_merge = pr.size()-1; // Next output CSC entry to process jc[n] = pr.size(); for (int j = n; j > 0; --j) { // Merge column from cache with column from previous CSC while (i_coord >= 0 && coords[i_coord].j == j-1 && i_csc >= 0 && i_csc >= jc[j-1]) { if (coords[i_coord].i > ir[i_csc]) { ir[i_merge] = coords[i_coord].i; pr[i_merge] = coords[i_coord].Aij; --i_coord; } else { ir[i_merge] = ir[i_csc]; pr[i_merge] = pr[i_csc]; --i_csc; } --i_merge; } // Copy stragglers from coord list while (i_coord >= 0 && coords[i_coord].j == j-1) { ir[i_merge] = coords[i_coord].i; pr[i_merge] = coords[i_coord].Aij; --i_coord; --i_merge; } // Copy stragglers from CSC list while (i_csc >= 0 && i_csc >= jc[j-1]) { ir[i_merge] = ir[i_csc]; pr[i_merge] = pr[i_csc]; --i_csc; --i_merge; } // Update the column count jc[j-1] = i_merge+1; } coords.resize(0); } zgimbutas-mwrap-04cdb66/example/fem/src/assembler.h000066400000000000000000000051071522673526200224110ustar00rootroot00000000000000/* * assembler.h * Compressed sparse column matrix assembler interface. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef ASSEMBLER_H #define ASSEMBLER_H #include using std::vector; /* * The MatrixAssembler class is responsible for assembling a finite element * matrix. In pure MATLAB, the assembly operation looks something like * * for i = 1:num_elements * [Ke, idx] = element_stiffness(i); * Ivalid = find(idx > 0 & idx < N); * idx = idx(Ivalid); * Ke = Ke(Ivalid,Ivalid); * K(idx,idx) = K(idx,idx) + Ke; * end * * The method add_entry is equivalent to most of the body of this loop -- * it removes out-of-range indices and accumulates the rest of the element * contribution. * * Elements in the matrix can be stored in one of two ways. First, * there are elements that go into the current compressed sparse * column matrix structure. Then there are elements that correspond * to indices that were not in the matrix the last time we built the * compressed sparse column structure. When it is time to assemble * the matrix, we use the compress method to take all these extra * elements and merge them into the compressed sparse column indexing * structure. This means that after K is assembled once, any we can * assemble other matrices with the same structure using a little less * time and memory. */ class MatrixAssembler { public: MatrixAssembler(int m, int n) : m(m), n(n), jc(n+1) {} MatrixAssembler(int m, int n, int coord_nnz) : m(m), n(n), jc(n+1), coords(coord_nnz) {} void add_entry(int i, int j, double Aij); void add_entry(const int* i, const int* j, double* Aij, int m_elt, int n_elt); void wipe(); void pack_cache(); void compress(); int get_m() { return m; } int get_n() { return n; } int* get_jc() { return &(jc[0]); } int* get_ir() { return &(ir[0]); } double* get_pr() { return &(pr[0]); } int cache_nnz() { return coords.size(); } int csc_nnz() { return pr.size(); } private: struct Coord { Coord() {} Coord(int i, int j, double Aij) : i(i), j(j), Aij(Aij) {} bool operator<(const Coord& coord) const { return ((j < coord.j) || (j == coord.j && i < coord.i)); } int i, j; double Aij; }; int m, n; // Things that fit in the compressed sparse representation vector jc; vector ir; vector pr; // Cache of entries that didn't fit in the compressed sparse form vector coords; }; #endif /* ASSEMBLER_H */ zgimbutas-mwrap-04cdb66/example/fem/src/assembler2.cc000066400000000000000000000060101522673526200226230ustar00rootroot00000000000000/* * assembler.cc * Compressed sparse column matrix assembler implementation. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "assembler.h" #include #include #define ME MatrixAssembler2 using std::sort; using std::fill; /* * Add A(i,j) += Aij. If there's space in the existing compressed * sparse column data structure, add it there; otherwise, stash it in * the coords cache vector. */ void ME::add_entry(int i, int j, double Aij) { if (i < 0 || i >= m || j < 0 || j >= n) return; for (int ii = jc[j]; ii < jc[j+1]; ++ii) { if (ir[ii] == i) { pr[ii] += Aij; return; } } coords.push_back(Coord(i,j,Aij)); } /* * Add an element submatrix. */ void ME::add_entry(const int* i, const int* j, double* Aij, int m_elt, int n_elt) { for (int jj = 0; jj < n_elt; ++jj) for (int ii = 0; ii < m_elt; ++ii) add_entry(i[ii], j[jj], Aij[jj*m_elt+ii]); } /* * Wipe the input matrix. Note that the compressed sparse column index * structure remains intact, so we can re-assemble without doing too much * work. */ void ME::wipe() { coords.resize(0); fill(get_pr(), get_pr()+jc[n], 0); } /* * Sort the entries in the coords cache, and merge (by summing) contributions * to the same position. */ void ME::pack_cache() { if (coords.size() > 0) { sort(coords.begin(), coords.end()); int i_merge = 0; for (int i_coord = 1; i_coord < coords.size(); ++i_coord) { if (coords[i_merge] < coords[i_coord]) coords[++i_merge] = coords[i_coord]; else coords[i_merge].Aij += coords[i_coord].Aij; } coords.resize(i_merge+1); } } /* * Pack the coordinate cache, then merge sort the contents of the compressed * sparse column data structure with the contents of the entry cache. */ void ME::compress() { pack_cache(); ir.resize(ir.size() + coords.size()); pr.resize(pr.size() + coords.size()); int i_coord = coords.size()-1; // Next input coord to process int i_csc = jc[n]-1; // Next input CSC entry to process int i_merge = pr.size()-1; // Next output CSC entry to process jc[n] = pr.size(); for (int j = n; j > 0; --j) { // Merge column from cache with column from previous CSC while (i_coord >= 0 && coords[i_coord].j == j-1 && i_csc >= 0 && i_csc >= jc[j-1]) { if (coords[i_coord].i > ir[i_csc]) { ir[i_merge] = coords[i_coord].i; pr[i_merge] = coords[i_coord].Aij; --i_coord; } else { ir[i_merge] = ir[i_csc]; pr[i_merge] = pr[i_csc]; --i_csc; } --i_merge; } // Copy stragglers from coord list while (i_coord >= 0 && coords[i_coord].j == j-1) { ir[i_merge] = coords[i_coord].i; pr[i_merge] = coords[i_coord].Aij; --i_coord; --i_merge; } // Copy stragglers from CSC list while (i_csc >= 0 && i_csc >= jc[j-1]) { ir[i_merge] = ir[i_csc]; pr[i_merge] = pr[i_csc]; --i_csc; --i_merge; } // Update the column count jc[j-1] = i_merge+1; } coords.resize(0); } zgimbutas-mwrap-04cdb66/example/fem/src/assembler2.h000066400000000000000000000051121522673526200224670ustar00rootroot00000000000000/* * assembler.h * Compressed sparse column matrix assembler interface. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef ASSEMBLER2_H #define ASSEMBLER2_H #include using std::vector; /* * The MatrixAssembler2 class is responsible for assembling a finite element * matrix. In pure MATLAB, the assembly operation looks something like * * for i = 1:num_elements * [Ke, idx] = element_stiffness(i); * Ivalid = find(idx > 0 & idx < N); * idx = idx(Ivalid); * Ke = Ke(Ivalid,Ivalid); * K(idx,idx) = K(idx,idx) + Ke; * end * * The method add_entry is equivalent to most of the body of this loop -- * it removes out-of-range indices and accumulates the rest of the element * contribution. * * Elements in the matrix can be stored in one of two ways. First, * there are elements that go into the current compressed sparse * column matrix structure. Then there are elements that correspond * to indices that were not in the matrix the last time we built the * compressed sparse column structure. When it is time to assemble * the matrix, we use the compress method to take all these extra * elements and merge them into the compressed sparse column indexing * structure. This means that after K is assembled once, we can * assemble other matrices with the same structure using a little less * time and memory. */ class MatrixAssembler2 { public: MatrixAssembler2(int m, int n) : m(m), n(n), jc(n+1) {} MatrixAssembler2(int m, int n, int coord_nnz) : m(m), n(n), jc(n+1), coords(coord_nnz) {} void add_entry(int i, int j, double Aij); void add_entry(const int* i, const int* j, double* Aij, int m_elt, int n_elt); void wipe(); void pack_cache(); void compress(); int get_m() { return m; } int get_n() { return n; } int* get_jc() { return &(jc[0]); } int* get_ir() { return &(ir[0]); } double* get_pr() { return &(pr[0]); } int cache_nnz() { return coords.size(); } int csc_nnz() { return pr.size(); } private: struct Coord { Coord() {} Coord(int i, int j, double Aij) : i(i), j(j), Aij(Aij) {} bool operator<(const Coord& coord) const { return ((j < coord.j) || (j == coord.j && i < coord.i)); } int i, j; double Aij; }; int m, n; // Things that fit in the compressed sparse representation vector jc; vector ir; vector pr; // Cache of entries that didn't fit in the compressed sparse form vector coords; }; #endif /* ASSEMBLER2_H */ zgimbutas-mwrap-04cdb66/example/fem/src/elastic2d.cc000066400000000000000000000073051522673526200224460ustar00rootroot00000000000000/* * elastic2d.cc * Element type for 2D elasticity (4 node quad only). * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "etype.h" #include "mesh.h" #include "elastic2d.h" #include "quad2d.h" #include "gauss2by2.h" #include #define ME Elastic2D ME::ME(double E, double nu, const char* type) { if (strcmp(type, "plane stress") == 0) plane_stress(E, nu); else plane_strain(E, nu); } void ME::plane_strain(double E, double nu) { /* input E, nu; output D(3,3); D = E/(1+nu)/(1-2*nu) * [ 1-nu, nu, 0; nu, 1-nu, 0; 0, 0, (1-2*nu)/2 ]; */ } void ME::plane_stress(double E, double nu) { /* input E, nu; output D(3,3); D = E/(1-nu*nu) * [ 1, nu, 0; nu, 1, 0; 0, 0, (1-nu)/2 ]; */ } void ME::assign_ids(Mesh* mesh, int eltid) { for (int i = 0; i < 4; ++i) { int ni = mesh->ix(i,eltid); mesh->id(0,ni) = 1; mesh->id(1,ni) = 1; } } void ME::assemble_f(Mesh* mesh, int eltid) { Quad2d quad; Gauss4 xi(quad); get_quad(quad, mesh, eltid); vector K(4*4); for (xi.start(); !xi.done(); ++xi) { double eps[3] = {0, 0, 0}; for (int j = 0; j < 4; ++j) { const double* u = mesh->u(mesh->ix(j,eltid)); double Nj_x = xi.dN(j,0); double Nj_y = xi.dN(j,1); /* // Contribute strain at quadrature point input Nj_x, Nj_y, D(3,3); inout eps(3); input u(2); Bj = [Nj_x, 0; 0, Nj_y; Nj_y, Nj_x]; eps += Bj*u; */ } for (int i = 0; i < 4; ++i) { double* f = mesh->f(mesh->ix(i,eltid)); double Ni_x = xi.dN(i,0); double Ni_y = xi.dN(i,1); double w = xi.wt(); /* // Contribute B_i'*D*B(u) * w input Ni_x, Ni_y, D(3,3), w; input eps(3); inout f(2); Bi = [Ni_x, 0; 0, Ni_y; Ni_y, Ni_x]; f += Bi'*D*eps*w; */ } } } void ME::assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler) { Quad2d quad; Gauss4 xi(quad); get_quad(quad, mesh, eltid); vector K(8*8); for (xi.start(); !xi.done(); ++xi) { double w = xi.wt(); for (int j = 0; j < 4; ++j) { double Nj_x = xi.dN(j,0); double Nj_y = xi.dN(j,1); for (int i = 0; i < 4; ++i) { double Ni_x = xi.dN(i,0); double Ni_y = xi.dN(i,1); double* Knodal = &(K[16*j +2*i+ 0]); /* // B-matrix based displacement formulation // Isotropic plane strain constitutive tensor input D(3,3), w; input Ni_x, Ni_y, Nj_x, Nj_y; inout Knodal[8](2,2); Bi = [Ni_x, 0; 0, Ni_y; Ni_y, Ni_x]; Bj = [Nj_x, 0; 0, Nj_y; Nj_y, Nj_x]; Knodal += Bi'*D*Bj * w; */ } } } K_assembler->add_entry(id, id, &(K[0]), 8, 8); } void ME::get_quad(FEShapes& quad, Mesh* mesh, int eltid) { for (int i = 0; i < 4; ++i) { int n = mesh->ix(i,eltid); quad.set_node(i, mesh->x(n)); id[2*i+0] = mesh->id(0,n); id[2*i+1] = mesh->id(1,n); } } zgimbutas-mwrap-04cdb66/example/fem/src/elastic2d.h000066400000000000000000000013701522673526200223040ustar00rootroot00000000000000/* * elastic2d.h * Element type for 2D elasticity * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef ELASTIC2D_H #define ELASTIC2D_H #include "etype.h" #include "mesh.h" #include "feshapes.h" class Elastic2D : public EType { public: Elastic2D(double E, double nu, const char* type = "plane strain"); void assign_ids(Mesh* mesh, int eltid); void assemble_f(Mesh* mesh, int eltid); void assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler); private: double D[9]; int id[8]; void plane_strain(double E, double nu); void plane_stress(double E, double nu); void get_quad(FEShapes& quad, Mesh* mesh, int eltid); }; #endif /* ELASTIC2D_H */ zgimbutas-mwrap-04cdb66/example/fem/src/elastic2d1.cc000066400000000000000000000250111522673526200225210ustar00rootroot00000000000000/* * elastic2d.cc * Element type for 2D elasticity (4 node quad only). * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "etype.h" #include "mesh.h" #include "elastic2d.h" #include "quad2d.h" #include "gauss2by2.h" #include #define ME Elastic2D ME::ME(double E, double nu, const char* type) { if (strcmp(type, "plane stress") == 0) plane_stress(E, nu); else plane_strain(E, nu); } void ME::plane_strain(double E, double nu) { /* input E, nu; output D(3,3); D = E/(1+nu)/(1-2*nu) * [ 1-nu, nu, 0; nu, 1-nu, 0; 0, 0, (1-2*nu)/2 ]; */ /* */ { double tmp1_ = E; double tmp2_ = nu; double tmp4_ = 1.0 + tmp2_; double tmp5_ = tmp1_ / tmp4_; double tmp7_ = 2.0 * tmp2_; double tmp8_ = 1.0 - tmp7_; double tmp9_ = tmp5_ / tmp8_; double tmp10_ = 1.0 - tmp2_; double tmp12_ = tmp8_ / 2.0; double tmp13_ = tmp9_ * tmp10_; double tmp14_ = tmp9_ * tmp2_; double tmp15_ = tmp9_ * tmp12_; D[0*3+0] = tmp13_; D[0*3+1] = tmp14_; D[0*3+2] = 0; D[1*3+0] = tmp14_; D[1*3+1] = tmp13_; D[1*3+2] = 0; D[2*3+0] = 0; D[2*3+1] = 0; D[2*3+2] = tmp15_; } /* */ } void ME::plane_stress(double E, double nu) { /* input E, nu; output D(3,3); D = E/(1-nu*nu) * [ 1, nu, 0; nu, 1, 0; 0, 0, (1-nu)/2 ]; */ /* */ { double tmp1_ = E; double tmp2_ = nu; double tmp4_ = tmp2_ * tmp2_; double tmp5_ = 1.0 - tmp4_; double tmp6_ = tmp1_ / tmp5_; double tmp8_ = 1.0 - tmp2_; double tmp10_ = tmp8_ / 2.0; double tmp11_ = tmp6_ * tmp2_; double tmp12_ = tmp6_ * tmp10_; D[0*3+0] = tmp6_; D[0*3+1] = tmp11_; D[0*3+2] = 0; D[1*3+0] = tmp11_; D[1*3+1] = tmp6_; D[1*3+2] = 0; D[2*3+0] = 0; D[2*3+1] = 0; D[2*3+2] = tmp12_; } /* */ } void ME::assign_ids(Mesh* mesh, int eltid) { for (int i = 0; i < 4; ++i) { int ni = mesh->ix(i,eltid); mesh->id(0,ni) = 1; mesh->id(1,ni) = 1; } } void ME::assemble_f(Mesh* mesh, int eltid) { Quad2d quad; Gauss4 xi(quad); get_quad(quad, mesh, eltid); vector K(4*4); for (xi.start(); !xi.done(); ++xi) { double eps[3] = {0, 0, 0}; for (int j = 0; j < 4; ++j) { const double* u = mesh->u(mesh->ix(j,eltid)); double Nj_x = xi.dN(j,0); double Nj_y = xi.dN(j,1); /* // Contribute strain at quadrature point input Nj_x, Nj_y, D(3,3); inout eps(3); input u(2); Bj = [Nj_x, 0; 0, Nj_y; Nj_y, Nj_x]; eps += Bj*u; */ /* */ { double tmp1_ = Nj_x; double tmp2_ = Nj_y; double tmp3_ = eps[0*3+0]; double tmp4_ = eps[0*3+1]; double tmp5_ = eps[0*3+2]; double tmp6_ = u[0*2+0]; double tmp7_ = u[0*2+1]; double tmp8_ = tmp1_ * tmp6_; double tmp9_ = tmp2_ * tmp7_; double tmp10_ = tmp2_ * tmp6_; double tmp11_ = tmp1_ * tmp7_; double tmp12_ = tmp10_ + tmp11_; double tmp13_ = tmp3_ + tmp8_; double tmp14_ = tmp4_ + tmp9_; double tmp15_ = tmp5_ + tmp12_; eps[0*3+0] = tmp13_; eps[0*3+1] = tmp14_; eps[0*3+2] = tmp15_; } /* */ } for (int i = 0; i < 4; ++i) { double* f = mesh->f(mesh->ix(i,eltid)); double Ni_x = xi.dN(i,0); double Ni_y = xi.dN(i,1); double w = xi.wt(); /* // Contribute B_i'*D*B(u) * w input Ni_x, Ni_y, D(3,3), w; input eps(3); inout f(2); Bi = [Ni_x, 0; 0, Ni_y; Ni_y, Ni_x]; f += Bi'*D*eps*w; */ /* */ { double tmp1_ = Ni_x; double tmp2_ = Ni_y; double tmp3_ = D[0*3+0]; double tmp4_ = D[0*3+1]; double tmp5_ = D[0*3+2]; double tmp6_ = D[1*3+0]; double tmp7_ = D[1*3+1]; double tmp8_ = D[1*3+2]; double tmp9_ = D[2*3+0]; double tmp10_ = D[2*3+1]; double tmp11_ = D[2*3+2]; double tmp12_ = w; double tmp13_ = eps[0*3+0]; double tmp14_ = eps[0*3+1]; double tmp15_ = eps[0*3+2]; double tmp16_ = f[0*2+0]; double tmp17_ = f[0*2+1]; double tmp18_ = tmp1_ * tmp3_; double tmp19_ = tmp2_ * tmp5_; double tmp20_ = tmp18_ + tmp19_; double tmp21_ = tmp2_ * tmp4_; double tmp22_ = tmp1_ * tmp5_; double tmp23_ = tmp21_ + tmp22_; double tmp24_ = tmp1_ * tmp6_; double tmp25_ = tmp2_ * tmp8_; double tmp26_ = tmp24_ + tmp25_; double tmp27_ = tmp2_ * tmp7_; double tmp28_ = tmp1_ * tmp8_; double tmp29_ = tmp27_ + tmp28_; double tmp30_ = tmp1_ * tmp9_; double tmp31_ = tmp2_ * tmp11_; double tmp32_ = tmp30_ + tmp31_; double tmp33_ = tmp2_ * tmp10_; double tmp34_ = tmp1_ * tmp11_; double tmp35_ = tmp33_ + tmp34_; double tmp36_ = tmp20_ * tmp13_; double tmp37_ = tmp26_ * tmp14_; double tmp38_ = tmp36_ + tmp37_; double tmp39_ = tmp32_ * tmp15_; double tmp40_ = tmp38_ + tmp39_; double tmp41_ = tmp23_ * tmp13_; double tmp42_ = tmp29_ * tmp14_; double tmp43_ = tmp41_ + tmp42_; double tmp44_ = tmp35_ * tmp15_; double tmp45_ = tmp43_ + tmp44_; double tmp46_ = tmp40_ * tmp12_; double tmp47_ = tmp45_ * tmp12_; double tmp48_ = tmp16_ + tmp46_; double tmp49_ = tmp17_ + tmp47_; f[0*2+0] = tmp48_; f[0*2+1] = tmp49_; } /* */ } } } void ME::assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler) { Quad2d quad; Gauss4 xi(quad); get_quad(quad, mesh, eltid); vector K(8*8); for (xi.start(); !xi.done(); ++xi) { double w = xi.wt(); for (int j = 0; j < 4; ++j) { double Nj_x = xi.dN(j,0); double Nj_y = xi.dN(j,1); for (int i = 0; i < 4; ++i) { double Ni_x = xi.dN(i,0); double Ni_y = xi.dN(i,1); double* Knodal = &(K[16*j +2*i+ 0]); /* // B-matrix based displacement formulation // Isotropic plane strain constitutive tensor input D(3,3), w; input Ni_x, Ni_y, Nj_x, Nj_y; inout Knodal[8](2,2); Bi = [Ni_x, 0; 0, Ni_y; Ni_y, Ni_x]; Bj = [Nj_x, 0; 0, Nj_y; Nj_y, Nj_x]; Knodal += Bi'*D*Bj * w; */ /* */ { double tmp1_ = D[0*3+0]; double tmp2_ = D[0*3+1]; double tmp3_ = D[0*3+2]; double tmp4_ = D[1*3+0]; double tmp5_ = D[1*3+1]; double tmp6_ = D[1*3+2]; double tmp7_ = D[2*3+0]; double tmp8_ = D[2*3+1]; double tmp9_ = D[2*3+2]; double tmp10_ = w; double tmp11_ = Ni_x; double tmp12_ = Ni_y; double tmp13_ = Nj_x; double tmp14_ = Nj_y; double tmp15_ = Knodal[0*8+0]; double tmp16_ = Knodal[0*8+1]; double tmp17_ = Knodal[1*8+0]; double tmp18_ = Knodal[1*8+1]; double tmp19_ = tmp11_ * tmp1_; double tmp20_ = tmp12_ * tmp3_; double tmp21_ = tmp19_ + tmp20_; double tmp22_ = tmp12_ * tmp2_; double tmp23_ = tmp11_ * tmp3_; double tmp24_ = tmp22_ + tmp23_; double tmp25_ = tmp11_ * tmp4_; double tmp26_ = tmp12_ * tmp6_; double tmp27_ = tmp25_ + tmp26_; double tmp28_ = tmp12_ * tmp5_; double tmp29_ = tmp11_ * tmp6_; double tmp30_ = tmp28_ + tmp29_; double tmp31_ = tmp11_ * tmp7_; double tmp32_ = tmp12_ * tmp9_; double tmp33_ = tmp31_ + tmp32_; double tmp34_ = tmp12_ * tmp8_; double tmp35_ = tmp11_ * tmp9_; double tmp36_ = tmp34_ + tmp35_; double tmp37_ = tmp21_ * tmp13_; double tmp38_ = tmp33_ * tmp14_; double tmp39_ = tmp37_ + tmp38_; double tmp40_ = tmp24_ * tmp13_; double tmp41_ = tmp36_ * tmp14_; double tmp42_ = tmp40_ + tmp41_; double tmp43_ = tmp27_ * tmp14_; double tmp44_ = tmp33_ * tmp13_; double tmp45_ = tmp43_ + tmp44_; double tmp46_ = tmp30_ * tmp14_; double tmp47_ = tmp36_ * tmp13_; double tmp48_ = tmp46_ + tmp47_; double tmp49_ = tmp39_ * tmp10_; double tmp50_ = tmp42_ * tmp10_; double tmp51_ = tmp45_ * tmp10_; double tmp52_ = tmp48_ * tmp10_; double tmp53_ = tmp15_ + tmp49_; double tmp54_ = tmp16_ + tmp50_; double tmp55_ = tmp17_ + tmp51_; double tmp56_ = tmp18_ + tmp52_; Knodal[0*8+0] = tmp53_; Knodal[0*8+1] = tmp54_; Knodal[1*8+0] = tmp55_; Knodal[1*8+1] = tmp56_; } /* */ } } } K_assembler->add_entry(id, id, &(K[0]), 8, 8); } void ME::get_quad(FEShapes& quad, Mesh* mesh, int eltid) { for (int i = 0; i < 4; ++i) { int n = mesh->ix(i,eltid); quad.set_node(i, mesh->x(n)); id[2*i+0] = mesh->id(0,n); id[2*i+1] = mesh->id(1,n); } } zgimbutas-mwrap-04cdb66/example/fem/src/etype.h000066400000000000000000000010001522673526200215460ustar00rootroot00000000000000/* * etype.h * Element type interface definition. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef ETYPE_H #define ETYPE_H #include "assembler.h" class Mesh; class EType { public: virtual ~EType(); virtual void assign_ids(Mesh* mesh, int eltid) = 0; virtual void assemble_f(Mesh* mesh, int eltid) = 0; virtual void assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler) = 0; }; #endif /* ETYPE_H */ zgimbutas-mwrap-04cdb66/example/fem/src/feintegrals.h000066400000000000000000000021101522673526200227260ustar00rootroot00000000000000/* * feintegrals.h * Interface for element quadrature rules. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef FEINTEGRALS_H #define FEINTEGRALS_H #include "feshapes.h" #include using std::vector; class VolumeQuadrature { public: VolumeQuadrature(FEShapes& quad, int ndim) : quad(quad), nshape1(quad.nshape()), xx1(2), N1(quad.nshape()), dN1(ndim*quad.nshape()) {} virtual void start() = 0; virtual bool done() = 0; virtual void operator++() = 0; virtual double wt() = 0; int nshape() { return nshape1; } double* xx() { return &(xx1[0]); } double* N() { return &(N1[0]); } double* dN() { return &(dN1[0]); } double xx(int i) { return xx1[i]; } double N(int i) { return N1[i]; } double dN(int i, int j) { return dN1[i+j*nshape1]; } protected: FEShapes& quad; int nshape1; vector xx1; vector N1; vector dN1; double J; }; #endif /* FEINTEGRALS_H */ zgimbutas-mwrap-04cdb66/example/fem/src/feshapes.h000066400000000000000000000010621522673526200222260ustar00rootroot00000000000000/* * feshapes.h * Interface for element shape functions. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef FESHAPES_H #define FESHAPES_H class FEShapes { public: virtual ~FEShapes() {} virtual void set_node(int nodenum, const double* x) = 0; virtual void set_nodes(const double* x) = 0; virtual void eval(const double *XX, double* xx, double* N, double* dN, double& J) const = 0; virtual int nshape() const = 0; }; #endif /* FESHAPES_H */ zgimbutas-mwrap-04cdb66/example/fem/src/gauss2by2.cc000066400000000000000000000007151522673526200224130ustar00rootroot00000000000000/* * gauss2by2.cc * Parent domain node locations and weights for quadrature on a * 2-by-2 Gauss grid over [-1,1]^2. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "gauss2by2.h" const double Gauss4::X[12] = { -0.577350269189626, -0.577350269189626, 1, 0.577350269189626, -0.577350269189626, 1, 0.577350269189626, 0.577350269189626, 1, -0.577350269189626, 0.577350269189626, 1, }; zgimbutas-mwrap-04cdb66/example/fem/src/gauss2by2.h000066400000000000000000000013071522673526200222530ustar00rootroot00000000000000/* * gauss2by2.h * 2-by-2 Gauss grid quadrature over [-1,1]^2. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef GAUSS2BY2_H #define GAUSS2BY2_H #include "feshapes.h" #include "feintegrals.h" class Gauss4 : public VolumeQuadrature { public: Gauss4(FEShapes& quad) : VolumeQuadrature(quad, 2) { start(); } void start() { i = 0; eval(); } bool done() { return (i > 3); } void operator++() { if (++i <= 3) eval(); } double wt() { return X[3*i+2]*J; } private: static const double X[12]; int i; void eval() { quad.eval(X+3*i, xx(), N(), dN(), J); } }; #endif /* GAUSS2BY2_H */ zgimbutas-mwrap-04cdb66/example/fem/src/mesh.cc000066400000000000000000000042041522673526200215230ustar00rootroot00000000000000/* * mesh.cc * Finite element mesh implementation. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "mesh.h" #define ME Mesh EType::~EType() { } ME::~ME() { for (vector::iterator i = materials_owned.begin(); i != materials_owned.end(); ++i) { delete(*i); } } int ME::add_node(double* x) { int id = numnp(); for (int i = 0; i < ndm_; ++i) X.push_back(x[i]); return id; } int ME::add_element(EType* etype, int* nodes, int nen) { int id = numelt(); int i = 0; for (; i < nen && i < maxnen_; ++i) IX.push_back(nodes[i]); for (; i < maxnen_; ++i) IX.push_back(-1); elements.push_back(etype); return id; } void ME::add_material(EType* material) { materials_owned.push_back(material); } void ME::set_ur(const double* ur) { for (int i = 0; i < ID.size(); ++i) { if (ID[i] >= 0) U[i] = ur[ID[i]]; if (BC[i]) U[i] = BV[i]; } } void ME::get_ur(double* ur) { for (int i = 0; i < ID.size(); ++i) if (ID[i] >= 0) ur[ID[i]] = U[i]; } void ME::get_fr(double* fr) { for (int i = 0; i < ID.size(); ++i) { if (ID[i] >= 0) fr[ID[i]] = R[i]; if (!BC[i]) fr[ID[i]] -= BV[i]; } } int ME::initialize() { ID.resize(maxndf_ * numnp()); R.resize (maxndf_ * numnp()); U.resize (maxndf_ * numnp()); BC.resize(maxndf_ * numnp()); BV.resize(maxndf_ * numnp()); return assign_ids(); } int ME::assign_ids() { numid_ = 0; int n = numelt(); for (int i = 0; i < ID.size(); ++i) ID[i] = 0; for (int i = 0; i < n; ++i) if (elements[i]) elements[i]->assign_ids(this, i); for (int i = 0; i < ID.size(); ++i) ID[i] = ( (ID[i] && !BC[i]) ? numid_++ : -1 ); return numid_; } void ME::assemble_F() { int n = numelt(); for (int i = 0; i < n; ++i) if (elements[i]) elements[i]->assemble_f(this, i); } void ME::assemble_K(MatrixAssembler* K_assembler) { int n = numelt(); for (int i = 0; i < n; ++i) if (elements[i]) elements[i]->assemble_K(this, i, K_assembler); } zgimbutas-mwrap-04cdb66/example/fem/src/mesh.h000066400000000000000000000065341522673526200213750ustar00rootroot00000000000000/* * mesh.h * Finite element mesh implementation. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef MESH_H #define MESH_H #include "assembler.h" #include "etype.h" #include using std::vector; class Mesh { public: Mesh(int ndm, int maxndf, int maxnen) : ndm_(ndm), maxndf_(maxndf), maxnen_(maxnen), numid_(0) {} ~Mesh(); int ndm() const { return ndm_; } int maxndf() const { return maxndf_; } int maxnen() const { return maxnen_; } int numelt() const { return elements.size(); } int numnp() const { return X.size() / ndm_; } int numid() const { return numid_; } double* x () { return &(X[0]); } int* ix() { return &(IX[0]); } int* id() { return &(ID[0]); } double* u () { return &(U[0]); } double* f () { return &(R[0]); } char* bc() { return &(BC[0]); } double* bv() { return &(BV[0]); } const double* x (int j) const { return &(X [j*ndm_]); } const int* ix(int j) const { return &(IX[j*maxnen_]); } const int* id(int j) const { return &(ID[j*maxndf_]); } const double* u (int j) const { return &(U [j*maxndf_]); } const double* f (int j) const { return &(R [j*maxndf_]); } double* f (int j) { return &(R [j*maxndf_]); } const char* bc(int j) const { return &(BC[j*maxndf_]); } const double* bv(int j) const { return &(BV[j*maxndf_]); } double x (int i, int j) const { return X [i+j*ndm_]; } int ix(int i, int j) const { return IX[i+j*maxnen_]; } int id(int i, int j) const { return ID[i+j*maxndf_]; } int& id(int i, int j) { return ID[i+j*maxndf_]; } double u (int i, int j) const { return U [i+j*maxndf_]; } double f (int i, int j) const { return R [i+j*maxndf_]; } double& f (int i, int j) { return R [i+j*maxndf_]; } char bc(int i, int j) const { return BC[i+j*maxndf_]; } char& bc(int i, int j) { return BC[i+j*maxndf_]; } double bv(int i, int j) const { return BV[i+j*maxndf_]; } double& bv(int i, int j) { return BV[i+j*maxndf_]; } void set_ur(const double* ur); void get_ur(double* ur); void get_fr(double* ur); int initialize(); int assign_ids(); void assemble_F(); void assemble_K(MatrixAssembler* K_assembler); int add_node(double* x); int add_element(EType* etype, int* nodes, int numnp); void add_material(EType* material); private: Mesh(const Mesh&); Mesh& operator=(const Mesh&); int ndm_; // Number of spatial dimensions int maxndf_; // Maximum degrees of freedom per node int maxnen_; // Maximum number of nodes per element int numid_; // Number of active degrees of freedom vector X; // Coordinate array ( ndm * numnp ) vector IX; // Element connectivity ( maxnen * numelt ) vector ID; // Identifier assignment ( maxndf * numnp ) vector R; // Full residual vector ( maxndf * numnp ) vector U; // Full solution ( maxndf * numnp ) vector BC; // Flag Dirichlet BCs ( maxndf * numnp ) vector BV; // Boundary values ( maxndf * numnp ) vector elements; // Material assignment (numelt) vector materials_owned; }; #endif /* MESH_H */ zgimbutas-mwrap-04cdb66/example/fem/src/quad2d.cc000066400000000000000000000025531522673526200217540ustar00rootroot00000000000000/* * quad2d.h * Implementation for four-node quad element shapes. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "quad2d.h" #define ME Quad2d ME::~ME() { } void ME::set_node(int nodenum, const double* x) { nodex[2*nodenum+0] = x[0]; nodex[2*nodenum+1] = x[1]; } void ME::set_nodes(const double* x) { for (int i = 0; i < 8; ++i) nodex[i] = x[i]; } void ME::eval(const double *XX, double* xx, double* N, double* dN, double& J) const { double X = XX[0]; double Y = XX[1]; double FF[4]; /* // Evaluate element functions input X, Y; input nodex(2,4); output N(4); output dN(4,2); output xx(2); output FF(2,2); N1x = (1-X)/2; N1y = (1-Y)/2; N2x = (1+X)/2; N2y = (1+Y)/2; N = [ N1x*N1y; N2x*N1y; N2x*N2y; N1x*N2y]; dN = [ -N1y, N1y, N2y, -N2y; -N1x, -N2x, N2x, N1x ]'/2; xx = nodex*N; FF = nodex*dN; */ remap_gradients(FF, dN, J); } void ME::remap_gradients(double* FF, double* dN, double& J) const { J = (FF[0]*FF[3]-FF[1]*FF[2]); double invF[4] = { FF[3]/J, -FF[1]/J, -FF[2]/J, FF[0]/J }; /* // Remap gradients inout dN(4,2); input invF(2,2); dN = dN*invF; */ } zgimbutas-mwrap-04cdb66/example/fem/src/quad2d.h000066400000000000000000000013011522673526200216040ustar00rootroot00000000000000/* * quad2d.h * Interface for four-node quad element shapes. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef QUAD2D_H #define QUAD2D_H #include "feshapes.h" class Quad2d : public FEShapes { public: Quad2d() {} Quad2d(const double* x) { set_nodes(x); } virtual ~Quad2d(); void set_node(int nodenum, const double* x); void set_nodes(const double* x); void eval(const double *XX, double* xx, double* N, double* dN, double& J) const; int nshape() const { return 4; } private: double nodex[2*4]; void remap_gradients(double* FF, double* dN, double& J) const; }; #endif /* QUAD2D_H */ zgimbutas-mwrap-04cdb66/example/fem/src/quad2d1.cc000066400000000000000000000136061522673526200220360ustar00rootroot00000000000000/* * quad2d.h * Implementation for four-node quad element shapes. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "quad2d.h" #define ME Quad2d ME::~ME() { } void ME::set_node(int nodenum, const double* x) { nodex[2*nodenum+0] = x[0]; nodex[2*nodenum+1] = x[1]; } void ME::set_nodes(const double* x) { for (int i = 0; i < 8; ++i) nodex[i] = x[i]; } void ME::eval(const double *XX, double* xx, double* N, double* dN, double& J) const { double X = XX[0]; double Y = XX[1]; double FF[4]; /* // Evaluate element functions input X, Y; input nodex(2,4); output N(4); output dN(4,2); output xx(2); output FF(2,2); N1x = (1-X)/2; N1y = (1-Y)/2; N2x = (1+X)/2; N2y = (1+Y)/2; N = [ N1x*N1y; N2x*N1y; N2x*N2y; N1x*N2y]; dN = [ -N1y, N1y, N2y, -N2y; -N1x, -N2x, N2x, N1x ]'/2; xx = nodex*N; FF = nodex*dN; */ /* */ { double tmp1_ = X; double tmp2_ = Y; double tmp3_ = nodex[0*2+0]; double tmp4_ = nodex[0*2+1]; double tmp5_ = nodex[1*2+0]; double tmp6_ = nodex[1*2+1]; double tmp7_ = nodex[2*2+0]; double tmp8_ = nodex[2*2+1]; double tmp9_ = nodex[3*2+0]; double tmp10_ = nodex[3*2+1]; double tmp12_ = 1.0 - tmp1_; double tmp14_ = tmp12_ / 2.0; double tmp15_ = 1.0 - tmp2_; double tmp16_ = tmp15_ / 2.0; double tmp17_ = 1.0 + tmp1_; double tmp18_ = tmp17_ / 2.0; double tmp19_ = 1.0 + tmp2_; double tmp20_ = tmp19_ / 2.0; double tmp21_ = tmp14_ * tmp16_; double tmp22_ = tmp18_ * tmp16_; double tmp23_ = tmp18_ * tmp20_; double tmp24_ = tmp14_ * tmp20_; double tmp25_ = -tmp16_; double tmp26_ = -tmp20_; double tmp27_ = -tmp14_; double tmp28_ = -tmp18_; double tmp29_ = tmp25_ / 2.0; double tmp30_ = tmp16_ / 2.0; double tmp31_ = tmp20_ / 2.0; double tmp32_ = tmp26_ / 2.0; double tmp33_ = tmp27_ / 2.0; double tmp34_ = tmp28_ / 2.0; double tmp35_ = tmp18_ / 2.0; double tmp36_ = tmp14_ / 2.0; double tmp37_ = tmp3_ * tmp21_; double tmp38_ = tmp5_ * tmp22_; double tmp39_ = tmp37_ + tmp38_; double tmp40_ = tmp7_ * tmp23_; double tmp41_ = tmp39_ + tmp40_; double tmp42_ = tmp9_ * tmp24_; double tmp43_ = tmp41_ + tmp42_; double tmp44_ = tmp4_ * tmp21_; double tmp45_ = tmp6_ * tmp22_; double tmp46_ = tmp44_ + tmp45_; double tmp47_ = tmp8_ * tmp23_; double tmp48_ = tmp46_ + tmp47_; double tmp49_ = tmp10_ * tmp24_; double tmp50_ = tmp48_ + tmp49_; double tmp51_ = tmp3_ * tmp29_; double tmp52_ = tmp5_ * tmp30_; double tmp53_ = tmp51_ + tmp52_; double tmp54_ = tmp7_ * tmp31_; double tmp55_ = tmp53_ + tmp54_; double tmp56_ = tmp9_ * tmp32_; double tmp57_ = tmp55_ + tmp56_; double tmp58_ = tmp4_ * tmp29_; double tmp59_ = tmp6_ * tmp30_; double tmp60_ = tmp58_ + tmp59_; double tmp61_ = tmp8_ * tmp31_; double tmp62_ = tmp60_ + tmp61_; double tmp63_ = tmp10_ * tmp32_; double tmp64_ = tmp62_ + tmp63_; double tmp65_ = tmp3_ * tmp33_; double tmp66_ = tmp5_ * tmp34_; double tmp67_ = tmp65_ + tmp66_; double tmp68_ = tmp7_ * tmp35_; double tmp69_ = tmp67_ + tmp68_; double tmp70_ = tmp9_ * tmp36_; double tmp71_ = tmp69_ + tmp70_; double tmp72_ = tmp4_ * tmp33_; double tmp73_ = tmp6_ * tmp34_; double tmp74_ = tmp72_ + tmp73_; double tmp75_ = tmp8_ * tmp35_; double tmp76_ = tmp74_ + tmp75_; double tmp77_ = tmp10_ * tmp36_; double tmp78_ = tmp76_ + tmp77_; N[0*4+0] = tmp21_; N[0*4+1] = tmp22_; N[0*4+2] = tmp23_; N[0*4+3] = tmp24_; dN[0*4+0] = tmp29_; dN[0*4+1] = tmp30_; dN[0*4+2] = tmp31_; dN[0*4+3] = tmp32_; dN[1*4+0] = tmp33_; dN[1*4+1] = tmp34_; dN[1*4+2] = tmp35_; dN[1*4+3] = tmp36_; xx[0*2+0] = tmp43_; xx[0*2+1] = tmp50_; FF[0*2+0] = tmp57_; FF[0*2+1] = tmp64_; FF[1*2+0] = tmp71_; FF[1*2+1] = tmp78_; } /* */ remap_gradients(FF, dN, J); } void ME::remap_gradients(double* FF, double* dN, double& J) const { J = (FF[0]*FF[3]-FF[1]*FF[2]); double invF[4] = { FF[3]/J, -FF[1]/J, -FF[2]/J, FF[0]/J }; /* // Remap gradients inout dN(4,2); input invF(2,2); dN = dN*invF; */ /* */ { double tmp1_ = dN[0*4+0]; double tmp2_ = dN[0*4+1]; double tmp3_ = dN[0*4+2]; double tmp4_ = dN[0*4+3]; double tmp5_ = dN[1*4+0]; double tmp6_ = dN[1*4+1]; double tmp7_ = dN[1*4+2]; double tmp8_ = dN[1*4+3]; double tmp9_ = invF[0*2+0]; double tmp10_ = invF[0*2+1]; double tmp11_ = invF[1*2+0]; double tmp12_ = invF[1*2+1]; double tmp13_ = tmp1_ * tmp9_; double tmp14_ = tmp5_ * tmp10_; double tmp15_ = tmp13_ + tmp14_; double tmp16_ = tmp2_ * tmp9_; double tmp17_ = tmp6_ * tmp10_; double tmp18_ = tmp16_ + tmp17_; double tmp19_ = tmp3_ * tmp9_; double tmp20_ = tmp7_ * tmp10_; double tmp21_ = tmp19_ + tmp20_; double tmp22_ = tmp4_ * tmp9_; double tmp23_ = tmp8_ * tmp10_; double tmp24_ = tmp22_ + tmp23_; double tmp25_ = tmp1_ * tmp11_; double tmp26_ = tmp5_ * tmp12_; double tmp27_ = tmp25_ + tmp26_; double tmp28_ = tmp2_ * tmp11_; double tmp29_ = tmp6_ * tmp12_; double tmp30_ = tmp28_ + tmp29_; double tmp31_ = tmp3_ * tmp11_; double tmp32_ = tmp7_ * tmp12_; double tmp33_ = tmp31_ + tmp32_; double tmp34_ = tmp4_ * tmp11_; double tmp35_ = tmp8_ * tmp12_; double tmp36_ = tmp34_ + tmp35_; dN[0*4+0] = tmp15_; dN[0*4+1] = tmp18_; dN[0*4+2] = tmp21_; dN[0*4+3] = tmp24_; dN[1*4+0] = tmp27_; dN[1*4+1] = tmp30_; dN[1*4+2] = tmp33_; dN[1*4+3] = tmp36_; } /* */ } zgimbutas-mwrap-04cdb66/example/fem/src/scalar1d.cc000066400000000000000000000020501522673526200222560ustar00rootroot00000000000000/* * scalar1d.cc * Element type for 1D Laplacian. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "scalar1d.h" #define ME Scalar1D void ME::assign_ids(Mesh* mesh, int eltid) { int n1 = mesh->ix(0,eltid); int n2 = mesh->ix(1,eltid); mesh->id(0,n1) = 1; mesh->id(0,n2) = 1; } void ME::assemble_f(Mesh* mesh, int eltid) { int n1 = mesh->ix(0,eltid); int n2 = mesh->ix(1,eltid); double L = mesh->x(0,n2) - mesh->x(0,n1); double kappa = k/L; double udiff = mesh->u(0,n2) - mesh->u(0,n1); mesh->f(0,n1) -= kappa*udiff; mesh->f(0,n2) += kappa*udiff; } void ME::assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler) { int n1 = mesh->ix(0,eltid); int n2 = mesh->ix(1,eltid); double L = mesh->x(0,n2) - mesh->x(0,n1); double kappa = k/L; int i[2] = {mesh->id(0,n1), mesh->id(0,n2)}; double K_elt[4] = { kappa, -kappa, -kappa, kappa }; K_assembler->add_entry(i, i, K_elt, 2, 2); } zgimbutas-mwrap-04cdb66/example/fem/src/scalar1d.h000066400000000000000000000010221522673526200221160ustar00rootroot00000000000000/* * scalar1d.h * Element type for 1D Laplacian. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef SCALAR1D_H #define SCALAR1D_H #include "etype.h" #include "mesh.h" class Scalar1D : public EType { public: Scalar1D(double k) : k(k) {} void assign_ids(Mesh* mesh, int eltid); void assemble_f(Mesh* mesh, int eltid); void assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler); private: double k; }; #endif /* SCALAR1D_H */ zgimbutas-mwrap-04cdb66/example/fem/src/scalar2d.cc000066400000000000000000000033531522673526200222660ustar00rootroot00000000000000/* * scalar2d.cc * Element type for 2D Laplacian (4 node quad only). * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "etype.h" #include "mesh.h" #include "scalar2d.h" #include "quad2d.h" #include "gauss2by2.h" #define ME Scalar2D void ME::assign_ids(Mesh* mesh, int eltid) { for (int i = 0; i < 4; ++i) mesh->id(0, mesh->ix(i,eltid)) = 1; } void ME::assemble_f(Mesh* mesh, int eltid) { Quad2d quad; Gauss4 xi(quad); get_quad(quad, mesh, eltid); vector K(4*4); for (xi.start(); !xi.done(); ++xi) { // Compute grad u at the quadrature point double dudx[2] = {0, 0}; for (int j = 0; j < 4; ++j) { double uj = mesh->u(0,mesh->ix(j,eltid)); dudx[0] += xi.dN(j,0) * uj; dudx[1] += xi.dN(j,1) * uj; } // Contribute k * dot(grad N_i, grad u) * wt for (int i = 0; i < 4; ++i) mesh->f(0,mesh->ix(i,eltid)) += k * (xi.dN(i,0) * dudx[0] + xi.dN(i,1) * dudx[1]) * xi.wt(); } } void ME::assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler) { Quad2d quad; Gauss4 xi(quad); get_quad(quad, mesh, eltid); vector K(4*4); for (xi.start(); !xi.done(); ++xi) for (int j = 0; j < 4; ++j) for (int i = 0; i < 4; ++i) K[4*j+i] += k * (xi.dN(i,0) * xi.dN(j,0) + xi.dN(i,1) * xi.dN(j,1)) * xi.wt(); K_assembler->add_entry(id, id, &(K[0]), 4, 4); } void ME::get_quad(FEShapes& quad, Mesh* mesh, int eltid) { for (int i = 0; i < 4; ++i) { int n = mesh->ix(i,eltid); quad.set_node(i, mesh->x(n)); id[i] = mesh->id(0,n); } } zgimbutas-mwrap-04cdb66/example/fem/src/scalar2d.h000066400000000000000000000011621522673526200221240ustar00rootroot00000000000000/* * scalar2d.h * Element type for 2D Laplacian. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef SCALAR2D_H #define SCALAR2D_H #include "etype.h" #include "mesh.h" #include "feshapes.h" class Scalar2D : public EType { public: Scalar2D(double k) : k(k) {} void assign_ids(Mesh* mesh, int eltid); void assemble_f(Mesh* mesh, int eltid); void assemble_K(Mesh* mesh, int eltid, MatrixAssembler* K_assembler); private: double k; int id[4]; void get_quad(FEShapes& quad, Mesh* mesh, int eltid); }; #endif /* SCALAR2D_H */ zgimbutas-mwrap-04cdb66/example/fem/test_assembler.m000066400000000000000000000022251522673526200226640ustar00rootroot00000000000000% test_assembler.m % Test MWrap interface to CSC matrix assembler. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions init; N = 1000; aobj = Assembler_create(N,N); try fprintf('\nRunning initial assembly loop\n'); tic; for j = 1:N idx = [j, j+1]; Ke = [1, -1; -1, 1]; Assembler_add(aobj, idx, idx, Ke); end toc; fprintf('Nonzeros in assembler before compression and form\n'); Assembler_stats(aobj); K = Assembler_get(aobj); fprintf('Nonzeros in assembler after compression and form\n'); Assembler_stats(aobj); fprintf('\nRe-running assembly loop\n'); Assembler_clear(aobj); tic; for j = 1:N idx = [j, j+1]; Ke = [1, -1; -1, 1]; Assembler_add(aobj, idx, idx, Ke); end toc; fprintf('Nonzeros in assembler before compression and form\n'); Assembler_stats(aobj); K = Assembler_get(aobj); fprintf('Nonzeros in assembler after compression and form\n'); Assembler_stats(aobj); fprintf('\nComparing the result matrices\n'); K2 = Assembler_get(aobj); fprintf('|K2-K1| = %g\n\n', norm(K-K2,1)); catch fprintf('Caught %s\n', lasterr); end Assembler_delete(aobj); zgimbutas-mwrap-04cdb66/example/fem/test_patch.m000066400000000000000000000031441522673526200220070ustar00rootroot00000000000000% test_mesh3.m % Test MWrap interface to mesh data structure. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions init; mobj = Mesh_create(2, 2, 4); try fprintf('-- Four element elastic patch test --\n'); % Nodes: % 1 2 3 4 5 6 7 8 9 x = [ 0.0, 4.0, 10.0, 0.0, 5.5, 10.0, 0.0, 4.2, 10.0; 0.0, 0.0, 0.0, 4.5, 5.5, 5.0, 10.0, 10.0, 10.0]; % Boundary codes and values bc =[ 1, 0, 0, 1, 0, 0, 1, 0, 0; 1, 0, 0, 0, 0, 0, 0, 0, 0]; bv =[ 0, 0, 2.5, 0, 0, 5.0, 0, 0, 2.5, 0, 0, 0, 0, 0, 0, 0, 0, 0]; % Elements: % 1 2 3 4 ix = [ 1, 2, 4, 5 ; 2, 3, 5, 6 ; 5, 6, 8, 9 ; 4, 5, 7, 8 ]; % Set up problem mesh material = Mesh_add_elastic2d(mobj, 1000.0, 0.25, 'plane strain'); Mesh_load(mobj, material, x, ix); Mesh_initialize(mobj); % Set boundary conditions Mesh_set_bc(mobj, bc); Mesh_set_bv(mobj, bv); Mesh_assign_ids(mobj); % Assemble stiffness and force K = Mesh_assemble_K(mobj); F = Mesh_assemble_F(mobj); % Solve for the reduced displacement u = -K\F; % Patch test should recover linear fields -- check that it does uu = Mesh_u(mobj); resid = uu - (uu/x)*x; fprintf('Patch test residual: %g\n', norm(resid)); % Assemble residual and make sure it's zero Mesh_set_ur(mobj, u); RR = Mesh_assemble_F(mobj); fprintf('Reported residual force: %g\n', norm(RR)); catch fprintf('Error: %s\n', lasterr); end Mesh_delete(mobj); zgimbutas-mwrap-04cdb66/example/fem/test_simple.m000066400000000000000000000017471522673526200222100ustar00rootroot00000000000000% test_mesh.m % Test MWrap interface to mesh data structure. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions init; mobj = Mesh_create(1, 1, 2); try % -- Set up a simple mesh i1 = Mesh_add_node(mobj, 0); i2 = Mesh_add_node(mobj, 1); i3 = Mesh_add_node(mobj, 2); i4 = Mesh_add_node(mobj, 3); s1 = Mesh_add_scalar1d(mobj, 1); e1 = Mesh_add_element(mobj, s1, [i1, i2]); e2 = Mesh_add_element(mobj, s1, [i2, i3]); e3 = Mesh_add_element(mobj, s1, [i3, i4]); Mesh_initialize(mobj); % -- Assign Dirichlet BC to first dof at node 1 Mesh_set_bc(mobj, 1, 1, 1); fprintf('-- Mesh nodes and connectivities --\n'); x = Mesh_x(mobj) ix = Mesh_ix(mobj) fprintf('-- Mesh dof assignments --\n'); numid = Mesh_initialize(mobj) id = Mesh_id(mobj) fprintf('-- Assembled stiffness (standard three-point stencil) --\n'); K = Mesh_assemble_K(mobj); K = full(K) catch fprintf('Error: %s\n', lasterr); end Mesh_delete(mobj); zgimbutas-mwrap-04cdb66/example/foobar/000077500000000000000000000000001522673526200201725ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/foobar/Makefile000066400000000000000000000002761522673526200216370ustar00rootroot00000000000000include ../../make.inc MW=../../mwrap all: $(MW) -mex fbmex -m foobar.m foobar.mw $(MW) -mex fbmex -c fbmex.c foobar.mw $(MEX) fbmex.c clean: rm -f foobar.m fbmex.c fbmex.mex* *.o* *~ zgimbutas-mwrap-04cdb66/example/foobar/foobar.mw000066400000000000000000000002461522673526200220110ustar00rootroot00000000000000$ #include function foobar; s1 = 'foo'; s2 = 'bar'; # strncat(inout cstring[128] s1, cstring s2, int 127); fprintf('Should be foobar: %s\n', s1); zgimbutas-mwrap-04cdb66/example/run_example.m.in000066400000000000000000000001461522673526200220250ustar00rootroot00000000000000% -*- matlab -*- % Autogenerated by CMake. Do not edit manually. @PATHS@ @WORK_DIR@ run('@CALL@'); zgimbutas-mwrap-04cdb66/example/zlib/000077500000000000000000000000001522673526200176625ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/example/zlib/Makefile000066400000000000000000000003071522673526200213220ustar00rootroot00000000000000# See www.zlib.net include ../../make.inc MW=../../mwrap gzmex: $(MW) -mex gzmex -mb gzfile.mw $(MW) -mex gzmex -c gzmex.cc gzfile.mw $(MEX) gzmex.cc -lz clean: rm -f gz*.m gzmex.* *.o* eye.gz zgimbutas-mwrap-04cdb66/example/zlib/README000066400000000000000000000003111522673526200205350ustar00rootroot00000000000000This example illustrates bindings for the Zlib compression library (http://www.zlib.net/). The test illustrates how these bindings can be used to read and write MATLAB matrices in compressed storage. zgimbutas-mwrap-04cdb66/example/zlib/gzfile.mw000066400000000000000000000026331522673526200215130ustar00rootroot00000000000000% gzfile.mw % MWrap wrapper to the ZLib compression library. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions $#include // ---- @function [gzf] = gzopen(path, mode) % [gzf] = gzopen(path, mode) % % Open a zlib output file. # gzFile gzf = gzopen(cstring path, cstring mode); // ---- @function gzclose(gzf) % gzclose(gzf) % % Close a zlib output file. # gzclose(gzFile gzf); // ---- @function [A] = gzread(gzf) % [A] = gzread(gzf) % % Read a matrix from a zlib file. # int isize = sizeof(int 0); # int dsize = sizeof(double 0); nbytes = 2*isize; # gzread(gzFile gzf, output int[2] dims, int nbytes); m = dims(1); n = dims(2); nbytes = m*n*dsize; # gzread(gzFile gzf, output double[m,n] A, int nbytes); % gzwrite(gzf, A) % % Write a matrix to a zlib file. // ---- @function gzwrite(gzf, A) % [A] = gzwrite(gzf, A) % % Write a matrix from a zlib file. # int isize = sizeof(int 0); # int dsize = sizeof(double 0); nbytes = 2*isize; [m,n] = size(A); dims = [m,n]; # gzwrite(gzFile gzf, int[2] dims, int nbytes); m = dims(1); n = dims(2); nbytes = m*n*dsize; # gzwrite(gzFile gzf, double[m,n] A, int nbytes); // ---- @function gzsave(path, A) % gzsave(path, A) % % Save a matrix to a zlib file. gzf = gzopen(path, 'w+'); gzwrite(gzf, A) // ---- @function [A] = gzload(path) % [A] = gzload(path) % % Read a matrix from a zlib file. gzf = gzopen(path, 'r'); A = gzread(gzf); zgimbutas-mwrap-04cdb66/example/zlib/testgz.m000066400000000000000000000006511522673526200213620ustar00rootroot00000000000000% testgz.m % Test/demo a MWrap wrapper to the ZLib compression library. % % Copyright (c) 2007 David Bindel % See the file COPYING for copying permissions A = eye(100); fprintf('Writing identity to eye.gz\n'); fp = gzopen('eye.gz', 'w'); gzwrite(fp, A); gzclose(fp); fprintf('Reading identity back from eye.gz\n'); fp = gzopen('eye.gz', 'r'); B = gzread(fp); gzclose(fp); fprintf('Difference is = %g\n', norm(A-B,1)); zgimbutas-mwrap-04cdb66/make.inc000066400000000000000000000010761522673526200167030ustar00rootroot00000000000000# === Compilers === # (You don't need flex and bison unless you're planning to modify the # grammar -- the default installation comes with the relevant files # already in place.) # Uncomment this line for new-style classdef support OOFLAG=-DR2008OO # Uncomment this line for C99 complex support TESTC99COMPLEX=test_c99_complex CC := $(if $(CC),$(CC),gcc) CXX := $(if $(CXX),$(CXX),g++) MEX= mex $(OOFLAG) # Use the following for 64-bit MEX # MEX= mex -largeArrayDims $(OOFLAG) # Use the following for GNU Octave. MEX= mkoctfile --mex FLEX= flex BISON= bison zgimbutas-mwrap-04cdb66/mwrap.pdf000066400000000000000000006037211522673526200171210ustar00rootroot00000000000000%PDF-1.5 % 3 0 obj << /Length 1841 /Filter /FlateDecode >> stream xڍk6;"39gK3pS:[xH ﻫ]9_biKyuٛHT(Drd"TgiTdzr9x5?;<!Wg" UvJfX0SE&<vUSɫE' /d EE+K* ^UuiIh9tU {sخV|咯\s0ƽ47웪*u*5aARWZV23{G44T!WP j -SJy$m ʡьDFΤz!H8p:ċs;%i`kQ޶KSX:mMKfDw~e m ,;P&xЖ# Ug*h&mi(HNsvcY|H+ˠ+ju(㵅И0Lae>3ΥGU5fkYjX)PN#B.!DlW .]<}JJ+%NcC][dfFgY _Kl }MQV'uoW\Tk鼏r/Hmu*Ǻ8h0':,-b6{=ΐ:ج4o!)ֺT= '\ȅqOY[/oM{y֞IΉfZK֔K cRfbr)#Nh}ڇ.P9F)cWgq)W &mQlZHU+o}Q i ُK;D)-m!ID|>`#Mއx*b%@Ff(c•eBBPA(/.֘Ҩ*~`}*Bc-hӾ>D#ﻝ$"۩cD:2 4:aU7COIY CC'T)В/sy}\5ú;7l3s](ϘA'G8 /)'4 VIEq #9vUp22#p +N۪) KX2-T¿'/l{7"H6"sNz?B$ endstream endobj 13 0 obj << /Length 2373 /Filter /FlateDecode >> stream xڭko~9;$EAq,ii}g8k!OJl`1rsH+81,6M_n~pX`RMvl8S<9FG~͇߼4g5S\ob3+:﷡&5VigPhuDHͩQŸ% Mp+(g&لR3D[+ʦoooYnekH"Y닖2%N6mMu$r y"yȏv ĚI 1^HݥYt<Kn3K+HG`+|,M{'eYDɝ xy;~O8CPcܢBΚñ$QT4 %7Dy,=NE >5Yׄ?*Hs:ݙ~SY?5r/&,&@IZZ9YnzRRq tq(u`_Bn'G ̣W`U50'`æ-=3d\Ve_* Xq;Y_b @އ8No-幣0{ T9%@l`*6複iRufE$!?bObRiMh ;Iӎ]SU֩|҃÷k@2-}n,srmsꏧ|FxveӖ{g_Dª{|Ygv:!I0kT\͎95o*k A;K K3֬f}&^]LѲ|g's3 AZ1wT\ȅƐSPlIpv 6EPM c"6,m E$*Hk& #g4NFD/@qntl Tgʁq=7Ќ|tIH dkvռҫu8I%KSyO OڅʘB։j^%jvATz0%yl=ͽcnڋ ILmG1A9z$Ѝ`_+Of *OQ P8G-o?} IXթ/[?aȫ rH b,4yٺnDx)p} aM^Ny0+6r#SXȘ ML?-8lyBݑ&H؀*9R]zW>8\[@Z?zy$^8 x܃Oxߡr5u֜*D~|/h/˺&k|$1|fhnz[8&hv(LՐ ɇKBLP+@T8:<%Η``~A{ #n 1ct1LEc_r&vp__'6<-z)0O3_- ӳgΥfEŃPLk4C8XHIOcpvmsHn5\pJX4}*; r`M!B4t_G^s_>b|䆟tu'PWFܗp:¯Wtgޡ^(/U\j"TP^qiP( =:t/z"~tdB]ӊu o.^J]/mھHB-JXؖSi3/߃6X&_ýw>%zr03uxAx= f,@t/]Kpd$P&}ОjXbϹ+[”]t92P>-fUЦ8KSGClXB-PrOgMck6kmV}r'V-kݓgW/Kjxknk \B^.*y?kBjWe_|3S^c%*2C@pr: %Tĸ@T\}=C7X)X3 iEe ^c݀%_w@ IJME+ô={+ܸk:xkk]-V]@v J¢/tN16c\إAcц1/`x{3ڒ\K u?Z0Cթ0 M۷i=\ݧAӋ8p\:ŋ2d= \wIB` jQMڻK"8*gaiwvwi gtd4Zu牧G݋-š%x0~={S endstream endobj 17 0 obj << /Length 2296 /Filter /FlateDecode >> stream xڭYYF~_A ~;x$/  FXa1mSBRO~VuUS̙@쳺ίZ7/*` d&, Di2x͇7o7߈ R D$ qPA< ܪc M߼,L&TqIi4/f VSOM+9_Q xfeKqUʼn_6 zMMH'bCE~K Ϙ ٱn?' gdؾj1;\/=7nnVISlDZ'iE&ۖkdDE'[1iqq 8*O5&d'\kisGB˹ѕLB[H|bC#M-u={d+_,d"d,i$Ѥ%$ʣ\iBmn&WQ$wR0&~ӑ҉&5 d:(#QhiHjUHOF"I:p2uWEo?U,¡LkDZnG_jx$h F؆@0҂ko`{[;2ahq$ nI ,"w߭7p|;M F`uA}R)pA+?[c =C5T|Tۻ1~)`R xpZGi i[Mf@FD ߽ٺY-O]ئ`C19(R)$I3:`n\uSjlO-މ/'ni@{¶ +Æyft4R^0cݵ.ygjȔN\ iSvffxYVk1yh&_D_oM Ḩ(yaLY$i Gή ےYtKh.gPI`xgeэ(s@BkC-cj'I@OGۓ=GB@*y 5^s/K!S`#tf|3R-^ ѽqo:9wLec/C6ื/Wlwh/]82F?` 5L+L[$ȤO4wūKP7Lg²uX?`S";Cp7Ù endstream endobj 20 0 obj << /Length 2738 /Filter /FlateDecode >> stream xڵko~k pIPb":ٙCb]w7WkRdQV7JHUE"Iv1_m~\7W_Ur(X&2Ѫ8\}i {2q jW~j"&Y*hhm[AN~KvL-z)®epM50Sǃ~oWM8Hv~n՜jRh.ۨ80P$} YF޶@9޶j84wqQYk8l4Â͐99wNJwmsX#l< T##XMzL=Qsn0JbRX?J~.{[Ub5"423 PPܓͲUz8:H+`{ZA)ȴOd8US^A5OR긦ow": Pۖw 6gxFoFN,}荦kۼV5-$>84'"M5AO:Eu"3?thW4cF mr!B,vd,I:ZN2 rBu,eZay?ENu5%讠&z2jsz 4`P75 J\d%۲` P^jǀ߾~@)MG;z5{<U_a0$R6/DG>&$rnhn~7xrw?KpkMhc<)۶!RsΌo5{4'hPIe?-5hX AȠC^c bJ\gf$v1x`)T=0R1PՐz,%J`ZR0r l=.zԦcc(橜t[Us(j}> h;KGMLL,m4Tƒe7ޭ+/q~|:GqamŚv6C͋'BGU)ēGB|b@Nnj;>_26/`$_4㧧\hXd MWS@~ rNJ.5bOӳ1Itç KD(-oHX|6 'A2'x9_"S!^k-O طr[>1gttp njAMFtqpIt#uScϭfRTNIDZc1Hf q =˕ j<$䜵e"Y?RRg&ώGoYFgY^(⾘>>XT&l endstream endobj 24 0 obj << /Length 967 /Filter /FlateDecode >> stream xXMoFWTHE9ٙN PES=>(W(*}HҖbǁÙo v:zq!1B5Esut]ͶoFYb3R:FW"ZH.ZZ"e__?Goxti m"!.*YOȎI,Ɍ_YƗVYVzqıK$!^W6ݰhMyijA_V, ReB[_*,F@ JʺNy^֔v}\c~M$֨*zO)o=5b"BZ@bjL AFA |Rя|11ϗ.Q*0M\ BЦ!d[n{^C^=SI/4#;^e.\/ei,r`7E=z͗:0O7wS;|~e Dj(n5^C1$$oJMA`҉쬯WzC=wKR=xEAlBXgz4@$ۤ\RM}􉀤[9D;P,ĥI,ѹFn[T8W y1ւ<f~eCd-(ƒC kk6r RsnLih`p_t}–fvR^*t!HǵWMs;Iy4'GgX?4B`yI5H?>7ͣ]kK9bu>[.+Sɬ|/Zsu1- endstream endobj 29 0 obj << /Length 2840 /Filter /FlateDecode >> stream xڭYo-/kl 0('#$Ow^|(Y)kv{uqVJ. b҉dЏdu{oۤ_qqLSX4k*7.YҬF |wwg>{`z^b}U~>&Tb/2Î:"{-t׍ҴF[*m#]ڴ؉,{'"gr2>vc5 wϼX3E~:` XQ+ux"ݺ8ZO.A:m8e' My]ݵkʯt@{i١)0⎇@m²\%~h\0bb[l$Nbp*50fZ`Ul%ӎ*s$i/g\Z{aP:𜙗('nlӭBpD [gE_McNž;Nw*бhk.l}͆Z2E#6mSeV*x\q߃0XO$â bP-zF}bZ3߁V]27V4Z,{k܋Bd0 ݇&LG]U^u5xzȲI|嶫iGQ)2}+yMnݻ: S5q0J+-ZqZ%f !~X5n^Re]!QLD&X!JJ@9kȂ_>nă{mLϚɊD`2s %,x`QRUwIwQs8 $>HYZt]O_B^˪`s144XlYX]6([&n-Y0\E=MMb+"kj JsMHJH/t,{͚lwe!Ci 9VYGWB"+P"`"5ŒTVLQu#b8nOC0#!sw,.2ysfHg0$ ؊H)NAܽL)d}׾}W>pҦT8J&̠fpJ$B"DԏG/`<whEyrOIsNM@}J CtG?h#Hzf |<@u[,Hb-Q)$)r{>ϟQiػ[9?xCSw5[a ԟ$3ؕ)ҟ.s]j GF 0QF¥ b**)^NDyBt[c\T9Je=v=U$zRsHcQV;gAq 2 P}?6 F@(d4#^(zdHܺ#?l& zSfLGu[ɿ&0JQZV-غ 1!Uu?/O ̎56Ț;Ł>S!UQ,pZF9v mWZd7:ᴤNdi[_]y@Jݼ@dpuȢ2#昁hphm 8cv0Mv8dBlevƆ%o2@% +2GVsź<l\/0֏wNY͘uT 0狌ŭBfj'; A.'t|a #„7YZh>y;֦Od}r_N ;R07/7DDjڏNJ3" HMhHǀXT-c<T#4]}qdRXB 8:+Q <\d *ch"ky!z#999?VC݉Dj 1.i?tsg!pQPF=zM9=J |`l(5MB꾼Vwb(D"LV;g hO,+RnkgkfB&v.L32c)YΘ#'|cjI3̀1q{]T+gO @.62})Q hy='ARqD ? ?/y˿A] gXRFzϞ1Ŷ且?b< S9x]"}<$ deEA[#jf[?sӿNYѬ\RQD':9/G~ʓ&F<9I'J?K3^ Z]P8ѲW%7V?*sZ@PIL/tƏ([x7:u3շ0,_"Ō3NO J9'.|(Ra =L i+0gĉq_$jKXкp*r(RJb!T$;)[1`CtlYuOSDw徤Z<ݘac؈o~&*_)'*I$?D} blZ/cWQh}&DB VϞt f4!uRA_,K90TLQć1Bf_uo]t3@Z=)o+i" ^C+w(;~ ˛l$Jw^m3_&Gh@ hUNbHਪ endstream endobj 32 0 obj << /Length 3108 /Filter /FlateDecode >> stream xڭZKϯPU+>&ص&|}HЈD q|ERpvǩxn_jT\ZW.t\q;毷7ިU?5tS6v=SA7$Nb@N+W WSz8$Oy-F)mP-W&dh,1i8% .(ݴZQ*.|8UB W\dE#FmcL?-eᧅUrjclAqo>,-RK&WkoBK$KpNuzq$6ŋb}V7Ȣj rlt^FkB6nwK[R3[ {gT#^ARꭗ%ϣH w"M Kvv7z*\-iN^ Or; -$s)Vfr}\P5b.Wo_ ݾ{&WV۪;>j z|c@^HNTx6>Fu"oPHb p]r{b\{UwɑA*MimBr k.p0,AY 키u W2<۬ˬ dз/q%<$:MeI)fI+I)$zIiP^PDPzlZFAAiKiBCs9ܥ oD&zjE*Ro;x(\bl2oṱiK'b"yeQ 8 ]?]~ղӱ-O8SYjn( @;pqvmrsp g(e JӸLCR!^4 %d~mhY1!$Yr-W=TA0£?ʘ  dl ɳOgA@*Z_N[m^4a9D;O^{\A qMwϛ?*zey 8iC_Z{nUX6'pp:0JG~JK rV7h isIIl`iV;w[,0?X'l\+ Lcn;6 aokh{B@B[(^%[8.I ~9,2y|֝(C͕Z<a]?Is\p\) ktRE2w&N_A .#J p.hJGj&cFT ye VCcduOK.8^|p p&!VrBLX'3IH3?[!4z9 ]%&:iXW2$ T;.N+`;CҗKAar5{YƩIw=~6 $ =Sf>2 МY;;ZE;yj\#2 pѰ6p{eapaXT!adXS`Zɛf#f*ɾ-{D"`vXoqAiէ}yCzq {m1SA fO}QDQFtkXQw<%ڇq?̸{lTǺȞDiҿ`$n$8ꦯ a[#q;yWfqa GqHD8.&Imqjv&Xd)@F@VBKco {H5Δ8a!׸`Ff#AډjH|ZW7Z4`_·H@/̻n ,#64D{8.<|2E,$,ASӛ( 07\N`դpzXŅ2yKr-`wDG(l'=ruOjQziqPDuـٽ#X&tDs1;)o  I*֍UcW+~jy: |`^ĦS /"@n y"чY1^*L-WO cmCh2ت_:IfF6xz@}l#$L>Vϱ)'Jg b`|dﶌu6T׌-LXb/!mN7Ǫw$|q* 0.ip7sΈ"D^ѳ̦~&l]>+koF'鳖E߭1 aJ,5kfi3Pb ͫ-s 'i_XbOXf/d.xxd`_0}, n03 o&Lk]_1!( `& endstream endobj 36 0 obj << /Length 2326 /Filter /FlateDecode >> stream xڥY[oF~*}rh>E0GTTls"p8˙s9ԻZ)iWw.tXL=y1պ&Nt̢Nζ כ4NBݜ5r6U῭~|q<)#Iƙ)"O-?3{ZZk2mJ<;o)P7p]ܞ{/jD)L6* $8YDXY-Tb7<)BFd^?R9c,C$ nׅf+9V9 hMaFaVHh|L8EUkQy#y'[rn%7Jq;mu>v~j{6`MN0"kxoMe@Q˱GaăpBy|ن(pz9x8+,LZ$khd x`1C,ͧ'!z06gX*Ι#0Zp{(]+1N2O]Aqd`(ܷ!V g]OBDe:nn5Ц֡ʄ;sfÏƀy10 kw=G?)Ɩ;EyBR+%x"!}-< J :"2BODa*T *!zr>2#LASoy9d :3HD.st(Qfi2Uǃ8 +\߶hE> @O<;<w`ϑ_}}pWFM^y9C5q`0`-W{ҥ*_aZ4TM M`SM"T.Ka٢bu+4=+\RVŸl|l  "'AjCӡ N.nzi?iqܢ ]BEI{ ZiM͌bFVyq*kT ujTCWF^=!WAzT3 ab wf2>cpuu%S\Sǯs*[y:.qB7 򮔼 :2Os*ʴT!:ڠ:15YE"[\QP\E,HS,/C}Q});6 `b S}#g1bg}{#ͭ6%s \xa}7`?u5;xݯX=dr nc] sYBKfE"rIahZ8,SB*׋s(s.?AZP@kPk{(xΔÓkC 42טB,PHJ(CsA'^Se1n1 N e*{sYt.FL lϮH縈> stream xڭZo6~_=tƊK''Mz-;Ň{zJ%:A&!goȼxNQf\lW2Qlf"kMe[w͏oo.>\Ube%Hj/~5^hqG*KW~~ez~=y^D*+kl[JgfWЪ/e#,'][}4ʻ{Mhܰw/HؚG+V U¦P븳*lͷr3u{qб\yY?YP,>9l@ b\~0kYͬ?5=hn%ByqAm7Y ‘hoML:qVAzaOmYAEef{\egM1uۺ%I5"F#v׿8_2KX- Uq,$]Pԛ $sYYF;Qa8х&J&V1tޔ}HE suΕ]f(S= E|llۥ՜|m=7|}Oׯqep}Y:cۑɺ-*Da2X :g ]992=zзkE -(ohPRQ!,&gioHnD񥔑c%RG6}si5M4#Gg Lnӑ.=xM0-0a֛Jś6`I 6#6h]D[W/a*L gDz'^\{HIL,75LsAUhĂFCAb6+Ls 4J$w䵴'  ƙ1>OEףnD=K'>/v4(`~ Жb~0s@/QBW/wD/^o%uJ~e'ؗMxOo``,ȅ#-9)%߿_ڍF!_چ9 _HglCUd&;9Ǖ~\Y(_`fu !en j?R=E8(Gf(aϩS{mu<7 (q3!"đؿGrʼn׫%Dc\{KXƯ}G-Ȁ':)UO9jd-Gm"3rVxsV[ 2iЖVU|Z"J] pl}rqR$<Ɉ`PtU֝O+ϻX!" {yEL K ]Uz31Z-9taŃ#N};.E:-ew|W+?yѓX}FÁC+3rHM)H<"!>8|CCwȎ[oe;D=' (,w^[]`BOhXU,fɔ=p<eQx,H L!F\3ipu|Ƥϕ(N.1z@<n ):X^0Li);JR@Jmjkg"H#3aX9<[DpWܦb|=A$JOiM@$_NJ K8 ;<ȣ@Up>)*!\]/}FFs~ X/3KX'r5@(YL-qb)_نpހ 3 9=fx+9di!ô _N~ {|h$>g -| &드Lsr"L> _c r55}8bٚ2hweal$HW.8̋ٵZ.Z?RiDռ2/㠻5.j74k#|)/dx"/N':G)af»Q/B"klըKԮu"&~p(p +CuYD෠.,WhHL)K:% 45)!ñm mUۗkAW|(BZF֞$L9Prp>S Yeh~Xb~&/<;E"рK)Jc"ϒ+sGC@]\:ާDCT>'¬sb/@[Tԝyܢ&>_T-j|mw3aL|r1ޤy灱>w 3n- V U7K{T,/jMPk8YTеePn[>]ЅG`(\3m lڦ endstream endobj 42 0 obj << /Length 1595 /Filter /FlateDecode >> stream xڍX[o:~﯈CĎ -XRw&g8͒ҶRc3|crqYpKGDEƊa,2N6Ǔ w+gyp.D87&]L|DT#9nA,i޴hl%^`zC~=VݿskD> +釴NmU'j@VIW4l*yȔ>Hy >8n(|.Y=@sH<뒂u{kE4l6`{o8zc"s͋zkJ~yC&LI,,dRt4$@+XB 3i%uC3 Μ+%JGp˩T)yCBʥE44nחMHZ{#w7H]Ӂ4) DI2bJ`52r;~b.z>LxQ!GI?Mn   s ƣ8Hi&@8 `EHyF֛0d bhݕi)P+{]/ԒFCdp31S!7_L =g^B/$-M)6٣8ņFjJӸu֬Lp5v5 P nTDL$[m EĢX؄W5I}C$RdXphdzFL6m;_VVaoG &l\ HĔ`{$&!yBLߣ`w]H8[0d}2yJ ^L,MQDlHЭ54+Sah?'LoƋz.(vo 1 /cc22UZW9PPWzǨ =KŠV7HRx8MjOR }qk>tEKPF@ ^<5{{ZqS"Bp;H8jwn$%g< WZ$>Zcž̙9RR:Mf%~!%7Ӛ+yJ9*>P1RVP{%CypWxVjeh%4w|T^E&i" ﳸgvc5QLеeKakԵ.S[kIVߺ o#yW;|R %H̢{}4[ endstream endobj 45 0 obj << /Length 1926 /Filter /FlateDecode >> stream xڕXmo6_afTKr+{E[\kd[l9颸^R<㬓E7Z")(>Լ⛘\)\",˂4W,M ~aws۫ DǃTgBl/oA< T$ot's{D,Jcn 1qfXK?X R;w5.I߈%R}|OiC_W+Y3;sugBŃvTcY,Ld1+Tfm2!d^; ~eU/*isۼs"4OHFL yNly~ zF'0OR9dE8Oɫ < xDxo.PN{?Nd٤ MHuscN؅I8/z@ = ܼCfb}"`Mmt~71>J1∇Zli"jYπ8C]i;S]&Mz-J϶[Ņ%&ֆLAS(Ag <[T~vP4o4ީ,,+&b5 |GNrH0;m?Ҡny2caj,wD@ߪk$8W$o@ʇ>ms6Pas ON%^U_Ҭ}E?_p@&1X_s]\}u|"E'] 63Ϻz p1Z hG@˥p0n `j w2hHRcm4ZK,gԍ.,)(^.O+i*#QEQ:QhF _ݍEZd,67VM1"d9:'V:W@!:=N$R/VB*uyDJjD[gL ;{z?^hˈMz<^ʦ!ؘhN:8FGRiE,q.M?NĆ$p btb%Е#Φo؏G9G: ڙRoEDZ%qEo`!]DH7၎|-yrB F}GQj3,Xrwm)yRڶ[| fENm@aRd(K}wJKWQp(O_g) U ~d r Yq(YN[B,.WG[T4ESk$lxR>Łj)mvTzf[}oDZ+ qV02+[֘PZḶHw Mtx ʾm($䟀GILJ>5qO9Z$r(VwJd|E9|8IKd@,YhY8FQ> stream xڥV]o0}ϯi -$&P4U*(6vZi}J ]Bh=sc7<E c~/V`9nVn!/' ֱX-p?ʊA0|k/pIB7u(pP`T%dӍ~.9Vzѡ 6Kl {Ⱦy `i>hEVA {P?5OJqWcAWzcNhE0{Φ2K!XG2ҁޭʒha$j|#vUȃ"h-[)/ԪӵL,|J֜fӇKM.vŚA t8 9s[ }nRe4UEe AI j ĕY QZ{3F<;$v1G ƙMGPsfP2n024a:,^mfS6ISx֭G3jz(W}UWp IΌ S7({`mFԗt&d3﮵F7W:l Y{e<,ǒb-ڤf6INR`,NZѩ~'₝ |cϛ/زN]~[cGG:1Kw|)AZc. +jIR endstream endobj 55 0 obj << /Length 2349 /Filter /FlateDecode >> stream xڍXYo8~X@޸Q 1 `!nhuQG[ b"_^|*G)/=8Axi$Y%I/]~zśۋ $6:rWW,uvto|5yOLNQIr=5L3# ~6YWe3|2 qߎw˄TbN^k՞1az2H4 ~0I+$7X)xQ> Xoe -Sej\;C6Lo a,Ș=Mx^v̫EGڎqhk8KLNǎW,I }mO)>bTwi~B31Dru[8 얷h \|Sڻ{½zmz≽`="D zD6V|? 6[=ʾYYbQkcVmaCi`~i.3 iWMгʶFh`s Ƨ@{^ '٣CD#B kCּF )_#fG <y7Lj|_CP!XXy)3 bt`-(݀04eSo=@ e-8tfg {R#O@w?yaGĐ܈8(DGۅb#IĤmޑL} ";;#32@ƩaƖ*㙩T(gv4أxMM0YpMH Ūװ:q "R$HM!N'.A·3{L6x㘂ro~hGBylQ9PI `TbpN0`-wDfF5E5Yu ',Bnaxq9luD"9ʹGjH |zSUti-Р1t[cyTS@v yWbDV)aЙ]c˵oyrmS/~[IF8DST;f '@nIi,|0rTnjB~VKup C_ʯ8gA~ \ܝb1P9j]$^!ZpbN9OH')+Hv6߄^IXd,2'N ` A;vlcٻ 2]ؾΤJM5G]><04tt j~a^ a lgi(o ]7!"t> stream xWߏ6 ~X7)/+pm+kwC }pl%1ة~H'vjЇ!(Jȏ'ґRQ;KO|$J#T\{6>^89>|"~Q>IᇑoN/OiZ |+_'Opt^(vT:/=< L)ʋ`3k5R_%Hx{7| D#GPQ27_BW{Ӯm4fWY"KRf[VѮIYUlˣ:'u }i3t̙+p EѮ.tM;1GR7-(t3?qI~dI>GǨ d&FW_6-xP>8հv4 7v׬JXf`K~,p@? 8ssͳWWgr ? ܔj Hv6$hsPdzUiAfi4lZu x- *Ip2c""$ G@!0"a>0<竵 ko./ޑwFo:AGpky ECHPa)C^lẑ93x6X\J I1fYĆʝ/O޻ ৙UZPL ̕})Mו Tz; Xط.qՅ^"n ߖT>Ҵ_7ug]nG5i[.3e[V#"lw OQؙ&K 856-Yln+A(r9]K2Vz*A(Tt<27%+lQd=W(x6HA/Ey>Vˬe4fml23㥍 F@`>"MZ[:27F/kgE Đ4em SZĪ'H?IN?Ń+,7fr6-K50<'-*9M"wf0ŘoV?7C돤d fG1l8ޙZai A3 VEfX[[ KZ!dFqoVk~%t2 i/gZ1L-~)<36hk85N!ol&ѝ[tSniHk#uF6R]* ,=?pn`bD 0](3S">C u 2XH?A^,'Y rswς u}Ƈ%pjkH=u_#x[&it"_/e­=&e 趭ɯmE4;챘laIpҶ|yDx͈&}EsHLr5 Vjۊ9LؤjV4kz -B8C0|7@. .;X.=~ S&HZTDLl~Gz7MwHjc7=g)@D S@pWf endstream endobj 76 0 obj << /Length1 2081 /Length2 14287 /Length3 0 /Length 15540 /Filter /FlateDecode >> stream xڍP pҨ)iunw~ceD;]E2MTt9T5ԈS8)R^fL!4 Yp:}䰼x-!&w \ SӡFBFSegA:.(U Rsrf0ɫn)z,YBe\١F@{J 9YɈ[|潷yC4)*.o d;[Zjr…Zr{{9}-g/-uQzKe݌J6 6>q:FUsu2-_mU)in!际ߡ+wWbv[;} l;}k[V3η2<44 p4V_i34;sz|V$s-Tc c !K@樽+j#5fp@khZyb KjYˁ{[&"2h C-C%uy-FWCrR^#k\ ,4T <`QJI`8ztw3oI•+HE/ƥWiJ6ͱ;(GV3uU#]vKR]uroUڲtmc5Y^KS]yzF̸ ?ot v}Õﰴm4#eb؆{ߙkFprX|G0=х>l]Omo4;X3l,ۈks>EIiGoվJ&+g`qk{0mAVCF:1.QXAm%^ zRS .KB!;BF #."wN _G5o*(Ndl: @oLVif{㙌`I'4F'84baH@" Si%lmqEμ=?bS`)9XyYʠYщc~# Ԉ~.jFZViKPdI2;v6EFOa77hFc\@G 1-O\/4a9U=]I_,ū h6/|a>9ߙ'34PXjimuCɚ1d<9k.)DO, .>f^(V'wId3ϐ9,A@Cd~%{ V e -vp N*yw'eh>БаԓMV :Dêt:"_i.5 Is;-[ݎq,l]/4ʞ9)[-Ee?ND Ć3g=oI# 80esD1]rJEQ.\t\vb,溒د28A7iG`m¬F-ĘRcfv#)VRzqgN8m*~38&U]3|}#E[_CN2>cgϺR^%yX/ Nde}˺Hسf /wW99zTE sɒp1>vh}ҒW3E7f |G ka˙NDd>*qJ,1io|-^D䌩u sKzGPۺ5RO"]$GߢDON1UKi%1 5#v<ѲFRY E@!^ Ƴ[: YHA}d([#p[fQX eok@)7۠[MzɎtBT,ouTkQ'q:9OHT Da`tx^j(u,]<ɯ&w!I݆T#mv~&c|e,6 ,GMA#YA:rԾqNXKTڳc>$ć/ X?Gk_4h(}^Xh*TBIL~cYW28A>9t-Pk+b].ʱ"R)#_{<`?Ӧ y. æҎF땼_OBv7ͱ?{x j)NQEp)Iɬd $'lAjZy{RZRQ:FJ˛ϛsp͹wK.3DuRn__u<zkqęߗO6Q%_~v~p4 R(g=n/K:C ]Ͱ>f0( ;ߧO\‘@YCWħeR&GH) }~9w3u!p1iQ϶ 3nWt?b3!#??닊"th UxA/Z$,>W@ %zPm#XFyW / _NF+ C8 wRNg}ǵn=Ap[fvhPޤVlfP4H38RUƢbONuU@BmD>92"PS8ka-&]hu={\Vm@ۧZ95H9eÞ@#^ p:^J"ꀣvȚRWy-j?u_;5)5_NkȯpM.T)LBb7fƙjuqcūOfr9//nƝJo$K|O&zxBl/BIb1n[Ej3N@jhٖ_J^`lJxcgS5 _Wȱ־踌D{SVFrYP/Jj%= ೽ӫK,\ 1gV-ǥzoS x|Bmvcg݊2z(^m<&LSQ@gfJ0/a߁/ɴFS!ZWl6Q)ճ=x}`s n9ఇFhjڥKod„ [^9H۾P\9]Hqq6^Ub_r׸""L`[_T9qشuKwT$8Q)_n5sC{QT>/5*H>.S6wCϼsL n[wN#H{󬌸l9ˑ^|1昴~ K-l{u S5?=ҕf;µJ:L} ƝNw#7$nz#CPF\GIzv#6mڶ듌ð8[U#A9: xa>=*j 5Ҹ`>L&&77xȊF$wU1Vs`*o7эFs, algmxٹ4:lwwY߻|$7d[LafB ;7Yîkcbw2[P;&(i J#Sӵ1#XSQd ,du4-1[|w`{Աa\,$BaU^MJΎ{IuTwɫε[1ϛb8mMdr(^*N |uU>g نy=03lPD&}"(v"ը$+vǵ=&^zP^(%"S!O0WR+F8aI☆~==R".`l^PGŰ:Jwت{ }⤋j[7I ؁ɚ|g~\idd<(bQ{NJsfUWg7&H@)XM.]esPQF39ߎP)l,'Ml+_b[Jc6%!c/᜹$s+z$H]ުLa1Qx^CFNJ<ŧk2Y,%c[O33*Qpֈ6jFZ1]Zŧ?ch- I'T,6w꼙 u˿I}gjp< u_uBUܠTˡ\&sF!Bi:B12GsFL^QRQfU ' xaJo v߇|;IbLBMBa12pIBb83E/Iֻti+Zϥoc7X5pslm+$tO>LE~{ӏOߛQxQ'bkl1;i[CS %7p乛`> [i Q}Tu ^b@dMRm-.]q(қ67єP Puty8+ qy -HA0Z"<}9+G|f[g: FqafhI(-#*ra,\Sl-r9^Nnq   ګPc^<q.2K.pр wpJLCzo._[7Rq͚f %t'Zu\-.I#b&h䢒hIQQ4kC-:ObXTbF&k炄w\q>ktkp[} Ї{͔f4zg{C,sw>,FQRaNI>I;#4$Lr;o7Y,/nKe!GCCIS})yl3ҬqTE%+?g] X <] 0 MQoѿI)~[tf+FQ'GCe^J5X9o͚"oJlMubljKH$47SViL؃Z4R(F R4p}jUg5 rfVm`$ $=R ~L{0$ˊ AY=Sai)\p-șpIiG:O*D\i_5<%UkK7ʚʠ||LgnVݪC&_IB<4,kov;,.pܛp _5&}p@e(=YNutoO7heXYlސV{ko߲TLs©w=o`bҗI3o'Ǻ5++e祢Ҫ:r52L&$}Eѓ-L%݋^Fʬ5l̙魤Qoȕ@kR:*Qd泓|Gj9$'(+zSG v Мjza̎V3UU 'S۽VB$õiW|y~#߾bLx ejU [`d.HYѨ 囿}04ҥ#Pw!X4c.(] @ko£9.VtGs ϧ0Ұ|/:xbW$KӿYϭ*ަ'-=;[èopM}f$XK#OlۻOiy)3}F]pZ@Ku6=?HdldGUǍDXsVq xȆTϮ3Ax }4N:<ףW<,>ؘbVo, f_/p;X0ZWt>ᛠ;ԫcNur] }WQ.E0W/@b)\U%iqxс76àa"rBd)QNXnWV;-"Dx y8ŽIuGYH)6|J40[D>u S钎&GCwM[ez vumDR bΖڜ\_" ->4>Wiwj ZBt C"OgF)8:ܚ/v `!Oc~u ͭCgEC bÇ_̢]zA/Z孍X^}o8hGJzdmv"㲫PcXt^x*o$״`zP~I ddƥ{V5nJ+ P6sq7 .vz+i.=i'LA/a! w\|0cL*辘WJ <69 [G#3fXEQ E]tF)Kk n*+J $*3dU[[du ;Ó5K9: @/rJ@įmW,cb9 WFmU7`nGxMڒwtQ L;]`^z@Gz+fגjA~s|!ʄQFXYKY3G9 %j=>oݚhLշ.lX2nlߝoLr1E1kʹ?l'@5\oscIvGZK]Ű9Z4ըw4<e~6+[L3b˿ ulT/$P-hk?94+~=#͇@mfe>iP Zj1LuM4h= OuYSpy@DnT@;|.[..ʤJw rG=O2|ny†lhU_8'4vt_4!DtMJ(EG$IpGjPR:^?YrҁOs'l}y4P0N!@峔~yf%+u4s(^Oy9EKR0NbԱ.iiwS]XZc3D ]@,e0i*cG3 <#:dD? §7J|>᳿O;+\)WɄ[H>W1mEk'7h]wGϸyU\deE:uXX._dFV;Բ8~#LIy]+1M<'BvD՛64`I҉yVn"H>>Kخ8i%$yk9)#B}o;σFkl]`[ /c`;ڨJqYwn}|S٢m4SWq|!=Lۥ׊'TvLNN?%{jX|4!sB5.(/mBJBB&Ć.'9/'҃ԛHp<э梎K̂V8TCj2t8u0EaK $_)qEWX E8 ^88 j3H2, ןXS#9gAceg{DAbD]xws=cxQGV(7щguXvNbÁ̃Ö́D*?QWjrh/ )Wz[2^* ٞjo:Ds.k]ƅ,*o30K|^uualT,ߒw.L72\3SRp?$=l0J ǩSH?doae&ED78̳hl&PB -񜟆.wpI_%~bMyJU(¤uYt// a??j()q{[4>31xt8 i^B'o¢SyA¹+43ŊO07J/9z|. cLxPB$;85LX!F5WC\X/N3#oڀA6)Oއrhp[3`GppyQB_b[x}f̺'ԐJiVqRWa=l \|io\ oRLlPa: +s>5ݖ'Ly#1ykBtaNYBYOgA(v` xɌ 7&#\K\%9.WFm-|"FBɴvWUSTh#P6TVdV"ͼpJ1Vc+2ߔ@QC>[ߵb|G b|Si4g9)ޭVpqΎTY4wkBq6ړ1I3K!W܂j|؁o;xmfWA,vê[IB[YfF(mا ~UN@l1ttD,o`@Jf5_aAA+Jk i;  8m@AJf:~̾lN\.Q,ԝ PAmt{Qn&>=`me)iH ՏTJ#@7%C :I $gU,_ϴ9:2)L.r^i\郦djd7~asR]pP)S<+^4`D!LW2QD=-g{_(s&*FFJMXhr@r՞UO)\ m(ODNs9${cN8{˟0YG̦{wuSlj/*bqbY霰 ";8Uo4,Yy̋jweTcY8;C]"ϻr|Cx ֿb -m4 7|tg؁x{ڻJwrȭB śa"nEh超YTfGvlKi.3K1~Ih fh\m-ekK/BЬlYcJ9Y+*lt$,qR ~]n<=h JSHqNDfep ]|lEDaݐoۼ'*  ?Oڼ/#:7%XIv 9m,%1l|P+L&Ykh+zy5y ,K^Y"DNzJ}it?ZBa4I8b=9XPB~1㴩e '"0v+ˑwjŊi#wK̏iu+f׉{ʫ:~Sj,qTmsڡ9ÊMouZSΔ,{T!dEtI9qU!'DxbK7s3n/+Zq6-"_q8|=Ukmvk|@%p/d +T@#饬-t̢po?uX63?6,z5슻ƥ 3p6nԑQYNf \Qo%Udux\f9"KҞ| rN6u+QlRqBP3W\iS"ʝ. yyN+?LzM&;+gt0E};Ju2I)B#:1.!…EmWjY9Zdt>^=x Z\2ճsZc_h")GQTY+iiPXlNKUD£zn5rn #Y)i΢ /eI[UKޝ {1]`12O< |hǫT]7^"du8Kpu}[DQwnNW_ӻ`u\ endstream endobj 78 0 obj << /Length1 1453 /Length2 7143 /Length3 0 /Length 8122 /Filter /FlateDecode >> stream xڍT]6LH4 4CwK H0338 C+HwI#H#!Hww7>k}ߚ9gs}H[:TuUT PT!2a8HLa(78!_U X.vw%d%e@(7<cP @r"]Pp{4vn@XZZp ]0v!!p_)hW!!A e07 ~7 M `wcC{Q0 nw`iu$:?# ]\/8`w=0vvCb0l%Q;lc[A7 vt;nRw9!HFp =x/ @FB&SwփHXl04@(%)`[{p 6cqE찍v0Q0?v p` #Hk@=@߿VXAge!UC#mu{ǫDāa$@w}0:+T a^A]3/p5$<'Cb pGV@q !J.-I_w;; H@ jsuaPz`H(#9K:Շ!jOys#`H7' ;d'3.vo ԿKPC@'".P`/Rc8V)<6@HDcCvvH[*DH$Ef i,EBB? P؊ڿO !ͧUL#r"#OjxWw ?qlSyz8K kر=m̯cܩ3 8f*,2En}6E}ι{o%CZڪ޼ҡ+mj~og.~x~KLZUx^:C3xJށT]ژh*5پĈ*M?ݺ.Sj(@Ki}a^%Ӿ .ECR@8 N7] [E^dj5|!77h"Ro=d4seŸqoD )1jy*`&N>M]J ~NO \%Qe^Z旎2C7wc8."MmY9Xqusbaf\ھ#xIioYgSO6(U{z $sZ/7;8U_/DI;lϷm嵥ÔIζo QScpiSD CqeJ1ڛvb'RYތ]tw2ݫ)Cb,=;Kfvn`oĮᨱS3o\t% /RWiCD7a p1pPCf g Mb ߕ A,Б_ANl$GbmES7BU¸E< >^{>R ŀ" 4Ot^>9G(VfR)Jш>5@H7%q+ķa|@\J_A[J݃X:3﷊xe @7CÓf6b_n_ZiNql|whYN*0)8ab&wMYXr t8DޒsYfDG2z&(Rz2J~~56қ.FA*CЕ-"NrnwrvU^;\L@X,hqm>p;:+gbSq5qMc"my ö:/OP$AKaɔ&\a݌\6qk@.KUB@\ b*hm%vs,ѯAM_f@ ! !fBߗ*GQ=$CT|}WL?3KO8 = mJ7?$UMX0",Vp5hq*.w='b_>Km ?.ocZH?]Cgk:pd5n]1EMP;?plL#ƛxm'BfX'=&֧Dj+tP;W5 .JڜQ۬NQ3]dFp>E{oGbS^^3<:ߤh;0% \t.%LEz?Ԯ~YՖ@\,n]NǸˣl فA{d[4zażO@7̍ѡɎM [밸פK0 H_SQ xGYkqJw8+n8cȦ9S꺁&c׾bMg `2rW{XMPzy8,CBͽ]BV"cz[3?]X9:Nz|^s9n O=\m6L)`MS浻[_S}xƲx|#;C4L&~GƜMkV,CЏ gz{G8ṶbjadMoAKUg-[no^h6~o#CDJ/snU=c2yQDk|l%ٝ{Bzfwv×DaT6tG/y^Tzٕc}[gȧ( ::Ȕ5 *`44Zȕ8XgWjAm>v:ޙEb\d䦶+Y gɅDc3A ӤGQ &&rW:, YZ@EReA K(Q]qR^VRDjׇ ?t5sR`ZC䠏HP۫7:HA7poba3W>"L\~*5@yv|2K{ l@[`\itS}]ZL W$;0$jq eUwNb`+'F|Ֆz)RB@iؕ#Y+A@J'#YhV/"~Kۺf*cZuEy^zQXDͶҸmsvIi %z`?>[bڙEWaza [UG^Qb#kZq\:Ŧ '~rT/k-ȅ#J_R̙kg-:.H|r-^=c@Q<er'宧2aTeݑ]9p-5{HU~}hKY&1#ĭ-vmh4pZO\)& mkȯ >(~yK>"2Ԙ$h.; &iw\!gV:H-)[?x{Eydr%gsec&HNN%_ Us٨n"|!z)b98u; Γc6[sUe"jnȄ0=aYĒ'V{Jt+ 4 $6_NS": V5R45Bp6TA- `W:»/())D8x:Af4 M#ޔ~H/kX#O%\>7'˟:n=2mZů}`lH[~OAY4ҷ߫+nb0i0pļtMRd1zmkd:nD(Ҩx_5F)<${9Axwc>5u=YOIM^ⱼi $Il} ;NEXLKvYiO g+spD nh?PhTKp_+=e_WmeO.~) 2h#&6,p/a![Y~N$uc.w1ۦ{&11\'O#&I 蠉+ˊ'/Oh7TpT#%p`y(U?v U{wu~?ucc]W? *k kH@UhNbӼ ;UeJW$Nꬪs.uJ/2TM\;dn?Xj bV:4MYJhɶN jϽ\t(pr²JȤ_1yu!tOJ^%hxAAq`(0~[TNxV'֭C@uk5A"^i }{|]4kwi,g[%,秥$e!D"VtvA V;;;)RFheC͂"Oϋj+>(JpfؼU3N/u`qU%vs# *ṫL:MG%)U;>{«(|6|,EVbb)}?t95<8Rk <$q2ƃF>!oIg< ߲|P* ߓ{SWx}´ ~\nub^D0;}csHcZ+Mg9N_5`o+]TRwgƍ~w7wFnu^(&V '/<ϛ@DPyC{ҩĢ"E3H'̑J7"H4]AB$]CV*je^YmF9ZR< T$?bN薜W?]Va_mܚnfzn[hIiC/rK[ӓv|ZB|(<3i6v x yIǓl) \#!6򵟞S_e2ʵrS3+09/dYEl_sH;<q5l_8h[%[Ys7gJж~Gph{{X#@ڹ|fZxDİ+-A}NEb[7-&w{P=#a.Ly7::\;~{*į{=|ړ9(mOH(XcƟYt:gd9I$2B#B9zH55A M DSE.@l0-䙷IKEFva-Kw.eC 1ܕN/Mj ŕDӶ3يQOMXF?s5tv'V2|{-/m A@r e*') HƵubgWw LVX6RjT.qNh?N?\7yiw*?%B̔#tD5y"}NuY{CD/'q=M2a7]̔2NRc1xs<$tr8X(}Ԇz0]d_։59}9'ַ_K7i~2lX^e"vpddSwku{'srx4{*]LN oQJm[0 sլ-ҶfR.D2J)q.꘺rFi"5\v=1EULOMs|8&wL|sN{ݣvg2ܳxDl+gouƀ"1$!%B`u䗃9w6SJq_lu H:Ǵ[= q{k]wJ-ډkKuw WT~lշ niѱFr}t{ۓ4,f5+7:C3 4mɵBwG8 fjSh4]'m'|[G0.?U /%jTGv|r$Ji7@_L\q-QsiS!_MOֈXǃ7>z=qNyR/-$@59[,q1Ӻ͈YPd\Iҋ3֝Qǵ}9zotxD@f:4)[TvKn'v2>8G4%xzER6@F|ѺfCBr]:E@Z4}TzxJ%${xItb'9ھw"ec6@dIZ,dG{-"Z#b!M˜[UZHł4LCo+ I蘭ԉ 'Xb/n5( H2y)QN_шK~ !}pZ֕Sd 9:/fOIԨBHu{R*'զʶ-n ^FaPʓ\%AV\uUӰE,\ "Z<"msխMu:3em y FpvHmH@+C~є\pYHj-W%f̪K\~ q G*萻‘ceGb?'M(P͕Vӑ>|XAYSPIz<]xvq![5!01{OrK.M$(*8ejZ)Gw!vJl;TW^NJ soߛ`2k5*7W쥽}+,%y]s7ժxњGL7N3)p[u[ētGAFG.R׵j];./LlSL%-~d<}[  |\ KuLR` LV~cCN-I+55F8{m;+=}&%ۈIfjSšKm-'`>-sCPaχ]WYXKQhptL#ׇ? .kg endstream endobj 80 0 obj << /Length1 1467 /Length2 7558 /Length3 0 /Length 8560 /Filter /FlateDecode >> stream xڍwT6 410tIIH48  1P * ҥ4Ҡ --݈79Yk{_{{I[GnQÐ<@^~ S __Mtˏ⊀a B| $ T@APD(**PCU8 g{ wr!QE P0! N G , rB]m9P@CGh Ƌз" m Wp!0* f qN詨!_ w$`3l:/ A8*:P?Jr:j¿C]H/{FeP׬~ wr)@]!`Խ{\/ =3 QQr㳅 bb" }3Oۍ A@P?;tuzZ@  F P?Qn_6jPO)?~@ϿQ Y1<#;(/x< @uAп'WfGe/ճ$`[!, GQ`f`Ro4]HO/9AFD@쿡9wT BAf4P_/?XkC`h06AeW 25@q"En[}"+ |% dj n/ DP3l-,CI AQ:À8Q^Ma13jp@ \]Qa'?3KׄY:He$3oQ>V4 Toהw{K'Ҧ-۩tSDr $\{д_wX<階xD^$ڢQUNA'|wYhկ&|J8k)&xYd;f5 8J⥾ynOU݃YÐ$t B8s wޑz[)=aҥx@ev͂cdiCEԚn|!(,5nZjAI7[ ftcrzS*&<ِ)nRN0Fe`gfOqJ[tl&^CU*~Kk4hYm.-ѡ/`M{fxNBb~EL|^f}QuįcQw$yc#6KּA&t EJk"?\$k:S{m5\HOդmZH#Qo/Ɖs^'#r?0϶fīidx8bdaS~j#쎷Tpfl”(SˇFgxaJz" bl(WG vFpg*1Zv :o$KvZ/N'IߞT)^{gTd@9T [D"dnR8|>ҁd&hfAi!UELoz $RQC㲹GU -&u=rP{cV61G y`aF}RFڎQl|vktWə_'B|Z"q&uS o)WqƜ$꯰/ []8 nsZEcZ">],f^L D~Hr'VJ8*SrASi|1N')UӰML@:9#x6" Gߤ$nes6:,ܺA}'Ӛ]{,߯#pgbR-wfc9y(PO6?TWƦ)W<_?9_p3VT BzIʞ|#;}}9V`V>rp (,p^e=p.v+"fa~GPmg&"7hg(7QL[zU4{0 {M\ $ <H7u fL?$MJe瀫&Lt9IW;q%b񠓮h sȴgHQW5vם)z k]j(p?C~j ;IH.Dj<sDf^iD]3^Nnim?UXOlIIKቧ'O8i}۴4 1m'F *Nw:޹i*+#HE#Eжocwl*V]ZMJA6cȡݖ]g>T+~zr>6;C"Y\JJf9\V6m|ϜYnȦ괒{B_1`g?Z7ۥ²wsB4c@h's^ң] =|jCU35[}ZK;"*Z?uURR5~7]h͗ь g;ʥ3`!7+<~(d|% (}90U.r4;jfξ!VBknX <* dc\o'ZFxy lT`9va&c0gL={K9aŜU͏@CᆬrM/3RJ!; qJ6^%!}r[kboLlglgWFҶ9֍ogDxbq>$8 *T^JO)#yOJZdPiH!) >D0Gq2b7x';AAI֘7o/?dJqf^%N?؊ã&s Uzp`0 *" 5Z8*8v[ (?R (@/R=Oq{A BwCtѣi uvg:j[K :FjK;=wf yﰰUmSW+>v?VlCAۑԡ{σ{^7љ/Jpx0p̱M(@9XBsјApñ%x0\hc3ƒw͏^Ί]RFAJ͘)+ 7218n^RH|ZҬ.+ߩvAjuGtdvRɖUo-Yk7p t@D+/:9ǹɽVI!mx-+QvG#|L*;ch1LڊyyZK1mxIVLajjjH-T͟c7e}ُދy|`&Ч֙WPmX׶jeAiΑUNmQ]zȌKq6̣WV'` Ͻ]VV't(g!7בo_7lkWz]i.%?#+wT*}"L8a`\z30tp7tSe,%B`K 5ϳ(~ݓbͷ@6]Rcj;к!@Z([pք-`$ ؑxaNT3cr͵ëXnZE a6fXl鶃 aok*G8§͘me>osYj4%Iw?hEVn\^\;}jkM\MiO=~LVȜId6^z]ptglÜ!9O ׅ֝IS T{/=V\f,Mhym8Go}űة(3.u-Lrw=^%&YdY.Str)E9Q\<5&[=T=Po3X;Tg˪UFڗ±'(VF^7Y, Y_֤sASmJ"nm"o'RR ʮ2=Oh5Jaz<#8qq<f5S,24OO:%ܗ>I175N!) 5l @t<2h#CY²W/gFùæCn~X|u)"1돲B%// 4dUE1QM)̰ 1rhI *4  $q%HOe~&E)sE3 _$}y 8֢Oy߀OVx=tpk‹A"[/lJz>?a_,Dwl,G$2q)a d$j5ajj1qc[꭪s{WmZI8.lPS6[bm7b RV-J5fY>E/)=%X.:{MbK#c ˆi -߃^mdIV^j+D1MzZ1P'o@3YYvEVޯfAǯ p{MDa>l0dE[U{ϘFH4'6"ȯ&<-xPآeRԐ:A`tb1}y@"aWTP 奅T̎_2_z}[*@IX#ٸZZHRNBu͢k9<=;<_IJ CLk9&YVj;?,rމc:wsԚjmರeu}>^ ݱB u/CTXR. Ɗ-7@ˎ W!9T_S UM/Z(xrHHE jU2dsdגv<;5DS"8'S{0S5 G"}{?@# <ݫ.WV={?\Q7lI/ovnU&:dD V8nN07''14(e%GcDą6žʩA&`۳l!է_S&:0s ;xL䵪9S\n9ܥ([cV凖Gۭy-HW dJ~["2rfp:&,"%KpTVs#xHlC󀈞:\sOpkH{c_>YRFY$"OWqnA4DkpoUT|<׏odtCR7 ;{)~fZjX[VF9*}9\?^Qc݄z}"p#4zeQg2pFFȈn/a_#^)9Ts˙c{c 6%GܙJ7QSPK}NRp@NImFW8i  $4d[P:Qlݻ4pQՋ4310}fpnMav1r h2V:(Vϝ X,n~i. O~x{a=iԩa.XG- pz xz0Tm31-Ct}Uc 4ik;8Ar*Os5%o-n,0 lK,UKze}RU) w,ɜH~r_E]gKV8ml]")ȨOCJd_+5ǐT1I&rM\ Bq=T]e\ -3}JJqje+hr\^  '~RqV!3sT.`ca8["p94}dﮠUdHQqW(YF: =!5fpT陥KKZHxlt*y^YYx6vڸc'{s WgWLsB&Q)-T m8r}b_ endstream endobj 82 0 obj << /Length1 1867 /Length2 14734 /Length3 0 /Length 15895 /Filter /FlateDecode >> stream xڍpkӨ$Ym۶Mlc‰m;xbsbgbcbOZUYպ~jXI^(aoBWaf01201YÑkxe 4v|d\m̬ffN&& ;Čݬ {;3120sss:Y],'TM.Łݝ֙Bnb P:܀f? (fi\ X?<\̀NrE_r ?#ݟƦvVvs+ @QBÅ`lgɇ$]3%2vf@;g?r~ݓlgn7[ٙQ+PZo?2 t=L-S_o{G@_+s `fe0ZXC 4?.1{?>y/3{;_F)u1aڿ*NDMgag01dF4+mgn+ۏ6OnA X S P3zLLGO #mwB66ƶV6| 6@3+Wv1Xa; Yhdbj״%WclJVVWte1Gۙڛb,c''cOK v7.=b#ˇ <_7`CqE!n (?(}DQq}ClF b}L!($ٿ?_ڏ-- G^6l=d~a/8*>zʛlG\ ?2sWx5NNNo?c|}@S{S`Za|wI9}4jzeN'$dꚬ/Nw#}(kTB+D' am>/*3pKX?N~W[<ѕ I}@`x¾YYh(y|8$P.04hwshSD2pѬ:[,1?+X{ppupog(ESdˊ~,y"ҥѣ0dXD5׹/w1%)%֕7c:(bִFԱ~vwi[~ZGԶ< h~np\A0$hN"~NSh7y0 ?cėMT~d{1byYuX[)8k$xÙro8ueA d#I֕9C]*Aʥ~fpϔ\o"@"nt[AL#6 ]P=3龽͢o_ѿ 6|Cv==idFYJ1)ҳ @, ƑcWv(?ɑz(>Lأ~TxjXSD 1˹ńj,&?=jZSTefBδIdq=h~۟mI$ΒC!L*"B4Gp>ۆ{mI N*&$ $[e>w@ jPD_^}a.UuS|';G:{c"!E|l+&VOB]SdyiAu]p6( (X4o$}2@% XMOqkëVt*hX-XŝCJ r3%8ZC7jՔo݂ဣt o+xANO-XWqQF6x0@8C!slV"MA+R^LZ"QxΆ4L6VBOvDiH1FXg-4c֦A{ryDsez?)Phk< k' 54%,U3ΓZ7ϣug݅Yص"AxAPu=R^tñ$6^?HNLAM{G`]zb0SZkdQ0M39+Í+2Ӎ%H 9}{f0Sԅa\4Mf Mśm|tm L6>Nju/b6Ah|`W2Lm*Q 3!2. a󑲵ys=6OTPKQ 7%[fo}g U(/4|?Jwу+{J%cEōB4 ,:G1'%:vfP*:IfXY;Ou@)vOV4yOñ 4C(Xmͳh^׻8U6y/'Q,E집@_g,1R ISN×XRú>4Pܙ}Wh匭UI_8u*|־D?vv-}B(_Sz 7).^pzu9'Y*QTGYhYzy˅p=%Gs%r.Hr~ոIR~3.\F. *E ol:Q[VqssFݱ֙\M>̪qz>hK8΅ZۯidN}?nt6iGo/31L=aEE"Ma4uN/1ao8X8+LgR9tC ۠#e6p>1 4њOyym֋xUft;] 5.$Yd cL /-䩪w ^hc/pTKFx!a}\|X7[TH1K$6: o囗_Rz$stS&L+X8eE^o#,~{`*!VpM$J:qs`P?zVS[5FYiE tx|7[g/uq^܈_h]5Oat~"F)`boAY JjyE'ò+%;oN)(O`_v] R"S! 7 CV-0r~FNɊm\rz׈j[mCQ6e?6_3FBR"@Y cx|$N$9;نS>Y27/ni5FN9Qk> 88[(9,JPߏ82M&E~e48نj 8h. _]0JiasV8]!KN>=y$/ŕ%?4-i9%|YPb̵Z"*YVhSuz4f+iK%䆈Q> "9C/<"&eVc-CBMM^Rk`̞WQP`)E ;dG9 =0['] *abOώ]9ZSBKȔ/&So _}Y7S2OJvI$SP(g牴4_}_[ ɭ'."aF&b "y'wdZHȖ+W)E-Uq^ j{pa3+1҅M#Bl5A䩮c@KIWWX}l(m^^oFO k.' ,O?e7g:<lBÙU} jѠFҕP2qم1jm+z|nks]᳘/ nK2AP} G%VI;^oPEC'{Ne_6Q\+;g.NgѴPnxbWU.'qt;mE#m;Sܺ08ܲ jZd{#MA;`CAT)(r=>=d^b;S}qѴ:<SEY׍6!P#!0<S"̅Æ+93FƗh%È5(F]9}xƄ^匃|GkF #}=]h6ABۅ9Ǥ1 y#{D&E p9P >_ R ׻G+|I\xU.S01]α'^.U𚹱80ދsmțIGz\o{{gbc+ʘ(\0vg!'$Ls R(Ik+$9 Ucdqy_[Zwyay 3-rii 06qt? ̪*'Rϊ6a ̧C UDeCo»[aVoS8UԴQT˥MfLQޝ?K`iH-`9cVJm0;6C܁ZF "W>>Vz!22s嗄4-; F[gb2?}Rڴ}gJE" ›㼂&$)BFu͑Nƫ5#I?' Js+NQf+zTXC3 +g8ilz׶BuP\6d?wOK)PQ[G)(J_KO&T9Z>PxV]g㫤n\ d3ok]eXFm 9CObJ"9- ^>Z7w1״rn-gI8,)! G|v'3a 6"𳄛FkQ{ U}ՈD9rQȭf(x\8'wK 1Ly-3k֚5pKh!g{DV8YpI]*0_Ok/g/r?T#y+~v ^.\³ /~7k^J|[EXt'j̥˚yG[fuL[,(uCTF\\\Krpz'/+w[ug k"ܡAA+rH[uk@RӐܡ*1#wC 0C)RyͤE}^zc6Ylx4ŪJAXkOߧg/X8{kϴ4G%F3E䒡_)niM{4~Z_8D2mV>LbduLd8m5ˡïd{Q С\[qh_Q`c O`1P@zMem|!*mf/n z>#8oLp:BDf=3GA%OPf)Ve}=q$K],Q Dئ\\Q3?$@3o+,sv6/c,'ya[ˤ@ ^up7t$;&+{hXTF]g4(W 8 ?$(J9*/=eR`(iJ$wqM39kԤ:rPJN#7A 184Ł~&֐ߑTAÿ엝ՠTey.~Rd3EI9:+3ʚiKˬ~iw/F,rB;OcVPXb}/4"f5n/h;_KSwS%g/<xBZ#pϕ]A bִލ1mYrN3M/(Oj+9@A=J+Er1oVz~o[$L80u!?g뫆TIbUoSѬk&rKT!cH?H+YgƗœ6FO!NPNvSc3k~2ƺe(LM5'Jt:W"朷hQiT? 6>&A(P]uCKAɻwj?-yuAC꼈mהHVVUȄ鵠b{ʒ \l*Bc=}-oZ:)fuPb2ڟMg FȆ#qB:!~2B4Fbv6+㎨36Fp\̰ڽXM Nb;+WNAgP.bU])LYw v r3r"oHɡz{7T8:D{& QH\% `Iٴx#/vO)e)N[5s!Wdj:LZ(p-0 _[2P`u2Z}ղBhʈk=Kfl9`R4_!p"j|NF|;t& GZ|ҹB,{4lOck6IQޡWys,[UW\HXDH~S8촱ל*cơۘK7oa}PqP O=4& aND\`ݰHoAgђ#]O.>cOwot#\3Sqř`KTwUdz"$u R aҴt+aKӵ3TëE 5tRg쑬λn!ζ^MuBKV(fh<JX4u*J$F'+},\<E$iZ7q|Ė20z/=UwV":҄QwyDIqYdS1 \]4d Ӏ.Q g^ 8T-(H0ķA|A'^!ٻԁJ=.uɊpbPqI+:LL~K\0&[%0+!ayy/*j ߴ2S4jM9=VW~>\F<7 {Pd/m%'͔ X5Hd j2^xC*lC(CHUft` ndl*nS۳gviVnǔ>1TSy1#E]x(r}W$A~%NpW)Ayն+xՊjㄥ&NE>-*䈉 ;m йYE;G'ɦJ:HHMwvh&M2,g(vk $FAW18O;$+ F1O+zR$qYyN|Ά/Qq ^Ut`PH!,SדI1SLș6CdXm"c&z\^f*ķت/EN]OK+Ufzܭt8ݔOmWrԹ H-$ vש צ(7̸~%gһ ^X&u,_.Z^`%rs ]LG4gWmw]Sj$q; ׵Ǹ'E(eO^g`X"ޮNHJXla~~VM= DϣaziXofIŶd_a䥎~KgGXy /2,R+b%>z#ɔGt X=`B_X[vOӐ ;k8dP#=u;W&O4'tyqh=c/HVwL4n*6vVr.>H!7^!(͵gDak^ ^ sF v-IiR]6ΏDNW\:6k9~f Q%޶񹧿!uDA}&Z R .!)~9;_4umۆ/-ځXU=`ebvH#=T4XŸAC!~e}$9`7]3kv JAՃG8#J3C#,dq߈`ǔ}ڇlZqFGQl\"8+M:ujD w.+L:,H`ךhμK˵n џg#W?e%SjU̹T:b¤M^_p#ES#Bß+ُ#/A)gu^ԓr]):^&7PA_5\{8DU4}sv%qXaƨ}SǕSr}&^stvM:39&rfwE'Sӭl t'x@ *WG{zb(,O !Y#VͿy@f+w3^u !)8i,3fMPiH>_ t6&Uk_,"`ɓf$ ۍÄAcb^u?J ClP0 0At "h3}W\e3Coh%{="9fTb>)@czt*=kq]?B+73|+h'y *nk={mPdܲn?%<,xA[cHW'ϋn:X[H=ž6,Z(H݄+~+Z*ސZď/_zsd9ŃZ#.:lD BBt1S#M?sq`U}/z+$x"EX'(xKFq:9CrLYˍŏ#n]Se-^zOc s7(GgF1Y*ixDY$yFh2X!g*w)Muae \cyUnBшW}D8ĀjɽS>,XX]gW=Hq탘Ah[ ?oۜ˵qѽK/;GuI^!?F/geM |CފȐ!J  !ѓ~ '_~B9K T/ل4JPi#6XX6F݄Nw]I@foU^"=Y`V7SUs!,3k"MֲM,0%>*ܬ/8&/D~Cj[!`f7~Drb\, =|) @ %R| fm,8)}Ͼ+;f~ V(ƼgJF7+ fȥ8*2e92lUtHcLC!sь&ei^(>~L4Q/؏6\`WF"(_۽IxgQpޜs+~P;ENr-Z UӅ0&$6/<'e ?@(~-ۂ{%(Ҳ"q߿[]y;T,OoŞ h|ˮ'qѵNqQikcmYr<,pkX"%qX|MCVl#f҄Wji Șx%SwufBAzhWkQ7 zmܯe~!1q3Xr{Y( #;Psm'ilk4[Sd*t hqMۙ9d]_HO¬Mw[w1 _(` /§#Dp&֫ډбV,#s !d_kFyظ*R; ~:*gxFAl=A?C|,~23-Iv2wv~)x !bCYyg܁E39RnV 82#YAKYŃ{2蓞g#gB4Lŷ䆙jrhp~5nB:³'zŇ#J^gqSu"or1S/ D;;nF$7,YZ"#]Px'ʥj 'XԾ&c+AC[[f2ͽ>sKQLQWy< Pd$f1h6/, cvQjBJJvet}c[<t 9>P&@ :[o~:%#nc)>s_5a4ǤD>nq\7tͤϩ{KCFC'δkU= '[Ef&TSD2[1/*H#q8I.1љ(`lE; ߡu OddBO4cI軭d8(EG&7Ͱ,h#-o!쪫+q[Ƈ$L0֭_mnye i褾5y}^TAq"+Ko\q¸Eי;Y1nnbd3X:`[* Z+މ!w0UIchzS藎e,?](įsQE1*#S Yc'Up5~34\҄V\x!Ɇ4;Qw /'$X1HS;m?꩓wEXU<;II *TRڔmI{C<$.:u.E P+?+]!xEQpl3R0Y ԥ+G{n겂mV/D3 uyS*姙"} qw7OP3g@YzeMlCPKQ -]8#h65<$~dszJ%Dm uB1t>P.KqO)N."ͫ57 j!%6CbIރe%N+c`wN}.ZWL?Q>:͏?uU7!)&uj-&i=f{,K;)lSa?_.P'°inJtG|T aa؞#MRK3BHD<9n =4#ߙ<#y{Χ,{'8HEa%zU|?ʛzcXd ?-*LOŽCjl6R 7Q -f]B:+0./44i Iޗ n!g'5VT(Fi[/~=Dhu+׍D>L:W;GpKZJlL2ZҚ gxt3ҳ'+̅-U˻bC9`0PbyP٢9 &_lGIޙgfw瞬(7n;qV}P"xB"k֐>bk"]C)"b"Oq.*@<kIJb&dxA$k_lQfk`!{EsH"t[YnGUSi7ly0;Ǡ eXmqQ!o{Dܠ6WU]Mzzw{ȓQQY5$c#"HlI%hZƾ")HMg^S-DyOnpYA{OoG+?LA'=&x`8Cao~'}UFVR}ٱ;҂غlL1D r_d4m}M & V;GgLgng貎HnМ܎T>C c7̈./Ēӭܽ Ż0AH%G?KE6-갓_N}FbFkIɄEwy=ޙ+.DhlQR1\h)pTuypԱW6RW{[t&WW(ٟi8fi |@;3B!W (]G>1: M[cqrN9]UQ&:uw Т̍dJynU8UIËTr5ϯ('L6`lF p n ^4#wOReˍ`}ls7wT]·;H!R0j &[J`M ȑcyCi]\TFm^nNeFsTC>/.U|=6^=]BF1(t3AVi'(%[D%{rk1NiwjDYiI-7q2_p#9eգʃ m~NOխ F8\FaBۮ#M ~nW)d12¸ݓ-싢 ZNS n#0ڼ cy@{:IOD3'#~&!܁?쉵Bp! QOO\BD"X8t̝09=Ǟ75m=r:@F/rB\o&n8/Mʄ!AD;Gsώk\bp2E| rK29qSw:Nja08wsjy_Zq`??Nw a1)EĹ)> stream xڌP\-wh k.]k.  }'+ǔ1}\UY $H(@ Zo1"6ɑ SwLlqظx@;?NISO  @H-jce?t6>>@6榎%Swk8=@_t^^^,n,NVL/wk: U0@we,MkN^ X`ocrt{x8Z\ 9E3ocſ ?t_ΦNΦ>6VK{@EZ۝ `h񗡩 lMbjSp.ݍYwYBW~6 sp}X߿_EXx8j9ڸx$m!Y\@ rͭYqK\A3\(f zo1wl ˿1x6} x~^N>5_V=yE E]ƿ+N\ `fbxfQ5w?rNw}Rv/-@g \@s/M߂tԦ6>6﬇;xWM߃Y%ʹ@?mqYڸ[,˵:2{G_O3t2?9/|8R c c_; `eqtr,\(7U/߈*X% ^`X 6`8r8)Ax*Ax8qX5 pZ8Gi֙;ك?Nο$:? {? ^ i?4X`6p=DeK/s' @pV$'> Xe6Bm=Ggq.;8p0s]=z?ɀK .L ~%:ڃ,tR'8kgS9qp?X~XGvr92qeGW$nNOX0? x̼X`o@0? Ӿ3\_O5sWp}A戫KNbD^SB3V]{(?_POBcnw&;'q^lo{]t1$ۼ4q#=740D[69pE@7ѠFXmoobHtZ4zojmx-\Ez1c_ܘg; NY8ARwK"16xuQdtp"6DϮ;B|:îԪ|)(AOlؙŒؑM^tza13PJC Y^J|a<_o2NX3iĐ7(ZDP6A޳K7p-(gZoZ㰨n11[v"(\᷆ l #_*M-փ~p^iO8aV\CF!?ɄX~>^ИG 譂XS$ua& e+p 6R,nCalp;?+I~fՈIQ!_k=|UdT1>p,;5n\xZEM@gldw?gjuIT5mХ _}zI U>=:3'r)W@D;NQˠ\O]H6Hǽ"Wd{@+5KA,ozd<4?NÞCI*3i=+m5o4Kb9IeEֻpc\7@Ǵ߇cc{'_JCWgKŻY"^Q.d GGӵb;ၭ=&|Aj,WX8|[OZWX4#r4Xrob T;VdAU}Z߃~ۗ)Vϲ[a`i?zMҋt@x]mckWq+/Pm5j Ғ Z%o擵EGc ;)sYܰ-3dz=nvUGQ.:rI01hMTeO4&gq6獧'Ef6bGQ#ep"Ap%wA&LiS='uEb&0d80$ICZZ h:8/W\w^v@Ф FhvdFFpk.|۠-цߥm^JѮ)|۪8tyv]u<#)mk]\_jZp'6>ƆBcUG8GT@'J$> _w왶ywV+1ބZMKÊ$@7g q'IW[ܻK1ԄC7#sNKl$J!.RU~N܋~1?3\%*31JUkX/ hĕSDp-%/x /+3,WC]OGs:~i_%VVI]ZaJ:ccnk43&v ȭtyh\+}tR59aA bE'Ȱz+Gܚgcg*/YX##rz#h:Fk~Nt:fFI[g^M}ϐrgekWJjN0%}rՇH:K΅KEibG K d\+9<(>W5MS@+9Q~ڑm=~*mMFBoPS$׉ # u,=r!/^R˖ sgcm6o9qDfoh 39z0.TF20<}ik-)C?){ X#A~ja, ;,Ө[ L6Ҿ 4rO %m*G}fRi`gþ,<1-\Ya\E3s]|Kmrr: e֠Gd(iIf.cUك:VRe!GVu)1ހj޴ b?2; ^t-6B,L"ĔTBcÙK"a )[>G [ь̓kЎ̸,fTQ46={!&%3Nе+#,v>k$8Kt.jyuh#䳐 > ;H9`E=3\%[Xb$ld= vg}"TȚj1JlxŹ~3{w%zT},Qaݮ2jw\j[)PXdGKy`m2euǽ%d+1{~µrSʜ!JkRO:oǭ#1PVme,1b1YpMLx}4&~#d$xx@I6ĔN<&F$iq[9Ȃ/?+]\La<x w ADG ;Cwފm7./0í ^eg4N8-7C3$H8fE8kX+ifؼG>]$iMi5GG]zFXOid6YQ܆?!9o qB!tO v XOwZp"/3ɉuOp*x 4[)QFBRDo ynv{H )>/3 N!qxĝf,v lrfdWI P(u&.Z 8HT$q*v𬸄NyONz7I(,K~JΈLK?Da*;5iUJ}AFߕ8sr7y+Z#=6D-ܺJ%(l)+\>IMDȝKpڝ{ue7eW! Fd"ȪDAp/>^|I~'I= RXaA7- NJ#\?䣁BݲtAi R PR<8eՏ!O RQ N#}xUVdG'\Zf_]Y|ɡ? lM<ya2_T>c,ɕryA2P7hՅAm`R*ѐdwUaCuZ *Gd%pT$[I~ AH% <ysojY1N C8U)V548EiõAnf-,B˻8u(tݶNnCY\dE<ސJXrPN5k`ۃdn)z[ijF ܽl ޏzbH<+y`JQD0{Y0b}3W<Ŋe;w޸> xcԽS5ZR'&t[vb -FO5V\ǦQNK8TXG5~hcY! snBl(q*K,5)M^L(VvI9vb47}g,1l6Sh#U->3͓ z.8 ɓ'506j4BC )M诫s<3*I !͍ u΂V85"ID'؟ B0\ μ0#|GF.Er-3}]O$&/Q.'?1Wܪk+r#XPaWyՂ;kNYLXPܿ _F5'8X}gA߃(T~{{x0(aӱkQJ2H•`:P>DD"h\^?$ 665D{zەYjd8z3<'0%^i٪uq]5aμaKBdo."saXmBRRh]<KK/$CSaa[鱶߈y镯na3Wɣ_^cRz 08NQ]izZ|cЅX2W\Vs/vdǓ^"7Se.S*Ee_JzHTsdsb6lG9דt_K{dZp  P̑cW8kg2Uw %}F$1ɺgre-裐QkFb*,a \Fz;r&‰3s:P8)R#!d0zTVlu8qad<qؽEdzPr9V$ʪ=<1ObA VPZq~#t̃wVNsץY=#壱ڙ` 8Gb rsib62{rDQyM${tMyZiroD--{_E\~`J3x0h"@JBTI00nܽ?E?'A1Ux[U"ww ];w/lf&~s2h.`QcRqY$mǖİyOb"L,iNJA 4>1Č=ͮ4iUnS.6P<ɥ$ L>@'~5zx֏cSKaz]G8SP7# a~77g5E%5קp.ٺ rflFEk6~̴<.9nʢYkYt.-a2Lȍu|}( 2wl5τ7iG]keFI ']?5430 grʻlqSMrAI^D:7w ܨqHqmrBl7 K8V *$:2嬾x^ I1&:S(!Wqx $Eu=tӒ"6~ȽG)yU({VRwK϶GpF-@|(VؽRͲrTTmqPg0r7RB%e\kׇΊ9%= =#1,/tU 5rf=D?7&JFC K,|) >7 ʶ\XwCҥk~ ReEφHL]L {s\M\*r=ti{"F3HU3z~:;gxE}6t}%U#_̭wVR}M82eq6lӅ7xC,jLwo^d\~7z/-C8oھ4}k_43NKuu+[TwU(]1hX:b~tZy<z&EdB~71WUբ|5Xxv؈Le"Y$XMg7گ,8iXAq5p6pyfH WFrq5G֫m?7M5 F*ͩ r揿9XdR~*0‹P 2*3eŇ@-'izWr,Ip滕5KM>5Mq}D̷BLmoc[t^"4pvFì2LbI/sp.|ZE&6O{fNilYƶ մ$?QHb 8O莐ٿSrեw lW!$#MNaR+¢8&lu=IqfFpPPTg%g-<@Sj*OTSIaKů4,<ȏ6=vTsc;/y`N?H0?͍1pz2{z0bf' (D >[6Fh[Q_wqN-l0T2յaגK=ZMsM>* AWZ4ɛ}cJg|݁4g( ^;?Re8ʢ|/RUbu{.KG=WBnJJ<̋*S&9Ұv15:NqNgBt N>_QJGhlcR?49}LC|Ssa?Y}~lx;PǢ%:d;No)MQ@_wB3 kuS h;tG7K]#kS1 j2T- ' Q Fo%м \Zy׿ek{[Pm5_ϽkG>̥,GAfXm4kǻgјfp r%)yNت"gFLtYM SAҠc؈:f ɿv:`5 x/>P9=KS8aI Vq 8U}@>$}ݯ!זR[uZf25cI <hO8Ћz/kfF\?<-R!ΈJLI[y /˄sł2 {o%ί|Oq׌@粨הO}#`S5%TO3u[#mtvZ:dG؇- a9~""zݥg1G5yک?2qF;k^xQ}fE!bbspM~mM38SهK k;6n4Ӏ 1N,+0$a J}G|@A릟!78dv% J\jgz~~gk|$|Bx ##qHL-4_ x ᖘ4e@g$J$)8R]!MXhIdTRY{9,q 㬜f|9s]8)* ^RDϜ Kn餲uyOo73h-4ZFx84>!h_vV47f~Rd&ش n+{r$!!ȚjR,LJrC:o; m+s?}IQlwMq%$< ~A/dMLuJseHmltϡI`m.ōMϤSs }-97VW}}# .f9ct8[ʗKog99;.Zc+gmAR[֫V`i"GwzU$!w)2M~ߥ_k%ǦWkd}2nD^b~ÚBd[3PѨxt} 8"J"oD5QMN!?L]F*L=R(ܘX!G?w lWqĿ(z3JC##?H,uQh'yx.NʽZg-E2Ҹ|M_HJp( ` >b/c7b) F; puI?lg->}[ːXVI6Ԧ}j0F婅0ËJoFR4= ݀oT'*_k`9-+JK`f`YJq=mN4n9霻TYb|{idUY,Ř _}r-Y(3Hb'ݔ _Pc3+:խޓTBH͖ (NtͲ3^1Qگ0g+󦭶޴kpMQWmEm&!|n=Yի2e|I`έ&Ia\ǟicgcia502Hb edo؈&.Q9IHkix+k!3AX+FoXH~WVZzv `?L'UFl.8v~5ރzʭڈPZUU3t ̅[X>J#S5W1>Y8Ip wb/R|Mr˵x-f[H1D各(?KW"FtIJs^=9H|17j}v!By Q󐏄W2 8QY Rx|[Lmp&">}k&/)W ]t]6 o -o*d &]O@i_2o./;C1ʢ1`tGSP5hlߴ"p.D–܅ زI ;dgC:U#%c|P'OО]LY)2 yM†Y|V]b$H=a)K3[ P`&_~1_$j+3R7W= rH^z鋼JEs5λAX=( 6WOg8}?(uᔗ2$(9icr{Vr؞z]n)ʛ-s'SJwڛ fۅ|Wf8@]"67yaua,2h:÷JE2oV>g^4Bv~QEyGRR:s>B+!Mt 0EN-p'nSw~>{oG!c6Fm__`hTo0P2ʰ`VXVɶ(WjKia$VӒ&" l6ksm: s׌h9ei$ ICB~̕ܝFՎkl#X' !$ +!Uszڝ|;SVɤ?ss;1G:ؿ%2b_3z+f")@1:T]+5\G:l1;бٝEd;Re[HnDPfF0i]mk,Wo3R&cNi=&ց"6?Æ  &бToB07K'AݶYI=ķ;mo۰~j 7H-EU+%5ZZ}[]Ye(Y1K zUޟpS}zm;A<tK}3/&iX{x錂VkҰ^e~A(Sx(]5xAz&vʍP[X"ݼufKpv<-94~ jv~g?}aۥOvU >7JxmG\cT[?^]\ 9L_sY\A5!h1`Y?S{RMrYqrT[ Chl,n׫sHxUi{~]86!'ՐqdiN=bYiihPeL|s5Hw|ܛY5k 2 %ZS  %ّ?rߵEAWeqa {!/0L-Q4KԡT_lFIX[@锷rXJ֣{i9$)gї=Ch^|N׫8d7}!sd̨ۇarW V~Xw"-\]&VdZt/G6sfTŹ ͼ=.=#Γ2\M0ZB&}V3 6㞉+lQ>S.v*v0@vɖ.=~52z듸ӳ֯aYXGpJUn-4D'saT BLE mJ~f]}W\7t z{gFbՐo 52T]w[4͎R<Sizx{  |8gw!p2Vq3gi!@:D bc\#1d8CIȲlE]+T#1ߌ18PW k{Jrv=1{l{|J2~L| ?]އ@y\Yv z#:&iPQ٤Hi2X]*c*<1&/=xCaj9IDͩm.S—DzRKZRIm~ (}.sҶX<@/X2MXozzЊxu fEϦ\VP{.} &6  db1jWP+gB_UVLɹkܯo O|YB!Z#@뻊S։ NN=.Kk5m0?D: ˝,pTcs'FXh1B$/6% e% ؍tD:q/՝]~sCkQ1 +5$u/7E{8m_P~*NE#c9ޕ'G[!{Bdwi'L;^4zxį;t1Ȭ\WcJ/shf_e%~8ܨE#-SpD5ҘO\"#c(gݚze9ƞKnbKrV( aPJS }QG!dX[yhPF2Q\iПvf26BQ}@#I{ڠz0ECVajO@ J1+w%z'̗KEF+tl1Ee,ZaJ$/+)+uCݝ-| MFi=3г;H==PMF56Ҡ*sM`ww .!~fPHaq`>XC(,?BUc6W0$w*RژFL3NTM2+ &ZtxX "GLs^K *RʓQϯ2}EC'a G 2.36wl>Z64/Puv mM82[V~j#cs-FVZ-=_Ql|ݤmPgqϖK9ȢqJFjjh \r$K^FG\wA#sbcV#qWkB~мʑ1" m?UkJT9JXz%"I979lԋ(2)*Wr3^*^N8ӞaT[T-~$Nmze'}6; EzB.X=Z2sK"y+dL'i}z/&j2t)zc %eB LmG7j.J*f*j&Ru/zਸ|F4M@;.fިYi.Q[yAMD.5ѪIci,O0U^kgYa&fCTǒ0"Y%Ԍ3ɘ14t>rs-{LfO-:gwlŜ[A= ~BzVd 'FJDƥ]$)^Mb[UUuf,{qɉeWT8 Hix`#CZ\jgi?JcЌ:D)}DMI;| Wاit]Վ_䍑40û͍jQʉeKy7~QuG#y]ϵ?6mB&[e&t%~r7)/ {ڢ@phLk4?INpۓfHN&+z 1XJaEūxZUfibjZ*L^ctǽ>$`Dja9~rhFki;.,zQ}푾PTz#/"~ ǻD5ƙk}{U&~\5%JgjV9dN*o^}5ptU#;wk+tK?'{1'Ne)bex:&[$$2xe]{kØMLkeU $=M4:zR؏`p.5~g\T*t2ipqP(sW+QZ+OA"&? uGђ1O>ݎJҰk:(a"c\T1$SOOQ8'4D!)1eL1FF&vmӉ^5yC9ҩ&+aVCA,z?9=m|Y@ۃH Aim~D΂b(LQrM0"|GLԞו PWKh3.Ȃ*.A&McNM1ZcFRDw*ĊGEQiv^m1DkqAy@Ϝ.ݔU j [%;3"#DzMkDl@'KT@L~{OMNX IQOb{Mѯ3Lw%H:Ԟ ]PbVh<٠2;a {~wb"Exh0C7 U83BczόopD^|jcxdW #L Q-Rn]+!g3T4Jʊ\XxHZOO]27UL B0QCѳĕ2bd>xG [it-> ?+Akx욎-YVá۷[;dzӧȖxAbSc Gg,\k5V}ԦAchG"5C3(4PrlS4Ex$8_s1ب1BZ>V$se'6XL:TQ6I] J}o!jx4 x+?7|%~ůfp3CyFlSI_s'0zV r}^|P.f4>^k,Vi/EV'A D(}.[sP#=]d|KX$.&``) °ZX:cU36PCDٞAϡD" CLU=ԩ/X! 7=d$d tqPv Y=Ϲp@]m&0.eЪ0W sO e`3A vȌn*j4r9+$S%O}CUZEzjqG{)dU_OoL_njn=D@ŠIfוQ_\41u xSu`[0N𸵤]z5M ΡR UhmE!O_4c6 7!O2G^nDlqDNM`Wg(*X&X}n9e] E׆ /ǮZOveG'4:@kDTc 7S҈#n-B%AW[@?G eĀZckd44s -^m["ÞGlVf?M8e^NN8Fb6܉ V/u\d؎Nh9KfP>@+hT \0|@uZfɮzG$ݍ:f0_EE~O5üJx8N{_B'B䤲> 'XGXLe/ym3h ,_eG% >2PbKGΚxbGdxc4Plή v-]8VEk mwƠk:f[qD:m|Uחm)|\:! @"4yQzO. g(is_qx,$f*ȘQ~'u(XGDFƹ忊пT !5X(j/EM"mjK]` *' -.עE7V[8h:5(] #!hCE6k(qyJӶ@T$}Ctܷ6JAqD ko+qاI?|%?dfJU*%eX[ )ŦVf7 zMRA]ȝ&@aK3m0>c _Ӓޡw/=y)4 7<}ʬT1˼7 F'Xg[nebuIr(|U%}UJlQ#! L@}ނnv_ܹv;wow W_ Lݲڒʉ4 L;z^ח_N5Vb endstream endobj 86 0 obj << /Length1 1561 /Length2 8219 /Length3 0 /Length 9244 /Filter /FlateDecode >> stream xڍvPڲ-A ww `]{]K ᑜsޫbfu{.h(Tԙ̡ i(ęPRc,@ ;* F BAp8?$MxJP@`g@ PG~+B@N4P{G1 30فf&D3[: rW :A+gg{~VV777;'0= lP9]A_ M@tƂJа;aWZ88O[19)^ۃ  0 tFJ613ٛ@<Kx-0":AM\M&OߕT&O ٞ#ى lE_i,1ف N;̞@n?bn s{VM$''Ʉ <|\nf+=跓/{= d 8;|7Bec͜ K0Of}_~< ~Ye%$/8`f8<|gQ1YP9GOSO?ݟAw.ehA54{Ve4Iv?n;ǟ'ͺ8?_ jY%9r&O{ k`'i;\lfXkZ2[0uzUl@6pzRoiq} jkع&&'!sq؞V[V)ԞFy2L/Uo ` ) =!Od_I}O '-@/4 Vg7?<V_1sqt|z~it#d45 n#vcNgZrlw@J \wKZٖ]&Xڒz}o6ۊ8?Q/VGB¬![+#l<;^ |kO2u}eG~V@/f֌zP 㽗{͜jS5!)+/o) ^%EX,#ᨋt^ HAoG\ "nG+-bJMf1{w}=r(?U6> X1_K/,·]}eT_c/o..޺ʌHr;,*<_R#OZp05&ޗ(>ۤYГiO2;ШLq˖!8xU*v-|V-/նgK-uR9 9?t9f L}x#cNi(I62ݯ hwD0i͑  kX sbәߒȩ җd &RJg̵4_V;La'MG2YH  ϽJ LEtkpW8;˚ew).|j8QʐE-"BG&ݞ,vhu Ma8ܖO;xڮX~%kW9xul 8,5 [=ӏ>1+Bfj8SuYx9=h3RVu‘z!Nw'n dϋ/2CM.-C##$אq&< U[H0d06ۿez2\yq!i$nx_ˆ_=`jvt[>nq'7$YB |JӔj\q.>m [@ |̧bŗ^Cr9qe]HĔ}^¶xLT)T-q! H dlF #ɏѬ:/̃xzS -alb K6qdz;ˆ &t6$y1!EhYVì7n/!&J,Z L֠h26e?'#~y_~5ĭX[Wg?hș{vjiaSʃό=U!5kЦ4 ^Ud390%擻K[+J{IwP6z٨e-L a&jңi+5Reيoo7HW{ AY@ɷhu2#g+sAǩ;6׈< yE?~6Ӝe{. Z-: IҀ+^s1"FdOeպ׹`zѩt~)^>0vз{Zݮ#ڑlf$ΰ v`qVkJۭ ^#Fg،\JKW9Eu8N[WykGwm({-ߵy37?&+`߀"Q{wG>ZuoR]9hz{7_ lKb\]jtN<ɋH/s\> Afeк{ޘUسєg)"S85xnjeyf^TVY|;F`8l9!($E_@_`)d[0 sv3֖`=d°2|O޴C}%W}[6쇥T΍㖪+ 0}]`X&R`xJmgŨ$EA9†v6Vv]LGfPm.Ҵ<8Vi`#TCaR;`3ڴEmu]j9帏a9vBp{a:G1ޏW m'45r>]V K8?%UFWPsmS&ZkArwL<~ܿc6'Ņ :*cXCm[ɧ"C7j@U̙<7@ş84mjn }%pTbv-1a2+=6+?jZֱ#a,3/60=NS' |`MNK}E`yP$WGM+(֛x}Ԉ\Uq tѷ% T*Hj:?h"@id@줙LI~ XE\驈6Tl~,* q(?È*G' ojO5 ' _cRwIg8t̛[~[T]!Gi>wHk Ip& @y)%"t"xBz̓e֬KlŨpK4da8^@{T9j ~ױĦnlUb:-"%#KJexDˆUrHpM""ZѶ2c]~y%et qP߮iuYL4gJɵ‹#_J_YȄ6Tu;x1\ͱ-@t kᥠnʷ`}g!@^TkŠ 2bO}]sQ k4rњm(%I+طb|K|HY6y0D&M#IRn~_od.DM1W$bbTzAYdo`LQNX#ؒA(E7Hd bGMf^S98 %0ʾɾa"}83lr_~Ja{Y,ȧiK{sO܆o:%|7TYSk+@F$*Ut0~+k1ly{gFԛ~zCz~ѥ,d>71ү/JqC?T6"! KEU{;VBU+_27yYT)259+y$Ivߨl9SUSxR[Ur:l!tu,270 :I]+ŴI)Dr\uP|k2hz WaK }F~\<zߑDͳF63 qhDm}_) эeyD͜+|㨽o8R<c]^!Yn~Ǵ~3žՁѦ~DLgV j\߄l]y\9d QBㆲ/zid`R 64 a SsǾUYgVe$}·-HQd T G W!#Ƣ[hxHs:XeE RU<E0_x&rB>F =&{p%F/{Du>ZTjϲp|;fߛ뾾^m9n}Xʏi˪Iأ@ ʹX9f$ԃRjǧ:tE,&ItR p/U7Mm41Fݓڭ/U*,OwV:?=w*2kBm 9MM2n6D_78;)] V^ \S|< Aa\1˳2.z HڌM SѸ1kU8d"5]˴ϐzx KD7jIJT4[T"qX ^ګ%&pKxL& =" +OXvBFcʡ70 "퀌EE41 =~ޣB[#"XG0OQgydJi54ql,4H!{~WO2 8ޅoԓHR'tdGЖU{04㓛i~+EХg ўW_Uv].u'TMV ;[=BGd'YvEgI3vGό8g$21]vws3%FjBnlq~۟o޶'+:f}?YݣDyd j|Ӏ/ B}T)ǯV>UWȾړ\@H1Q&߂a}J{rE8P|zB: b*hKyG铰v+V[-UP` d,gZx%70ɪt}ꘙup4h?R#y f,v]p'3_"Z=dS.M0X e{\~dtQ6qn"^.ᓦ.JJ/?ۯ{Vrfk0?&.KV-_D<dH suư}(Ͼ{{8zUBI:FuU!1iXeR7}iS^b;(JɘL#DI3]T0G/mDB# &gӰϷZ CN=ɹ[IJϘUa a[N,[.K親W O~ҞnLej(x3T%L w8?BDrj$s{;`0{ I0sU|QTaręFڊiˉJ+#(IcKiP9 ֎# =P)m uk,J-=jhZ9I֥O0ܳ;ctLnIe(,OT[d3\mJ+A4^囎;:šm~<~*Mo % 氊i!{:a;PXEhH ʌɨLEJ̐Bfb涀gl>Wh,Κ^&~pTJϽif>l#}BB6-6Xe0Nä{mk/b*JoD]u'j IuMhx[{ƫlID.:]"yt\4WNJMg`tH&|>o*5s/UL,xʾ0=o, Izhs%""}Sysi_w'ãp{BDLƩQG18zp1F$u|.+r迋 R_4d6m>;崎̟ICq Ty)k"P^N)|&Şpvs`_ud 8{Af,xE]%uҟ/mT2s@K/6G)N/yNG5DE*χdDp?ͦfgl`>`[9i̴xdK2~NYl5-D5+r8EC1^){⠂)jԪdJ?:eݰm +2Cu=3ViBp͌T$"ZЊc9`xXUɮOnh;4c5o`WqH;E3,C6ķWFvwrSu50w'Am:dI.\h#Ȫa?nԎItn5jq@nw5mrzbAv#H bˉ|d# x(r-hb4K">-uPyuPd 7cbIB-wuVQ˩𲓍- dU!~j9]Fv"{*c𦨙6Է<н$@bp}q#XNuć;_1u=j}Yr([ZV4q1ZcUtNpCV;m9lRũG=(b|WQ٤2i4/+aBCɣDj% ~1W.S:ߥ| ELʺ2eȉܨM _ endstream endobj 88 0 obj << /Length1 1373 /Length2 6090 /Length3 0 /Length 7029 /Filter /FlateDecode >> stream xڍvTl7%1:I(nI 1 ch$$QA@$$.%E@RywW_}~uc3QUg\ ŠB@uCS ,!XO_Z8F/:uP,z!@.X(+<08rc@3]m/8/gD_ "w'BCa04 D\.O8𶖁6+;B=}x u;i R5B= <Oɚ(gu4 Ga}@`0G: P.GpY޾p]=*tp,P ȁpo << B{]#C.p_x) 3:](pd1]0x ';<(Y.HZ[B68Qqef1"X] (W[W~o_obG,(ۂ0BoGUBxb7DoWK_t5;#|mBPEz>Z1 s *-~}( /U0ODο%.% b0@~xI i ` H CB.h >% [Gab0x8 aI4=Wr)Zf 1-4d/s"1Gt5Uf/pobRMςRLGWS#mpQp_x߉ n$l1.d<驘=jRZhEmD8 VR(^C%^0 d+Qg t2幹O; +!Ç8 =O⹢q[ߒ8vڡK2 W}\nQ6 Dcf2]TUyIvp\ uyΟɨy|uI:.\:4=̃?nn jnj-7=. 5\<~UO9s}e}6aHB@: G!ZX1"-(q;E}Z5eРrz-i,l%mղݚr1SB sZmB8$)UrI1՝2v/GJފc{vmf |[˃(~)ҦPݯt/}5$Lȏ2l\}%6r:JGN~%@8>.X-z˰f7*sonή?,ʧ?tXFԐ!uxNMg@ϖFy=iK3n3CHxN~MsL`xm6agA8!ZЅ@q.ȧv!@+LkFm2~ϊe- ; , O}]F64]a4M:՛ @G+&IsFROr!_q*;0Z]ǹmUJM3 ΊMQc_SNF%ED"ߎ5+L㖷24jO+9kD 4w| $o"Dž8a#Ӌ؛zg]1<>^|HNTQ|ƽjHp8~D/g;oI*oސ^|bHC)پˡ3+~_: )Ip^ߓ{(Ux4ei zV[b[hwe9&Ӳ_>9.X4VwO&S ؜*> g}YsrN%XA{]c*cYJŏ4_^? AxBCy޷g{(rGBtY!NvV컘WCJ+ܒ7ȵ{1Ŷe4 %)Ԅ1jɰ|~̅o˃qlk]#fCpj7~`d.&sC'-M7-*2tS]mepWץ>D,OWniLT@FFD{kqOm.pf&P4J_M|EtjqAyM⊒UMTj~#.'1HcH,.[(vKl4i$1&DϘ)6< n˛][$g.7̓?>n~`Go*q/͎;*w5We`GuX5Zv("]zN|Hsu8nmrF^ɯs:Μ]~G[qU+ҟy ޥl]5jkܟ5ѻL&ܤҪ2#ygWxyAԶ._W}`2[hV%!Ҥׅx0;m lew|CwVs k1md!2U*f[GyYa;݌:# gD^`V gOl}wX`[^jo L6Zaumu}x_p$t\1x`Wcؓ`ܫ[l<{ySPen~ƀ(=4{ޕnһ6gצ,e9Ijl,_n Onkw^ޥ>Ǔt%G^w~_8?_֢[לT>͒@)5;J?v~ jcSۏ$SLʁJ5@+联Z=]Hxt50ꨢ\_|J>kdsۇEW*e'M}eRt8ݖ)"%W#_G|يVWlW;Q)zcK_ pu ;- D? gKB([;}r mEJ4>sYo0 ݼjl3r m#^lS4)JlٞPxy@c:xFf̽$K *!j eȀIe^+qzo3i);\bG?ӓ o*(>s?@2*1u>M NI6tB:S PBq3EKx_K^抠-/WCI\Ow8׼NK\AV EwMSG'gP;bەQ{m=X~y кDP˲B'XꝮVKZ&=߽'[vody_=0֛i27KUΝٵx/~MUCgiKyD%,, Wk;{ME^${3t\{͌TfKI{4'-Ʒ1Ē!ܳTŎFm`JHfj Ki Sh1z/>ɉ BJ{2 j~: 3WD m{1 ӷ1$桳! cR%0:߯:|^4ĵX: ;hFJMh(f7ɬE_6 鐤=!B(ټ nER 9N6 2_q|=9k^LuЉ#nf&/W6$~ ̣#̢{u=Gb# >=\/. ~esmZ ә[{wZ ~ p[<7?as:YgAh' {!Y/̻|,6nFdjxߨjK)q.יn9o gLvt옇麵j#ҫ3^4"rli ԣ3ˀkbgӃRk-ρu_2)K3&C)!1J66΁~ۅefR%|*\-ռJ՗ #^8UVWsJ`u T>&gb^pj娑dK{ugke"Kmi{ҷP_) EbVY-F] :qto/guxB3hFP%G`t0kˠ -䍝hFWeYOMTq:&[ovt% gu۬!'?gBCՊ.`)(p.iG:I4.#dŽdٖحbԝXpaAybFP,r%[L-x\^-eV吔UfޟIyM>ЪWgC?)$K|FTop\NSܳE}8 > ȅ9J*~|"UMcu%F%\A ]Uo*$Hh$ve{E6UY$erXIX!|?Fyj5`eBEd}Xܭr3Rl[5xZ=J?g^ 1JұI4B.c?3{ʽgìKmϝw#zQ.l[]\Rt|ҍKɭuB!,e>ʒ_%g>2>p |mS |^K-/kUj_[vd~Q36[Id<@ )=)5Vxv׫S(ȪpEHs\`~wdpu-.F>CY~MUq*kw ӚӖdZ,#9 wҎkz o~F_vzP܏X lzh׋LsNsig#:0{~D^ΌUP] Y,gu7]DhUrzb;@}M墄. _sO=yQ$% ewj:ԑ6#ٴ1+˥W|p{7UV^0k'لZNTC.#AlDu,"Gn,p|ωM0fy&)+n㽝pa+`Bե,S}w۷'[э/z!slUj944! JnX*ӝ}IP-GU):=@?[|;bL#Ykv/3|kWDtY h՗NZ!3k&5c:$j &.k@ǽVPfH)~ S#^ݭ*y}G$D B*^t7J5kǪ/q9:F1=P.O`$D,X-\:~⥙_mK BCL1+4:JJ9l7"P~g)8{n2ɞ N⑝4'B6RMT.XJVs09L,ųhnW'qG.Y,Dݟo;B=! &meE4E1RTmw$u[3xtGB<vMS㐟߹R.ѱ$CDE%~zKg?1P endstream endobj 90 0 obj << /Length1 1373 /Length2 6093 /Length3 0 /Length 7034 /Filter /FlateDecode >> stream xڍtT/]%Ȁ9H#) 00--J#tJK(ߨ9{Ͻkݻf<~mp/( P >"bc3?Z"6c0 $]:E ݡ#PT(&)(#! Py@Zu8 $bSz# (t\p Z #]m!`?Rp>vD\%<==A.H~8 /r胑`k\6{0~"6#GmGy`Z؂aHt;k 4:`g?;ѿA`ApWsC`&? ~9H8:@A6hߍrzzC" ($?54KV)]\0W} {|!0;_#ع  n`5ſ=*(vl~%7v6Vu#!`/`mD ( #OvlGFo dƖ *&yy(OHD̢ ݅b`pğfѷ=>36X0?7E0C,w?Po+/a@xuGG3߮OU Bs@%B/.e*Fp$׃ *[gD &?K*lv%$" ! o"ђ708 @#~SX ~~):(Ool4~ſߜDp[Pֳj9OQ)ͧ\|6 R4+>+q.0_~kÏhNkJҟl!8N7\m/!#ߵq3vf:[8nՙgWmopVƝI8XiW63tx(>&n/)ʗcIC6 nslj!v~ZIr `SĮ4&$ |R_R)dI@jHz&j3ڐR[iuӃr+Q^ujяza~(It)i/9K:*J(9镤+;xz$LiR8΀ہFmCRn|qnV.CǤ1K 2/tx;\<+1R]0sߕD55bM;EJp@*δ;3Ŧn(rD>IE7,(sA%V=0!J%a8.aS>h;Y&`=uʚK#H|!PSynf/1T4Shn^B!KIi!! 5J-#Q(ͼNqE3Ɠ#GZHLwW$wC>4l(B~ב:S6!U/~5&, YOlj hy̥U1 N\Id:v@ SQ/]tCG2uk@uѝ,$ ?c}Q0@u=44mg z{ I.DmX6WD(LkEhni(9}d{az 1,Ũe(ǻ3e,3&—$O^u'5oU;ЫM-([t` ?Rl}1Đ7N.ĩ2t7?ER=zYbf6]pD`@g31,ܹRo>3kMonFJy_^t.~X] |N"K#вMd Cb.ך"&z B##]],P A1±V^aV36~jzwQu0<~՚ζoULby[p#i:m:w \!ܾ-onVIz6(JhqSnuߧpk#Eq",_U@i CF)(؁XkaD5lPB- ^K=&j2}EHLjq2٩Y 13̾< fGSiU[x"5O-ݎ7u>1^E.)a&'ѩ' J:^DN.E\&mدg#bCbv^~v& -ޔ*,lc@+nNG)d_LQ0:}_U-!8]0ˎqksm1m 6. Ǒ$2Z{ګvZG7Ym&Ќw#0Gf}P${Ǖ])fDDzGbez"uO>sl"ɑÌxG^IĺO4Z >A[0OT_q"2Wng]ŸխTw ΧRټos`bA=swǴ-Wer{*RP)N{^Ou/|fYڏzΜ~4N NA)lV#xbg&G=We\[i3SSM/:Xа*s|^4OA#~kR2Vq`L׬=GY¨Eg dw%nMz.+1T SFv7rTr]LRSux·{pD+6:5YE#05.h߸=0п# lD)cZ͓_g)'IXg6}ܕM))=fL#C~}wiZ'I*屨{lּ.嵐]-u$#] pdi+t}%-ޮJ=ƭ? _(UwR&x@fTf֏;;Om-(a C䛨LQO'_y}#kjɔB̞UlU$uw:yx4tJlRB7Z+&2Y'cdy䴧}+ݔfmycj'DUzkɟX ܝ=XE-*b7x2G>[<9ЬOgș}u^=?XecYʀߨS0z@\)"Jҙ/~nwY1z:|wZpaťM*)j/b-HΫIƹ A’C _?cG>o\}ѭ$JrxdU=_!;YH}U, - o'PWoܳ L|] :Ut&UZl¥RFQ'iSW%bgGO i,CG_ޱwȓRi[J)`\R!zB+l[4Ct?4wSK5uƾ>VkS#9c^z`J"BNu0Y,e,5v;4fc>ج]™kXp8Hx>:4"9 P6!K@Hf./+w52:' 8G'0c@|#bySb?C(sv,l_}cu (g&1y6Qyt+z4TtHHVaGR#ikTʻe;m2 h v2\pI_c!@ڻ˛xԑm Pܽwyn@.=| joKLy[0c-lrF2[f1*1^5$WlyNvGZm A>Nh$!JRt6ܴѵ)cԄC]7ĔgWGScmVKZeWІI3/}FUTּXkꋪO%y~@5drjoSXz_yecvФ%^Fw ΂4:[Ay~Q5ewWHG)]3YgwIR!&y:gB;!]| +V\8t\GuX mz}mNv-N?(mۇS3o ;z?lt `VɊen" eԭ$ca~f6Us< /Gl#ڿhD;M2slFp^b*U yµR69 }$ܓlF_7(u"R%k9y:t5׼I bKc`UGܾ̃#-EKqiDr&"ViJ|Yςc9(C"U)7ݣ6%{5!9i!E͘0o"ؒ]3{Vp_} v Jv|'n`#uAAUcmͰw!}> _!1+m%O=XX%cpW/QjpAeRQ}zsJrKCy3PE5,('v\W`68cZ >,.hAQ Pgt}h=,J\"a.hR;LRXk:2#[\eCQiV[ٶ--dÛwQ+Bƒߕ^ȩԼUq)ey`ɖwڑ-^l7f@7-lHW0p+ YMyGQym!FF 2JcX>c3V<,oΦ jc-v/enHy.Qiʎ8UP*!ᅀfOnux\'x>|\vLgEO~ ͙T' CMk?n&_~5*^o5$ʽa]-M'}6qx,ez4rtxglޗt͛=!pk1!Z%xu@.;R Ϳ9sp Lo1;8!Z#xnÛxectk->g)6pzE ~F u`2٬ojrVS8tl-\5\KF PÑ4AM7=G6}S[C]IT"2VմV.^ۡ9 xW_-]` =1AD3M&ī^?-~){?g>cAM]Q?a|&_5jzhg4D\%&J=^Dt[)þN>ET mM$m}'݅{M0}C4C$M'{@͖L BN5S7R*9?ziZr. 8$x7{HH=5=ۊs]và)~YN8?S7 -) ʩb ?I#C>u"Љ*m9[OQE >OwmX3z`Ќ%}]nk;1Eq*- IuF%Jz{rAdEګgJ. Җ`^]e|lw3`(=y'Ǎ!գg'8Ы|[qM` e#&"VUp[&(D$_a1vy$ê endstream endobj 92 0 obj << /Length1 1379 /Length2 5903 /Length3 0 /Length 6850 /Filter /FlateDecode >> stream xڍxTSۺ5"wkҥޛH !@HRIo)Qt Ҥ+"Ͻ㽑1o~m9#FN(G e ňo;1 G!e c650 G!:^HI1 Po S;E:($M̭;`uA )ew'F;" p()`0>>>"`wY_ǸLh7 kdg4bn =@hl Vj =ȿz6+ݟ_`Fj`|1B0@`o0v~h( ̇x=0h4kF_i۬tREC4PvD {#`p5G{A`&ۜPFRR@}!. y@;3x<0 8 !@4(?\@'8p:ÑΎ5Ca >2 DEt,5T/  ,&@ $@JJ#V CdjO|g.P߿~(`@gWݑ?,s0X裰Z@7tNp/jcX5(#Gk}NFp /e77 5B0( |XAܰw4]PYW A9$ #ƞ5v%aUMf3`(O_ JD 7e^4 /̢ r#:딙}7G VVb%0{UnrVG]Pp+g(y\q:dcVr|TˇR=[>_u̘x=G\a{ T6^fW g*qm߿;XHV&_#$e69WGET"==gAjt@{WjMoP=G,9fDIZRb\sp:g~Gӱ~OϬp}`8wN٦#?Z~+A핲"ę7EVFzWbb7kZ+PZ z)F]/v酥;IzQAVk4wLO{z&.gl_Jkƃ綀Ғ>o *\6ΐwtPd,Gs~ 6h[A^[i84}3[ _M(c-岺|RUxSF=}In@ׯA`Mr^zױ/~53a&ERfMcbyj0Mw[~bOC\Zn;{Uߚӳ'M{ϗ3M+ ҍwQ;[Fv Y> ԥNCZn|3pIlCF ݱQ4COֵޯm [ϳq*eiGRY6My:<#4b^xҰ!0!M6G/dBk*Hǘm6H"L~ƥQᭆ@SdoDh_?>>DMQ;Q>;tr`!-U=[^|6L&Zdnm7CyUJ怔wF(Gpf IdzY ?x16=}f7'nȖRJ4im͎?l/LzZiaa.ӔP.q;4XY(KREreNTX?pV`T88( zr`H`:CK6U<ZZo0PIjPمr0:jPLYm16 ȶ=V"=8r"R3]($F{ؗiI2p{x|)yJnv<e]V: Օ;!{ha[>_SU;,ćc>cT2~hVٟo:oo8Vnv3 6epIZfGO>%S_˓xq8f:6GOm|̯ ~%9׈]9+5,yOr'N+0CNh­X6Y<ИԐְKbr83n'6MUwhlj#A_Wf4l(ˣLM\f, /M ^ޜh)+f!ޥ4;rHqdI>'‚|&P2}˸4:V/+0]%gwM#VRnόzFƨ]c-Lm~tXyp5(*|FZN ~L,=_(F ZX +q[b`;K'9'z?qY6VZ(»bK~eLJ6Y~|x썳0cqmdoE.a%gF^/C <~Tr5?=u-l!D0z.}W;.xY0uq{gºs*='+ P)K1*!#VR gh6뮱o ֠u}{ DfczI,byyNJrp:$W?3~g fxyڟ6LHN Oc}#KYNHȭM""f$ПR*{ KniVLE=m?`_S[16Yn K*Zpi )YL9 e޷ڵ0ؑ-+0 I жnO,*8 Z.Y4/=~̺tfÌYGeoVҒ ;5xM,N޻/4Q98IXD&cw]NƾHle%ml  :{( tT :C9< EȎF93P(c+%qab&3 yݘu8jT_i<,^fLӭfqPF.U"j;/z=Y'y*Y9UT\DU4 FЬizu'e6_y;| hO{9dw\$u更ƻ4F ua 5`F:+ED|bLeBZ:z x`=eXi AGfsBUk ]`\͢ᢵXraqqޫ?Z /j)U-3F>`G>I5ZN+q| Y.Ii|3ʔ)=j㷄?uxtތ5>+@wu*sy&HHB̝?dvZ{Vtwoց{1Ø-Wȃ^m=mT Vx>3lo< Ӈ\]'%q?x%S ~K/SNICu ׫ J&hC4+&@lM^F?~ )\{l"֋mM ykTW6 p|̌muFj* =j~nٮ;)X1eVVe^QYk '!}$0CQyHKٓUk-VGPjZ`@Od6#.Zj GLG~[i5zTNN 5u\4ڴkF/Onlݸ3wz6Z}dr ] ՟^WK?&k(GC͞"՚1eVԉ"Frf/txS'bܼJh|Q𓯍XuTz`GډgV/D6__wr6_0Ģ^h#zi:^iٲ-G&o`z"ɐDi4BĪHa|rC[cKV6D`jn8 sSu)Pe(fyVRTщ;bUk)].9msKz('%8EI-f/^t'ZAuC ,8~ۣ8<(ƛ_(hKYFXg`AyPdqdIi(\e֎j8s^9ō/}J8S3N4H-rS! َbE{lg![bL+aA2Ƣ}% А|WCSuM(]6lcKo\Vݙ<ħǰTٌG30T*O[!Ŕ6{Gcju\=ҷРJtQeUPL5c~VFr5~<4K\08dC;R`ME?r]z;P+PX {PGCr}#jKֻ q܅']L-R%0;/xcFTpJmDOĨc9tc&F86'9!ypADr43#{7 }s&*H hY)זWнRs!z7lwIU:LJYj,\!}0,OO}2K`9 ˩؜5~ /;#dLuϞ湙gjSGWZ>GVU)t _Er|laY#=6@vȕŽo xOղ4]&!4kY.qus&owng> %('陈>nmQ兗 bM^ęs:t#^&"ֲ3֙" \OILތ8!kEuFl]ѩ C-Ħ(Q4¡:AŲSL b- CשƖmO N&0&i9vUY\NߕߓJfj<=}+,^шMU|Jj F4l=7kkL$mV7cb`vij齁/oEHm9/Ǿgۛx j΂" I.K3O>*<^7oiᷓj:$T~IYD ^e~:;tLXqS:6@ \$1\{`a1zz N I]w5B|RIaJq4TC-vt4HpX.Z" V}K!^P0$6B6!;ED mi klE endstream endobj 94 0 obj << /Length1 1540 /Length2 8936 /Length3 0 /Length 9961 /Filter /FlateDecode >> stream xڍ4]6^.z' f0DQDk#z'B"'O{oZk_{9> zOem`V`%񔗛G afև q p */yW0`S!5`P37G/?WHWX##U r4`P0Y G<f# vX 5^Mp=<& {Zo m~ }h?A` o//bX Pd0m y/كl`PGq@}]m]cοZy|  xy Σ UǿbU0>%߹4a#tSAS,WoEJnl:x/!@0 u59`4BWGO;hCOys@08 ?ÐY;<"iAfUZl~ y$}Jb0CG?- M"QP$iD8(dk/V}a@~ /^t"Zvsu}xùxt`O5,Z&k/n<ј(LJ~<6 j٩vU&n"ej/%\ȐTm|gmw?,S݊ql'٬zO3T7ƏT6bTuTVQRPWVe?xZ~3P(/Z]ZL*;4+zD><)c*5k=iޱ5PTk,Kn=@7)3圣r P0Γ@CB+ґ:jJzkO֞@jkqr?.K -C:*-*qC.#qϭiE|=7Y{O "L[n+O-ʏwmΞfTϥ!r$RM5լhyˁJ&ޫ,@ADFBitk؝(v73 L cjzov&,H~E{Ma3n_^QpxGD\'cin ==%x%>Sޖ(RǓZ":ˊ}y$3Ͼ0﫜a.{_x3t Hxm ݆Q?9g|Z+(%4 S0cqH50'~ N}Ư$˼#;Iz .!ٱm@lzfv]V" /0׻` )<'5N+.?7- $b0YmשMԮU c-wsm.W^Q.Ѫ:gȞs4zЫz~YU8*|A8pJ0[7ݢX{JMyTeU/n ?.3#:}=Z^Ȣ4YOQP9K̟n&:㩂RGAhu PhWһ'1O-$)0+lqkC(Uɂ`J4vS]pђ7u NƕM\{f?ot]$ڋ`+| qsܭ&u%&+^,iI`Aϧ/pM{o_&E>z "ׇ ҃#Fj&]&A?r/z`@qnC_pd&ޜ6~{ˌkʳdZMц}ڮsS[G5d.:},NPQ~ܴwam'^]Njr+M_/5u][yԨW[cYDri13%H|bf@\aar8U *JA yTq@%M4#̽݀h%;<1eA^?ۖR mG _C5kg0_$%`滿;!:.\Y0|l1E5uƐtv9Pw:Ȫ_@oH2 uzqԩz\ѓt'ٸ݃O : bR`AÝGύƘ:4tĴZ;M:G= Z2/$GW-ԓdBK%;.w}Q1? *yjCnoN@E+77U2al{]sШ5ɾL\5JꊤoTBa"["eJqhׁgqfH 'D_->_?ysODw@*}<32\:`=z瓗[V'EO~B6l/Q)Y6!EAUɧlZdݥRf̠7:%ݭ" |1m`9,baU$O?"02lӻr.#M%0}RKk=U>U%pˆ2-KTH6h_r[}Ew ][ŤdWم&EX.qRܻi{U%ޟZ Wm{gαP%NA*3LgW8cQ$* @VѽQ7ZA>TLJo1u,ӕ~_dp`- _Jn}_M=dMEiU_CBFo"08A[ĖYJqy0>+5`fZ) ] S`n&`W~'s"`i$ ͊Y&%1좌BESٛXt  u8kY_Gw(Ngsf;=CgkYR *B8Ee0lIYe 6",1d yV0STq|OsX3SGHva2|3PU2΃w $=wl7ey]/lb/X1|f3ZSGZ]sJ qtW0Z::; -GrEl| _[]ƊkU_=SY1;E.c*oT |u_WjB"@ 9].*lpAib[swJ|77>2&3o+?BgdyXҟ?fsߖ>|܂=uSb ){3}NSԿ}Oj>I$܎Ij(PvB.[un7KY: uL}`~sQ۩5qb +V`X 5cߓ+XT7[< U8n}87/\J_^f*^* 'gMC;C<"<3Rlyɟ,[FS5W' :t%ȸW=j{ $Zl z;fPerEnj=sz@j'P*t%3]Vtk Jɘ, n$qROt~kkLEs! 9"pIN\BQ؉hД+q:\B{95*|˘-=0Q9 \r\z̒IFMC!$j/©*abNT&n8$h5Zk @}22F賓_Bo`#˒ULZ b7RxU`gη*Jive'P:zd{͎U縭z#b<PBŭ揜j FT{NíN֛QoϘL/j{=+IdFFCb0hgz\6NEG_Hc"T30`?!Ɏx'=^NheZ))+ղדoZϊj6c\SP#BD;4IQꧢq]lb6_o)n?kgqm}ns^')TgSmx:Ҿ=; "{V8V.Uz@#. vT6vsχ ̦|QtJ9Rp*ߏȆkJR|qS1'Rqu 9x#~V9@7pvvsҢ%3Yqרt^rݾSe_rv5>c]Tq_U[`lQ<@3P"f+峸(Zf 94O7 ؽaY'})+ܒyt̮>{irZOo Fв.1Mԗ.\\;뤏:k@Jj*Q a/qvWNhc({R/dm0W3M=z5y3h‐i < >C K Ct3x3l]SNSXLl{_I%_,)`}ZK5kJ b)mv/ _gQ [ĉ$ʚp$"Boߪ=װIvR-}g21C2"Ar1Ew G^Q⋆K, r@~fʩfIY`rzΐhȁcyU_YiY~ªw qv˓*E޵- =US%6,Y$XP@{,d>f_bK lZD]X8Y:Vr\3=ABfD-5Vvs &^Y`ҵa:BjZ4aoCrl^džohqކ,o5 'xarhwI>Y8ŞZ(鶪Zme>.sNhj#T̤{$O#M1%XP;U[fHȎ1T0sUP`_c\Þn|[@LAFvs/RfvRM=v$"~ݛWvg\T GAe_KW)a ;,Lկ:^6|-K2_)ujMeDw捅9ԓ$cP<wA͗og*XYP7& | A鿀Ob;. &wt6Q[,w)bZ)wwEUSVy)w48>ĄsWٺ~YO Ճ[VYp@Ֆ_|y\+B %N#NXezpCͰzUjN 8jLuU s";`8mZ#d-;6QǕ2fĔE^3mg-bpPyA MW JI(}\_'S)+@v!^XյhnQ)d^X.ee;NjvT=HͿRMkU9UQ*u}5p}VcЄC"!of.fe?ܖxl7FȔQCcW9r{祂BU+LZ`;ux23fx*R!9vPTc6kIǧ; }H|Q&zs0}a#-BkˆE)sSFE/8υ`MsIƐE% ̟ji")> zoeJ-\|CՊٲwٺ% 9(s&Gƨ_j Z^UpUc5bEKez+d_p*zi; ~-m8VWY*O=\~+~c4t-'rŎoװG?c֪$UKtRng 95}; ~4;' i 75ϋ颮%3=kI#.03RkZ@\w~7::$O{\qDJY;d8Au k5)> L]O'iC3/"'KZS/PqfCgT$T.V ^v8T;/[maFHYoVeV2~f}TX^omǛ^СW!xV.}QK|4͢j ` ҡ9siȂM0?#LS[(RY{m[w o^SE.}1!0 68E]dITTBXP #}b~g >F/) \s`LLW9Nx%?P5 u*JJzy4NȯT"&X` 7Xn;D.vIkH%V ĚM-uhڰ]6djn>9=QGU3 n1)e#7_1alHlah dWPǧ?8L˳|1j9񘅻eX+Qkd h}[2z+jo[අU$mq޾鋼h?H?~\5"Dif߼=zɻi:2:zkn}s.r!?6݀ҹeCm[4Pyc=(!>;fJT LiC$wռ}f{?_PUX/Y_?.xb(I;nutǡ+p+C(HI z'|_)aِ;qa\>PLnR38Ql*If0:0.&p6`Nb]L֫YE\(Ԡ2jke;Y5JTTu%QHei rcnZ *2Ad "Օ3f5]#(h4zn![G%eDm); {WyjQMM3jմ=1ӻb[U/:y~ ~{dʶ:H`fd8v{6nGD\2UXiW*WEb'AsG1yon1>-cO"j"t( 6 :WQ`LDL:ccX=Uk,+`2LNSh+Ndv'Ln{FvT V}jqsOK oo#` 6gRiׯ3~*%jq:u2yjZjHh!|WJ9Kt|I!yL/AV,RLI`s2V$ "HgXCs\Sm=/apt~.,9SNלԐ'gkau{7ĖM2%E|g58D&NY1ğrb}ҥБBWcGմZXOˇB 27'H^G?*;I8J|JzEM`2 )jrG.CPE)0:)ף7YyDNqKю6tj:pi&򴬞j4zIֹs,jӼ۹IN1/.|=9iUBSgMNFR{`(Ɠ9`7CԨD\;)<I] endstream endobj 96 0 obj << /Length1 1779 /Length2 11233 /Length3 0 /Length 12354 /Filter /FlateDecode >> stream xڍP-Cp q in!K'8 nA#w&_^Q}ڧ jre5&3 Pvbbcf)˰XY9YYّAN6ؑ?A0ob@cK uqظxYY쬬| 8č]@ff, tDع;,,^#Δg:@25,/MmjSJ Z:9񳰸2:2C, 'K*412@huK_5b/)`3;@MFd,W#1S? &Bl d(I3;91f8B^]A6&/R7H_&{>GS##Y(r`31-?qY\k0d1d ;ńbeee@7SK?ta~b0 2At4vޞ;!@No3/r 7.IEaf^1ۿG)* qx2qpعll.VQ6̓\9ݗse5@"E@ݿBcb5},?STG͟~?~c[/uvz.7T*@ζq2~ŋ8Y9%An@3e_ˮǾـ@e#%|/Kfj8HOebDzsqݑY_d{J3۟b0!N/)?. "/`XAKb/z31o  X/],~ YKw;˗߈7 `  00~@~/~/T~/}/}~/||/d|'6uvpxy\n@SS@֛j"WQvN_H.;*bs"쟞X V"ZM6Ʊٷ::t8fx'M϶f2i*+,OiQaF"rdU~nr~(Dr6wkoFf'w/M(g4xsKGg8&ؤ,ѬegS&NmNYnhn;}݅ -6{Bdy)T܏jKƃu=F?"酦@EҌkw61]g"?Om^lvԿ6ǻ?R㦇 9 r[uL|#O݉HOiZWa/\ DzNC,P|gVtk9 *N]P g{ }0.RDGz  j~{DS"rb{~:?I^TH>$#Ex[*de` UcNFHcJ{r0(V'AgKld7mˈy-,$`rJ9/e,I1}W{d\ײl0UM5F *6#CqS){@*ҳ- I3MkTq譣%b!\&.[:2J`y3ٵnvQe[dC+aV7 ] i(ˉ5D)# yQowo~|$ٿd(U(B,$8-o`.Z|A r"NWyծ`5ʯfB1"bZwK2)_٨s8iHZ,3dؽ"U>nK%zz ڑr}l.g% Rp&, rv/-|ݹ|GHt}y:֛YN:3]ZrjncLGsג@bjWKX0Ga`2| -Nh.N"FO#2Qu9:] ?elQk~?dAa^+klQ΋3<Ʈw9|_dkD  &X!/ c8w@*\[&nPew ?IR C ^[%DT*^b*Lߗ ^"SӋ0A4P 1ȍ&@`hFd8.x2ݪ_L9~ԧum99]_|"!U2+Wr9_8@"NQPʚ{!nk=4j_-š(˸<}W¨<櫼Eb+4@7B!W~?\]OQ`p}}?RwQSgJbJ݀#iNL:- /P0&[=Pd~\C;)Sꩭ(\'d_EPEٱ4[?t_hºΨVn[+܎/R9)>*JW0 "N3$5K fIx4#_f>ǰFdUeɛMj53߂.K?|ZC~:{ݒ(QcF3 &4 N|Bժ l$!?t?r!ȁH 9G1OͬpUi~|i/;ey[gJQ{Clݏح :>Y<~p XHdfeNb&1!!wg+~B5Un4׎yKZXO,]]"{OuiD(ڣrxZkPf- >q2 OZW"| aoAqY($[: 'n Ƣv$J}yqG/RURx>]%`Z7N =\,:BX1FoHpe (sWR8F",#[09 ֩5gV%Ċs߮!o2Hsjfɓ˺zcgygof+SM%~cȇ ;A)_j-JY >{ mż:SWMwHyhh&Me9vwu8P42ÿFHwԇ# ܍2 S|1V1K3$"AɲyO-} ekhABKFIC#;Qeeo4 gzSG2V{.~. =ΒRLؘ6rI :)|iwy,Sn& @\aY׹m}aE ȵϗUh _?, LhGu>o]nK(>wt,cX! '}Df)Px,ae C)yx 7:PL!JZn,OtЙac, 졄W,fX^aU^Ԩ𐛇: ZϚ)8nл 1%sov|tAG#֟nrGfvzC_S ŗP[: # NWΩcT@>X!`h y\/>~fg=-jv9ƾ gY@sNWQ/VNhH=d+M] sϪY70mx1~3GC-aRV@ 6+hȻ*0qQ0}*li~ < UNZN?F ="rZRPf|u!+s75f{OmɣwNj88-(2bimJژOH6-&2D/iˠ ,!)Yd'Fp{ʖR+VăF`EP= kpdG:j?Vbδc@A8J1kdd\t++îh(̠$'& &l -WvR4ǵڮ_Лj9QuF$A/ǯ@@S:yT;jgNBԬ$ $ַ=Ɩ+E!MphF86[4 1;,1h(of7a*X^srTu 9[\Ę](OK.&3l׍Avg76^b2pPGq,X”""s5g(YI~2W$t5|O<!јܧb$+,ԌX狡"&܅uC.'o̫/i60U ?=#t؋GgBEVvI!+5||;f^ǢQn'>CQ B 'pәI>=A] ?Tp}GUoU4osP4aOӢ5hem-w6Nu.)z uAB=K1EnPb--a+r5> ͽ@]E#^C|M <yl$@(=Ԡh%.'v;h^ 63GN e||F8HKj_i|`?-Ӽh_wJ#fqgYj)Ƅ3Lʇ6o5U47lΑRLKDuɃvìnPA:қH{RwLY!_jV>fo+g3vNF6z:0S_e:xdjW (f?1n[F‘7nC#4Jy7pmț=Śd.fI )Lȓdd }= E[ez$j•[>zbsZB,% hC|9~!P`7(xk}rwC]j4|$%oF~&0p˞zÞ=Kc.h{|ev ʮ:Ny&:@dad@14}H"rp9}fn̋d}{}RPQc 97>GpDr ޱ @vcy64ұ{>{(o/Ђk b -ǒc=fN㒖%ѭOT`е~L^Rau-G)iqϣD`3-$=-[dzE j}}LLa;uFIb,Z/LjAz$:yh@5l Nn(2r1oy.4Jgq~\֌n-nuq$H:yĖv!N;YҼ֢hrZY)m>\WTm@KiK\Eu &1d;iE&.Sο!Jd2έZ`׫(fVh)TZ q6s=2h%wtěsڪ5>E/*b}tG$˒=%kb޷ެˋ"ߕY1v\oXI>F3~6\l69*!7pUkΎ8c_)qsg4\GhHՇPbZlnK4Íf1!1ϱau}:t4ljU^ܿFzx[tω+CdG/"׷K4pVo -]"| 2dۗR ](`F@085ZE9, [t8g)Ɨ%xȰрGWS22fUY;YJ[X> ?9qW|BHǚz'!>3l,&NW_Fog5XGWcLUqy|BG߰ 6~hz݊#'إ!AzQb/mQ%1V*U4Y+HρW&_6H|w ҷ+,x~Jp%(_bsx_$+ sF]䈡tF`Zާb۱kxko+5ȼâ3͖.+;Yu)X9bQME?^zd. |ryY`8Z1CqY>CEl2i(0aֶMl ڑA|r16`QN 6Ӷh5>x'K:0ڶ\hR6d7fufR\Gx|9QtC` -^ܺjpYE^52".7#NÎ2{Tp}(Nv% =A ,- !ʮsp)ք*$C}RZo MՃvjxQ$eseA" ř頀L%5t̺͊MR/}3C)͜\^sarZqCx$%c R.bm̭ wgmf<2 qOI$˶ͲQHLerR]Ia'LTBaehEo Je+܃EA\шޥw3%TOX6b|$6pK9)пsV2*8)*&H`$+x7:PF*iW!eey{'jM9MO {Rݽ(ԱR?v~w*V:VdUO99WLg Kw2xhβ0eI[S\ [;(M Qu,6 #":}mÌph-D G 9U _9[ETW\G$ӔIgQom/؇4%\RKց "^L}eߢ2URu&[o)(dF8:2NIL_+![OfywGdX 9MIufkwlۡ͛N0l^jW޺ɱ{b1eE0XB_Hmf;4V1:>Phn?QߢubD!b#%t53[p kvF&yv/ `Mv|KǖXA>w޾Hj\Ww@; ty E Km3\-8dPɆ+BcA*BQ8nm(pM Uf[*Lf2&թ(ҭZA6qrt~ɪ[v;̏vvugW<2)QgrDurb tg;0Dq]0uZ7tm{Nkٸ (OT6zߚ%}-]XC.#2]>}Q3 UR32PH`Va 9Lt\o"$*c[Wӧ65-ظ48OKNucZBHCTW"Hpk BiOo{^{XKy!Qz"ڂ} k1 ¹s<]@CjzI]2]>_h'k.A;N[T_^O 7M@׽El&q+Gd:: OޛI\4:NU؎FeJ:߰t+[LM[2E?ޚң2 ]xK+"ɽOm#$D,O>jz 3Ni D11}4旛TN$ZI>,~G:4Tq-#Vlnqtӈ{T._(ht:?Rgh(.T; KbeIE\iZOaIVIeTR~ejY@7-"6ثԡO=E&;=sn1OOu'l4б2D|Xnu?j ެ|Vn~Lk,È#DU.8;!:Dˡ.*$\KHKlhu &Bh]|s\~9 -Np j@.| >єBqz󛝫4)8@g"%s~X3>gPԈs.,`S$ay/"xiqt H GJÿ56)άT |8åKkΜ\R y=DNJMjNk\5ZZ'fyCtq.nb{&dbCc4LQUm\L 7gk_pEF8xhUGxFCjZ SݣZ>EW(ֱUo0oBa٫s#1Hh,ϭ024#oUj] 5 gF.w!6A}ļ/d@ӥ> VDƘ_+D2} Qݗp)QMm}~[eUp JQߒ" k3v 30RfZlIjSSv-?Y }.i[GeW@WUbp?vP/D|S]wX!TG1਍F㭇)=Xpjod<+tYCC-h#<ӊG + Xr}\z5%Np:;pmbl//Uc~ ,3\ q I= ][Ҙ$c< ҙ5h:s*zyN@R:5e w cpCM"韝aBXq^i ފ0dvSS?fdxH{ <9+ eu1S IcG>63Q-*YiAh{`18h]5+#)u Pq iVxMz 7+U%Ѣi("w\υƘ? ]?mZ""1pųdtg-$IK%MaZ@ CZ+Cz72a@THύqqxD00 ҙ(I8Hq$ޘ05|ڑ0*YϫF8^O& Uu!h<*ބ- +b݆XיP/o\u-CF#bQHnژYDMנ}kDw}^,fdG={DAXJUa3~)Lwda99a*\@+ 4x#$5Y{ͻRur&ܓ/,Ol8 }Lu˰-0mf ULaf$>,Vy(*ki4͝00]I|^Lҋ)B~. ߊ-lپ^[SYnj)D|\ztwKgV]_|!Yc*J{2|[+#(s Y}g WBv!ؖA c?#rl'T/Gń!XPzwhM29> stream xڍT]6L(ݍԀt]J03P !%t* Ҡ >k}ߺ׺{}}uε !<n U"yEm## ?__tay ExPD@A{yԆ4<@APD(*GH@^0@wz(|0{:(y(AH`3A*.DIy{{\Hnr烼@0g=Ay}_<҃#2۬ Q@]8S!}p\ޮ[v0WoO7'0wO_{|P$@_LPu@}|0uvswi@av @"< 00` j}^~@?{Aξq|OMkrE# DEe6{K7ku GYiw#Og??q /Ľn=3 C\m(Qu$~]H  у!hOs s=`obv=<%Gz?;^Q 2a+I@XFx]=@HE|J]X>,1?}y?99_|Ho ?j@OںgU@8 pdcuXyoQ)- G+4ʜUNMes j1#S[n &Zp)z kziy~< qBoBm`w#{MzݭSS81]){S6$"x ؇pIFNa[53~<کͨI'Xv_jPŮJs0 1IkP;#U[jBLs m8樸켽UT_6u[mGb_D! ^ : M56q6ȗ~EicОm:f2Ww9Ml~-WIYIHDZU/2t^E*d])bma>@Y"QY浟7msX-I#8ȌAf4wh^@fc9E{Cȝ벂kc{]f"AA ƴ:dj¦|7̪{ w "{RP)hGޚǩv \ɮ!JǼ"Bf`RS SH^hca»eô]) ?60鯌 1fU2OCf%jdm )ikt>kk4.CݲvewvK Ŷta%|^D7;~򝼽W|y/  |МFwqajLOJotjh>d&h>Sx+*Ga$*}G+60ʕ N%v159W,gh}#⶘Z^m)u'FU˜:4?62+w@9r|r`79gэ9뷜 th8޷6p!DL'O&Qb,'U+D^MC;e|o ⢩/(rK 'F2Cwzdi\e" T lX~-?:HZ@%ϫWEaV9ױh@{!F6 ~ Nj޵!1>leQ* iWOg0fd@]`,ڰ jA  :p_);\cuꆉxy&>ǎԂ{m$Z|} M2X!@}C3-S6Ts)[^X7r9vUK`Ktl|+ϡGKg)tso+;z'c*ylok:/_yTK1ZR56HӕwLؠBK۵׿cf\r?gC@rUvxVD׷d_ > l̅b!$ћ~lm[c cڲL!*._hEžFW)jQb% c7U_";hvzJm%(WAW@6!-mֺMc|V|y *|; W$'asSJ7S7ֽ;b= A/,T` 7zcP<>/Wp#U9C&FEΡPn5XQj'tҔ+L;ճfvfߐ'ۊro"h,V]<_E\Q+֙4="~+u>F-SEax:~)wonQ\GaFp2!EJ/Zܕlb7e))OGI݆)9AlAfڒw?D2MʽdҧIvu&Oq0^H?*r'c=CX7 *Fd¥aƆs91D>X{!.RBMxZ|՞/ӧQS{ůd!dpM15CDqf7̀06BJ)L_;BF%f9'07gWW-?5ݍV6դ OTƖ (*42#G,#uǫRm9GJޭdgփy/$fMg-J*WTjw[Tt~l<>ol+>-KXCp^M3sQrf(B@3zZQ5KȞ(g@ #PhThT^y}^QI52X !?KZrm|C~RyPiaYr>q uO1:-*[6.it O8'l:^l;dr dMUsȓMЍ#^o*Ϻ t"Id<@K۫Zuv<ð XX32 WJalLe&kOHܔl2#xöe =s))yc0F3-#倂#$Km񄁷=Fh{;2+Oâq'.P>J> OSY7a9oH6^쎌%ּXWG<;rOB,SV@{uqh&hl]͟gޏ4&k0AXA4lw$gWR{Զ!yjL\H0M|DmM*oF%?UGe>WNֶ T/ʡ'/=\2Nt]QLac3~n3 G!PvLT*>-_$ j[=[kf$&k/.S\D+:S3 PP7j:%Y0`8y93T.ݎ?L jWQ5^~';=ϏQ 'B 6%}Rۋfl crzN Ԗg1[g_O=dBdxd*FĻ6b)2J]&&V瑹IU]n^gZtc.Zfc̭W#h߿M.,a&ԎbRʼnd Co*V*3ŋqƝ~Vds~d:HU- f[e;."oH3l˫]zw2 }CCX&Xj'\'!(K4$D_#/+.6 J'j(bS5.%}پMr)lËUaٳH+oQć+Tu!L9 f߽قKT2o7sσ 9zus Ckg@ ͡[aU£ik r2ED{:bvF4WeƖeQR ŦXξ0L%N_&v^w1Mqׯ}y-1h𼁻\o9RTw.Nm*i9|Cyvw^㽺i)Uej|̂'pTŜS,&آ7jٯ7m?CuG2NR>,& ^7>‰r7J8&'uQ*D5xԽ7Qu`[H+@0IUҭE2Kgb}=i: Qbgږ !pG}I t% xI|bDG;t8jɯos]E2)MNZD}MX)U7 ѢL[Aq,oMINg*xF [DO=}J?NH8~~2gM*e"Y$4aG1Ya ^'km5:{E5Im[o.]G;c=?~puȺAq(m7t=)9%FmC垛;y 謯YAۆ#$R YCSI#̏I}:f[Q`'/?OX>\O{OڛH&ר{,Kw5I ]#$Tv^ab6LgsD5 % ɻ&8z>?) V{m<6P4.Ss3Z[s;1ymuUgwUvxYC0iK]gIsHH2ƪLkvu!!#2- sCL4U#v-/*zGE}kfCou-)Fb9!!7¾H;&(;oHA?Vm.ca:EkrГ˖(JRϺ_w=0) @ d5~ UrJ (3{W0XOfj_=QȖnL1F,&PS \JNlSV(z5$1Aߌ2y^|ICq֣tsΏ N&m/Ye=vZZ!^W"ݎuJp77IT'LJi@w'L}[aм"vYk[/,7[E Oj܇DSD kٲ :Sދ~NfKv@os) cezqzu714g[@QV{^zJv1B:ko@عٙ-5=d1'8nOj39vD7]s/9(Um -Tv<]GD~%ۇS& -xbq-|^ _CwKtД 7upXA|?eE,ymy~.R%>QkF kаtNں NxUzYKZUd!D*akK4եjŭu4z6L hUkxlyTp?dЀXTIVm6%R}\5Y|{ʐ]QAqC[6cuHaޗ!7@̪+ǷIH3;G6DK:7,QY &H4S(/>x^1 G~q|~ʗ){+-1Oi"lM=uұzD"ǚؤH o78'F\]o3J_Sז)\I!q!ω'L)< +CEgGȫ祸1ak%SN%"TTr3Qw cOd鑼FD:氣Eoc-UX=(S',ꐊ]~j `Ofa \zk0n 7-`|u2!زH=SơP 2R[ z|Ul ?&OcHrE'PM[R=ӭ>tLm&Ьzؑ|F<1lWN[6TAU 2&f%NYj-NkY^>V"ddQlk( }F,ʹ͟"v )ZQ>2igZ%x'KAj# n[6')N_R3-E̡ݹDѹBU9|*HB"0HXՅSIE},\VqNN|&M09T3j%?dބc=㕧3d:Ȧvu>OU! CQB554`|6o!U7 $AI,{=SgvHdS/ 5ukR.дf@[$K>!`3'ba]w7JίB@- ,1]^Wַn~ڐhnؑ}7ڑ8C|aJi*}{ endstream endobj 100 0 obj << /Length1 2710 /Length2 12716 /Length3 0 /Length 14253 /Filter /FlateDecode >> stream xڍT[ ҩl;6itwt -) %tҍ w{yq` k\GI(j6I]YXlv&6dJJ +[ȔZ 'g+=N D&t(*rVv++7 `'> ۃ)NV. 1r3e9Y@Kh PZ\ ')aߌ:7")W[ۿiV9Yz[W(!`UA/"ʺ! joao!<@f*V.b{fkeR;[Y,, \62 _FI{S%c,IbxB .$;_9 wK8̢E#.30?#30KYRψ,2ψ, | § |J§/<#3=#30k<#H®/ ࿈ 2/I9 L@gȨX9<Bty4yF -_)+$H')dKֿ6 +?ڙ8!m!o%vvDD!m2ЀDzv +dM ٛ?F`?]Bxv9T\8H,=,Ah@dV@H`@H[mZ<I=C*g Y! T_%}.hGH4'+V]| p<-?vd*!\ɬ *$pav|v+df\ vde(VHώ9!F ;om8q5G.4tl1Lʶ+եgnof{ҢRd5lցUKM u&T1x<<0!D>UՊNtR8|=zO,up?ƹ9XmNxx u(E o<OtHc<_A`qU,$Щn[fmkJRCwۄ&E1ĪM'&x]FɈ ?1bar]gk֒iI5lN-W9\޷4HTN q nE-@@{@ udcZ%o RLr =R _*!m~'3Jo5 TE(2 8o '$tfS)p}Kj;ͧLUs~ n5a^a^ (;=.t$I2zsX;Iyz؄+ջB,'=\O9LT܃}'8 (\薹w9fw~tuY{aNzԍWXڿ ocI5u% ;8Y!cׄ SgC]!A9;o0~\e9ڎBlu|ʺ~[I[qZJ{u͸`nbp5oCۻ5rl)$J 3XJC~sWhʽy 2A-X|# YǒzRzgU~lרm˜p`p>G11%(a x2&)s~$ZԹr {e!:@U|*gWaE{404U2OX ڗY$R wś|zs /K[ʮf"1 r]5ZW |8t"7%CH5gp{"!hf+ң^)l_c$ j4](Yk0ʠCN"f542$3*t"䌎B43ͨFY21(8k=1rjlD.E a8zʢQY;㍳_Ƶ溔fm&x;`X4>ra^/W(Ɇ- mHyXGEOg|::WkVTQlzMM[`^ CC/iOWAÿl(s%7DE8#Qk|7wB%l`P `'(V4GD&++CD֦(Fcdd;!.'6 Mڤ*D襢}5"p,Rr;4,z 9yܺs}`<뺸ѻ?Z;3,|T/Q<Ǣ72 B39a0AȘĥ ar4jP7y6W̯}=+\hƙ\m7svUpDG)U.ug4zg1tV%\AI~ge¯)W>_X>Y{EK^LPrNr+UsEJ <)R8kT\eEkBTZOoFޠ|{Za'x!ԈX=eX:Wey 0չ5og u[|` q*qrxz&B6G`.ט"y"8jCTr_Кh6f ķi gO ѱ 7E0r3j6\Qʱ@x{LlR7d 5;ػN:e^`u2q= be WE+1nJ'V/J2'όuȎMp$K(o׶In2W0=XF]tE~Z0O:i=sGsѢ$iVCa}yΘjfpmu/oa,-?;/_t?|ȇCA9*X1frl.ÏMV ^-{7(/];!yg|AzE4mdq2O>ǯiThǜ1j!%1Oh e`>)E :.o1Xqpۭ`i|c }<^-lvU]4}Fr!/Dc3Qs?Zo޺}ƭ퐊RBMY+uwVi.I9)erfqaCjll柈 ~ VN>JOAE2_POa]LI 3女t2%36$ebE\?kJG4ܵ0<8 'y)b*iIdڹZÃa-84s;fzœgITࢴ'A}2j";\pdòn VLQX u0 TDKBk!Py3|dL?# ɖh8?8o r\r{=.Y *ўRaMT /Pc4.|܍ԸR_^Zi/^|bd898)WwYhgo<_E,Vr9Wqۮ]cB4<. bHVeFG~[n-YnG|Z?9 Z!,7{ކ`R/vߠÎhs<->wIIWi]O~WMRB֜%WK9.G͜y.=&b덟ׂ,F5;͎bii*}D LrN ohJ>sEXzu5 HEܞMI=I-w?x=iOp*(j!cB |BE][KMґXZA W5G7%ur[~0%_Rmi[`+CY왩*}LQQ .C J9)H6<j?_JyەD,2$Sz^`0tOZթ 1Teδ|' Ȗgp92~,-$Mu!Eؚ߫tm ]yWRJypAƵ ݼ5";c1.S!"~\hJ..Ty4l?9Q|4vglwn|+JY9˼ݟA+N0jq9a`GvPgT xq챛EJ2WhcOmW75gHb*wBȑߛ7ꜹFYM; {&ky/)5QVa29 sNiΏAgUX6$kp-*ڞ@#Z|# ç!hfB:6h*3솓Jv"  ~Uro^! )$:,) P\+^ir.oQLAOSe x@*UrSBH$-<:ȷ{@m^n8*Ru -|4;e Ajs+"(Ua赽Sbx 9|OHOh7{Q5+IV9` &N58YRM21Gi-'i-}mcc VnC;Iށ!xpi{nʇ/H<{Ksp_ω?XhOK[L-am$age{+/M/޿m&*+W&=&*EY4-;%(F+p}L'OUzӨa +.].vHhN.Dþ=L-bwޯiVٞ>Odtitl𰅂k::~Rb@Kdʴ[)HN)_>-ã}*Nzz>L=r5)$>{#\zv٬f 61^%3}eQW.6쑔3!$a~*{&xI++7#} )jwNvq2XRshL'Ph~M*^N6G}c>{6kaGC1~ן⣊ iCSgE (L։_Ƽ<+ƀZ[^I6mdqqg'M>yvRHIڄ秓u|ƨ_I]>񡣀q76 $]:1@uK%^q*U\b43?StblH'VuFxi5'l|:Gu82OUۑ4kQk.ZHF`7yשX'S?+aC+3CZT|7 Waie3jީe% DR}Jp{Uas{B覆Qp* |\E qt8'6R:~D<%e)2Rư}T%[cKovC(Ry'bOL&|Ip|HƉ<7;i$=b>eZTnsPOGlKyՌ$*~ve 3Y>F2b*3ՈP|m"ђz3Ō=$䰊"ak^ɶ.`ƈ*CfƸaf,wgJ[z#D֢&M~&qſ\94ktE.-AaNݯg(*+! N4ԁє~w%DVhPzgC?\5r&CTl^ZS!z8W\`s!4Ktf ꣵ3:·Y!a-C(EGA[u}X9/^ 4R[,`Ǧbk:y( ,jSmuW4&}{f\Қ Vw̴yȚhFx-^ضw̜q`6ԪNG'. ĉKqזz$㝞*͟)OWދ=x+,e{h5 .,mk'Swɓ"o= r!}q2u(g/i^oC m߿}msws0"Z p0~>rEԇ#-r֡^77UWhs~ teKW=項ew^i,b]/]+TnCGL }t59YFQ%ϛ&x$oaТ8,nDX6\]6?>?,Š(R÷7t~*(0`U᠍RŰ&Npw ~ G",z*3SkAp Wquqqh=u4ʪWcΘܾ4_ M> "i `֩Ru(!c{0_OڸvuA.$͕z/&}hoS%޼Ԓ5]ФFJvU)S9"ĆpK|]WCD݆r[@S TܯX|޵,8U/>'}ቆfCm,%Bo?W@R51je؋!3m&Snz[0Yujb!/zcnmks S?uM] l6;ڞr;~z_Jt^HkhpڰZ7@+Sq<֦oYJGx2Cqrpvx{yh^wѕnՁ-|x y+6[y DWM ˵Gl&d ~-F]!9@N+}qr)ڂx?[s`ac"CoT0GmFaNZzW6<\F;=jl @5# tHqA_늩WV NޔحMwjk@aSKm0JeG >+r;[Z(H.XUO, F3H| =}n.Y=}OD7s>;ZJx-҉[oWjD 6:ݼ %K,^+@;Nil(53TD!3v.p`ϭ%wЮ6Eu|+2(. kmYk6l|z؊ZjDC9}DC'*b/駁:z?02<OE2Mrm g!1}}U?CΛiƶHQRػ3&am#_"y7~Qİ jGR& <Ä]f_(kO0;ocQ$<̰|\f9 y9j?t 5#,<+(|m/S4V%AbGo5 8S-ŽhBet圓IZ؁R{{8q4Ίr,f~+m+}QBpwq(bR>ǭ :* .1ܻG(FQkQYo$ҍ_mʘt[(N5:~kIgwf-{(@'O/)jlPG$o^[RCfTqG-ߝThI% ڰဏD4@Z&|µČS9ҏP?W64t~.տc3d$_q/ pStoYlDžTS)r=c26@a;S/sxвjw:+F!ݚ^5hrY7# oMxc }}Z ]#Mƒb & DwWe%S&A gjc"`"'_Qp ax!j4΃_Ogfl]Yj1[5As;?̼nE^N ak)磑cF|%ݤSFl )to o\k½5ތ| zVY9IS%C=V]īb!` IE#ƭBwU]ENCaI-v[ngVw&Kq ([qcG{NSDrB-y9cz_d(=uSzP^\"BԈU~ՍIs6y1Q`=L8*rw*VtA*n){+NrirаV+퍫/"âJ?D0%ƶq)@n""wnW+âN2{`jse_V8a)Y[Eu.Pg{bcݝ,|1rHNMX%yEIwX&ytIö ,x4q@䷎)`hgܒ q: ڦG 1&*Oޫs=_] bE5dJ5GTo%.T^ỂX)Mq@[0b< F'a:kOc><ݔrlo~hWvpʲAeewl3B"H0Dk 5%*d(JXKą)1ա5+~&W%pW0]foJ.*[մ$$p1kU>88hR%ÏD SNt MǯvD 9'&̄ݮ}9 =BFh$^mS}72snF%o8Q+4'y]B7t5(OD&=8||:SVdk;?2\y|&vIODK[ Vg3uS^h;c7ۖRvcb_"KMۉ? HcĄ z59X{ 1;0!ZG6'ޅʗt۷ !(>ǴgaeXfo  x躵-$ƛtʜqweyEXctm),D&Zf;[Zp0_}Tcrzg{'&Ajϧ^f$i٘D: A! +!CUR)9pG@E~6F<fvyRNuoLG'(X7nDr߱s&pL`r V C g/ "r_˳`P,o{ov!D׎-k.BH}{`PtIͧe_Ɲ7l m\ΨeRw⣗Ud4"NO(Dr?̤)\à3|tXdTZ?o\wLY-] zlOe`V(dڷ5=tYwPY]<&I&_}FKf۔`TldR0UuugyA6b/dMCeTa !N[{ |ެP4hh[1/,E<Ծ[3 lcw čp'xM3#@){~i|4O)TC9QWs͏ _dy؁OR&`Ѷ]$9̍mc|j{7\| >jJS!kw(_]eoҥqvKjzblb듊x@#zJY[\cX'Zwq?qŹ$J# #?] =?IJtP¯4HWG8 &uRa3\-h${ @it"v9N#]cUT\LK&Q݋v}<-mgErrr<~nF[u(S~|C4 쫿${Ք»=,(M #~pH ;)o!W಑^P{6URZik=n_3gÌ-iën d?.C haoКR kW:>= ߿'!A5]\4{d:|86BcTt獒N2#Los4ӠEAmh83+Oˆ!jr6QO9[vmIJj͆6xKB = b?e$ I>b-rtQ&s; -Q'&j"rQDcQˬV;%NU\,E&$4OzdK@km,,Hk/;#/BʿH^40TZҾb2:sNRA S<+wlw0zBmX @k1=cf@:5BUR+HL۬M_BV0̦U(+-kf;rPTmIVK:dLp%g:E$Y՜ЗG~{oڊv>Z\\.?ٯj{H҈/%]ZXlfWAc*zxק4+ϬY4oQّNgfsm`n+m7TgǘN )оу%Gk*Ll uZ\7ZRB#og-ai_4񧲙@2R}_&4}rkPZ_e.Xjz8ڞbYblm<:Q5j'6;r)ƫ)]rg1 @KGHfwED=oOЉYȀŏSO%bM~xt'$+ `jJ7ckɜfhgwqyޯ ;bZH瓀syd0/Lr\Q햟&f` H/xW#LZ__a7>gZ{ O?Q!w endstream endobj 102 0 obj << /Length1 721 /Length2 5243 /Length3 0 /Length 5832 /Filter /FlateDecode >> stream xmrPZ5P*P+ł-RBBHRJqw(NZ] ]9{vs^Da\<ym5^>?3# G!e0Q> Ёx^f neZNPB\ր...R.N\\M:0m X0UA]CMgnP[N0 @,PH(/MN\ @,Qv59: ' #Uv$I pk:?0ACUA\( H|SBZ9{@algB`A;a5h̑/Jh{V;`PM8jKsĽEa wvBQH>y?r@-e9 GZt^;BK]4=wn7<\__d,+ D?r-aH߆o"`n0 )XM|͇BoѢou٧x1 5!wZCTD#~Ej(`8}E.cwVeEo=J; uCt{MA䴝Re|q2m-D?A*MX'VKZfH^,Oce/3SYgٯbYytdWzg}.VbC}DJ/aUֲR+}ծP[yW6JQ G8[am0 ]VoIYk 6}|nz#M H} j]AtLKHd_,~Rɍ?&gf N,1 Gbtwk#kuCWKU*1Ohh.;/v~Fpl`JԤ0 rĀ8;.,W4 \vuŽ8}l0r|5U͗|>yΓ䫗Sj\]W(/hǁ@x* (bA,n ңJcoN:VWRش@$tl=m0e]󌭉Nt) o&ƴ|n!3i4m] |)xZBfF?aov1 PH?/B3<S^56яt.;.zl1 Y?6?NGYB4d&d"kXkH ]SFh6co)糈j&%[SPn2^h9>g&<

=7op܉(ʎT/( Qv l(y .Ewr9_gR.U2.#Ot7)sZC˩/A3S¯PԶL/!ư T/TƐ-r[!+ԇe6>o;VBM)ii]f2t;}"4ږզey)XΒ`l+ɷwNyů\6+7 q 5m}:鍷w\x(g͔u20F+ o} I3(XmDYbD&#CL=61`uN6?NA[mԺإD-W.]%#,n{@vݣfh:ҍI^ŝZ\E.Y;qAzCfDlK*_.T_|'YiW0<cdlTr- ;qP%Ó =dpfeK;J̩<|p7':BjUݶ^/I$G6L֤?XaH?O__rH1*1) Ny~Ў9Gr`nYʨ(~7og/q.0C1wIqW-:|cgdy X`}JE wfp``egtB@Jg'*DCԭTNGni7>y=}0tx3sL|tڝ7#ke0˽` .1`A8hAR$1o'=Z3dT)?0}rUc %sjõ>wV"-8<>jPY,V!}zj.>CbN /\c, I2&iO\-b @U:u+?>8W61YNECHC'VPrȖBgq_$cFeO14Q@TU0}0վO}~/sN5)zJ c-I6cx?ek+\Xy`<Ϥ>'q$Մ͙H֖ܡii(3?tOP!cƮBGUQ~?_DOAfkK+$YW7D6uwNr4qͽD4u2A]ijOvWm#@@{P/^ Ips74`!mdX0lөiqX[9m鋋=jVzC8:e(DO1k|1k sRh<4`4)7LhHdR/wR+uK6 Cc fk4äe֨$)R0arإs{m+A=Qrb{+V?%i5+p}ߟbF(2I /_kL,ڻ^L$L/'G`2&|ln=B=" 2?,M^qS4}߼gW8D Ty>t T{0J(@Sd̒4k}`d-?̉O3XPg{eh/ Sy-ǔT2!@SJ=1 WŬIx.ɾܩ'%77\Ej2i\->] o>haHL586uϋbo>4 SDH>%j-Y |uQ8'-HmwjlJIա\xpO:yұϓN|I/|yI>O:yҹϓ.|R T<띹_mKz}K=W7"V{/znb endstream endobj 105 0 obj << /Length 711 /Filter /FlateDecode >> stream xmTMo0WxvHUdCmU^!1H$3C z̼ïg)>]Ͳ߻swչ"ںQ;4m_%= uS'N%Fwڴ.HS1ʧ`׮o/x͟m/с!ZKNN~gEʪvyW~~ r-Ҳ\b&jr ACu ad)Xy x &I)RV˘.LI@o_pi k-cp [ ncXz3N4ִWYf% 9%b*ԩB+|A_F:C9QY5KE9rW8faD>ϐSG&xV_kU8!80L5#D ԏJ X'9Ł?: iYIWF܍湛n endstream endobj 106 0 obj << /Length 696 /Filter /FlateDecode >> stream xmTMo0Wx$ ! 8l[jWHL7IPV=M̼ su;Uٛ=w]yil;<[[j<=?׾+v`&ߴț<^*;~&Q>MS 9_P{=s@dkx;`VY`s4JaQܡn.Uu9\Y6><ٴ.Z.4>Dӗ}~r:-d0VWk,8yLһʮӮђ[*mLr?q 5F8@=@)& 8Rx uD\j2HV0CzL] bctI g$`htы0\F0s jd< I6zg W qȐ+#k .bsrbmXK7ǵH7Gnb>&jؐu1VljOu$՟qWS/%1{\xB!K(hHTЖ枃Jρϯv=k2UKς_:~$/ ~E+7ˢ/ l(/} -+ZXukoԝE?ZK endstream endobj 107 0 obj << /Length 739 /Filter /FlateDecode >> stream xmUMo0WxvHUdCmU^!1H#x?gx]OTm$|͜s_Iss :L;<Sz==׾f`*_`ɫڟk3'iѴ}=M;7rfnj-eSӵOLg~8 )ok A8 $`I\3`Af<Z]! xNky"7 _㓧q H`nḱRONH=CpB:# =%888QA~!*zƜАT?!~> tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo0WxvHUdCmU^!1H#x?gx]OTm$|͜s_Iss :L;<Sz==׾f`*_`ɫڟk3'iѴ}=M;7rfnj-eSӵOLg~8 )ok A8 $`I\3`Af<Z]! xNky"7 _㓧q H`nḱRONH=CpB:# =%888QA~!*zƜАT?!~> tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo0WxvH UdCmU^!1HDI8߯-@=ۙڽ١=?w]pwdV^ڑݧl#oxdGa0NiqF?Sր'YNR}{f{x2A! u xk={Exo"}Rɑ#x۠_J B C쩁b8!=%p&r"D9 Qg̑Tu+gGNN8O-(7ZRntH ʍ(7:hEњr1+w(O:͓.ndm'#Ʉ'> stream xmUMo0WxvH UdC۪TBb B8߯{ .@=/ۙڽs{K;K.k6/k+[M'ҷ>dyӔKe'$cS`vfSfK}fƁVGGf\bu<19w|擬CTAW $rG]IyMsh$aW7y̟u? sK-`θtJ!'c83?NaO<Dg!;IX 0z)rЃ@kpBQ]^Z7! / U <ɉ#W m/%]cX! gȀhID8QN~ACT/sQQRs 穅ύ>7: F+}n4eE=zG~<6OɈy2kLd>O&y2ϓQ>OfdV>OF<dR'<>O)yJS*}𗏿tx>z{O->tՍ]*3>cC~ endstream endobj 111 0 obj << /Length 739 /Filter /FlateDecode >> stream xmUMo0WxvHUdC۪TBb A!Gp?gxYOTm$|՜s_Iss :L;268{zb/}WUjWm?fd}Oi=7gRd{nCN8oͰof-%6'&9Pu`L/"tkں(a[ duS $xqa MN{}m}gىx` tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo:W5?$R. d9M eCkmCp;;w~>|3E_?O]5߶w]Occ]=~?}Oyh9%?۹׬B|Ɯ>);vw%g43>\ 6 EJ78 1{~`W(-;]%=xe_,b+-O;q\L}UI--=BKE1p[! Mߊyu>.N5K)Wb٬8i[_uʕMzQ)V(Txޢjy!Z2P="Zd0\ÃGR\).2*Шa!U,H`+j.5Nα@VK-x%3%AYӀzΚ>kP#5m0Woþj.ZT$X/)n)#Wo(oRZ $Kp4Z-b\1ܰJ P"GXQi/8k^Zq:Zs9dB )sL-7xJ`aɽ)f$1 dъcCZC<73JgznHȰYɚTa,_-O87}KԴܗLloK+gJ.GZyVc48Wt]:P~`rZq.n1] S/Pu7Ue:?&?!d&1yHn5)yғBx#1ޞ]Go׏M?X endstream endobj 113 0 obj << /Length 750 /Filter /FlateDecode >> stream xmUMo0Wx$*B!qض*jn$H$3Ch<~3~~~ngjv9{C{K;K.k6㳵ችm#O7٦4\ =؏8ݿ߳4ւ8͌>sIvdXC6OLx9im$l6Dl_7ڞhz*{pɲ2kAʶC+mk>lpfIQTT?LA>J e .1PbpqH I$\kL8Hb،Shąr =z51XQg_s2Ē+ sC:CQ}.'c-BbOEu+Xg~:?aj B.U $,ĨAA 2A%%" 19hM_)ELN 1sR3fg =傸aCYjV^w&L= 3nqFyDŽϠOL5'pZx?i^x?IGO:~I4ϼt~3][gF~Qgf}fB3y,h3cL}f23{,g>KYN0`^ay{7)q W7:*ሟS`R̯ endstream endobj 114 0 obj << /Length 750 /Filter /FlateDecode >> stream xmUMo0Wx$*B!qض*jn$H$3Ch<~3~~~ngjv9{C{K;K.k6㳵ችm#O7٦4\ =؏8ݿ߳4B8͌>sIvdXC6OLx9im$l6Dl_7ڞhz*{pɲ2kAʶC+mk>lpfIQTT?LA>J e .1PbpqH I$\kL8Hb،Shąr =z51XQg_s2Ē+ sC:CQ}.'c-BbOEu+Xg~:?aj B.U $,ĨAA 2A%%" 19hM_)ELN 1sR3fg =傸aCYjV^w&L= 3nqFyDŽϠOL5'pZx?i^x?IGO:~I4ϼt~3][gF~Qgf}fB3y,h3cL}f23{,g>KYN0`^ay{7)q W7:*ሟS`R$m endstream endobj 115 0 obj << /Length 672 /Filter /FlateDecode >> stream xmTn0C6*drضj^pHA@Cfy'n`g#govh/}eg羋򶺜m=Ooٽ[׌uRۉ=Iۏw{VQҜ8ߛIߞ3d_ ~~hZ# W c *'qU;HHV7xwuɻa;zopO_`_ݥNd0m6G_?[6vLClw6ZsaD%!p%blcä  PP[ u_g_x4$O<X^\NB8 \;cBbMx y%P 3jok:E q:/d48Q4A2="\šY+ːs(5$Y r~+A\HȕWr{Nxo $TL~K//p1sQ*GG-G-GzA>|)3Q/G""&!uN>|%h8hh$hb,n~ᰏnˣ+p]h \2 M endstream endobj 116 0 obj << /Length 672 /Filter /FlateDecode >> stream xmTn0C6*drضj^pHA@Cfy'n`g#govh/}eg羋򶺜m=Ooٽ[׌uRۉ=Iۏw{VQҜ8ߛIߞ3d_ ~~hZ# W c *'qU;HHV7xwuɻa;zopO_`_ݥNd0m6G_?[6vLClw6ZsaD%!p%blcä  PP[ u_g_x4$O<X^\NB8 \;cBbMx y%P 3jok:E q:/d48Q4A2="\šY+ːs(5$Y r~+A\HȕWr{Nxo $TL~K//p1sQ*GG-G-GzA>|)3Q/G""&!uN>|%h8hh$hb,n~ᰏnˣ+p]h \2 ᫄ endstream endobj 117 0 obj << /Length 720 /Filter /FlateDecode >> stream x}TMo0+J6*ħöUSEj9߯ IVcf͏睟ݛ{)^؝}]u:vzyu|CW$nmmΑmq5)M{`qjS5үxO%r^q &\TƦkR@YwDoYia) SZM5_$$>kxq4|;o4vhwqB؝Bf#j{p7P_?{+4}+VYu}e}n.ˍggfjj{k:lF #QhJq  HQ/e.!Pp #]gQtVTv)#l-g!7'uӾ:[sI r.39uf *gQNxEqV11V啣Yq:54kDCZ+)]Ws8:а/9R\Qrz\8Ç]按Sp/ d8D(B!4׳030 =;fzÞJmw&^0C~/nS0GKW皠NdzG5cC)!=E^K<3Iò8ȿ q3NOg{ACt~Qn~ɸ\ %1.: *4hH`<4̶E hS!| endstream endobj 120 0 obj << /Producer (pdfTeX-1.40.22) /Creator (TeX) /CreationDate (D:20250505110547-06'00') /ModDate (D:20250505110547-06'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) kpathsea version 6.3.4/dev) >> endobj 9 0 obj << /Type /ObjStm /N 76 /First 615 /Length 3494 /Filter /FlateDecode >> stream x[[S~sj+htR[ $a. dC6Ń جms!ydYjnɃLfgRe2LLEI(3i3A3B&Cϔ|RYB#Cd,LLj2 L6Ӡwd>!]t13:S Ad>} t#}zPT0 QCT(-5Yh*yQGb02 )0ZXiHGP7۞1sdGdžL- .9lcɘXQdɊV̕RKO52i!`"{'N-2qؿ*zbg<4𿣞8*E13Aq9ofs @-U#B<I G^`L3/4􄺘=..uy"'b}{8)βK.McX bw@MX GPyDش7U0Pa 755#S"]6H葒kՖ\Höm-1cAiKj)ǂҭ] d:eAw2#ްz2Tɤ> 3A20s.(ML| h۱ͿJ1]DoD`%N·a@7U|tRoOrf ܡ5?rKlcHyxi\ٖA{;PՂD]y}1y؊e8֟jKsnI-mɳ֦4iҟ5*1ѝu{it`}(pC!814\AE&Y\!Qr#$ZJS!J}/sh4Y.Y^ ޣg$cd ZwS5Sl+CcN~3; ZK$iWҁ-?LU#Ej<4B|`9BBU )KnPtPm|^ܒ;B /ۤNf/oXW@۽ YiZf=mTUQf.jGInçF"@b 쐠iYG{ذrpU/9BSl$PuXsV2xZ ְh:lC22YLU>6y8|޷ptv<  캊g x i b(7m86kނ7FrgvЧ)_I-s֬ں9KY!Y),[/GY YXkodqX }-U-$KфŘu _GDqP@G׼rV4\*)Ok'[$ FgL0[{\u"5Gc-jtdQ'&HV[TD0-,Se7'w5iѯ#Y9itG#4/)*XWT,i(ݝ-?W#y2vA4^lsxj񚹕VzE=%?C4sijّSFzQpy.(7A晊9=,=f7eg(bchno&HiXB#ɽ}@44R7^Ʉy2 J>?"O4pY}*!ǗzAM?Zu1/TGn?LK"a6ft:].|hcӍK!=]cMn1 ngIy';?Tx0_M3(>3egvYO^u߾.W\O2왤ofjXdyOϊw8-' bKbG{x+q$ʼnM}q..ĥ(OħB|Mĕ1c1VR!&b*M_L̮'E!fŝ"__NػuC9:{U|.qjx}S((ԕ*zF-j<`e݊INX6YZ(g<O]b|st0$\uVu0>V  ~ן>ϓCs/_x8'Z~{eM ъW+;b5JŅ22p*~_ RX?w!)pA&}|GϥGE@aiz^nPxV) \UIQH-.& :&MWP'ώI7Ю0uub)UTy,!o@]+}x ʗ{X*o0A/_nnC͠q렉k{;@saUU1{YNCt3pK"&_HqxhNެB̦RbX*Dـ2 bʗʵBǛ㓷JuRIH͠dΆUz﶑[\\+ݫT,M3(ţ)AʆB JK].S"+aSl爸VIBTi\u}:ǥ_ rǴ?)gџ5SJ=/ NKYb]0\üD(X[$ 鴪Fw7rp5zh -+.χ"FXb<))(PҮ޿u{㽣;B?BU.ӈç'bIJ$]/+5鍍RY;V;Wn9+7TE/f {tFf_~ .gSzǛIOF%ɿ"D'O(.޹@+]_)OmydCA-q+[_%V8~}qZǪ8#l+k-'/Nͮa,_ci-i:lbM sSu40Q~0mU0"%N׏ھr]#yn 'P#iW-y=0Е]:T%N7OGvݧ%6 j/#rhii]/ɜg{_K59w;羽^ OK lLL^U:}0cNN/vI&jC*@@BepjkNX,(} endstream endobj 121 0 obj << /Type /XRef /Index [0 122] /Size 122 /W [1 3 1] /Root 119 0 R /Info 120 0 R /ID [ ] /Length 352 /Filter /FlateDecode >> stream x%+a0}_c>u ))'rqOPN"'HRA(N|>sxDDD ".' ("C!in$QA$q%uI OXH"] I!ɐH*$tcILE2!3~I!C$dC3< Y1@Hɇ,A!)"ŤH))#夃I$Ux z_ h]rUѪ|Z @Y[sZMV3㎼_+ԮV̚Ws0s-0'eoXN,3˫>lY b@?= endstream endobj startxref 198001 %%EOF zgimbutas-mwrap-04cdb66/python/000077500000000000000000000000001522673526200166105ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/python/COPYING000066400000000000000000000025271522673526200176510ustar00rootroot00000000000000mwrap -- MEX file generation for MATLAB and Octave Copyright (c) 2007-2008 David Bindel MIT License, with an additional clause permitting redistribution of mwrap-generated source code under any license of your choice. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. You may distribute a work that contains part or all of the source code generated by mwrap under the terms of your choice. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. zgimbutas-mwrap-04cdb66/python/README.md000066400000000000000000000032361522673526200200730ustar00rootroot00000000000000# MWrap — Python version A pure Python drop-in replacement for the C++ [mwrap](https://github.com/zgimbutas/mwrap) MEX interface generator. No C/C++ compilation is required — only a Python interpreter. The runtime support library (`mwrap_support.c`) is also bundled and can be easily adjusted. This port is **experimental** and tracks the C++ version's functionality. ## Requirements - Python 3.7+ - No external dependencies (uses only the standard library) ## Usage ```bash python/mwrap -mex outputmex -c outputmex.c -m output.m input.mw python3 python/mwrap -mex outputmex -c outputmex.c -m output.m input.mw ``` Key flags: | Flag | Description | |------|-------------| | `-mex name` | MATLAB MEX function name | | `-c file.c` | Generate C/C++ MEX gateway file | | `-m file.m` | Generate MATLAB stub file | | `-mb` | Generate `.m` files from `@` redirections | | `-list` | List files from `@` redirections | | `-catch` | Enable C++ exception handling | | `-c99complex` | Support C99 complex types | | `-cppcomplex` | Support C++ complex types | | `-i8` | Promote `int`/`long` to 64-bit | | `-gpu` | Support MATLAB `gpuArray` | ## Module overview | File | Role | |------|------| | `mwrap` | Entry point and argument parsing | | `mwrap_lexer.py` | Tokenizer for `.mw` files | | `mwrap_parser.py` | Recursive-descent parser producing an AST | | `mwrap_ast.py` | AST node types and `MwrapContext` | | `mwrap_typecheck.py` | Type validation | | `mwrap_cgen.py` | MEX C/C++ code generator | | `mwrap_mgen.py` | MATLAB `.m` stub generator | | `mwrap_support.c` | Runtime support library embedded in generated MEX files | ## License MIT License — see [COPYING](COPYING) for details. zgimbutas-mwrap-04cdb66/python/mwrap000077500000000000000000000122721522673526200176700ustar00rootroot00000000000000#!/usr/bin/env python3 """ mwrap — MEX file generator for MATLAB and Octave. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ import sys import os import argparse # Ensure the directory containing this script is on the path _script_dir = os.path.dirname(os.path.abspath(__file__)) if _script_dir not in sys.path: sys.path.insert(0, _script_dir) from mwrap_ast import MwrapContext from mwrap_lexer import Lexer from mwrap_parser import Parser from mwrap_cgen import print_mex_init, print_mex_file from mwrap_version import __version__ HELP_STRING = f"""\ mwrap {__version__} - MEX file generator for MATLAB and Octave Syntax: mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] [-mb] [-list] [-catch] [-i8] [-c99complex] [-cppcomplex] [-gpu] infile1 infile2 ... -mex outputmex -- specify the MATLAB mex function name -m output.m -- generate the MATLAB stub called output.m -c outputmex.c -- generate the C file outputmex.c -mb -- generate .m files specified with @ redirections -list -- list files specified with @ redirections -catch -- generate C++ exception handling code -i8 -- convert int, long, uint, ulong to int64_t, uint64_t -c99complex -- add support code for C99 complex types -cppcomplex -- add support code for C++ complex types -gpu -- add support code for MATLAB gpuArray """ USAGE_STRING = """\ Usage: mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] infile1 ... Try 'mwrap --help' for more information. """ def _load_support(): """Load the runtime support C file content.""" support_path = os.path.join(_script_dir, "mwrap_support.c") with open(support_path, "r") as f: return f.read() def _build_parser(): """Build the argparse argument parser.""" p = argparse.ArgumentParser(add_help=False) p.add_argument('--help', action='store_true', dest='help') p.add_argument('-m', dest='mfile') p.add_argument('-c', dest='cfile') p.add_argument('-mex', dest='mexfunc', default='mexfunction') p.add_argument('-mb', action='store_true', dest='mbatching') p.add_argument('-list', action='store_true', dest='listing') p.add_argument('-catch', action='store_true', dest='catch_') p.add_argument('-i8', action='store_true') p.add_argument('-c99complex', action='store_true') p.add_argument('-cppcomplex', action='store_true') p.add_argument('-gpu', action='store_true') p.add_argument('input_files', nargs='*') return p def main(): ctx = MwrapContext() ctx.init_scalar_types() if len(sys.argv) < 2: sys.stderr.write(HELP_STRING) return 0 p = _build_parser() args = p.parse_args() if args.help: sys.stderr.write(HELP_STRING) return 0 if args.catch_: ctx.mw_generate_catch = True if args.i8: ctx.mw_promote_int = 4 if args.c99complex: ctx.mw_use_c99_complex = True if args.cppcomplex: ctx.mw_use_cpp_complex = True if args.gpu: ctx.mw_use_gpu = True if ctx.mw_use_c99_complex or ctx.mw_use_cpp_complex: ctx.add_zscalar_type("dcomplex") ctx.add_cscalar_type("fcomplex") if not args.input_files: sys.stderr.write(USAGE_STRING) return 1 # --- Open output files --- outfp = None outcfp = None if args.mfile: try: outfp = open(args.mfile, "w") except OSError: sys.stderr.write(f"Could not write {args.mfile}\n") return 1 if args.cfile: try: outcfp = open(args.cfile, "w") except OSError: sys.stderr.write(f"Could not write {args.cfile}\n") if outfp: outfp.close() return 1 # --- Create lexer and parser --- lexer = Lexer(outfp=outfp, outcfp=outcfp, mbatching_flag=args.mbatching, listing_flag=args.listing) parser = Parser(lexer, ctx, mexfunc=args.mexfunc) err_flag = 0 emitted_mex_init = False for infile in args.input_files: lexer.linenum = 1 parser.type_errs = 0 try: fp_test = open(infile, "r") fp_test.close() except OSError: sys.stderr.write(f"Could not read {infile}\n") err_flag += 1 continue lexer.current_ifname = infile if outcfp and not emitted_mex_init: support_text = _load_support() print_mex_init(outcfp, ctx, support_text) emitted_mex_init = True for tok in lexer.lex_file(infile): parser.feed(tok) parser.finish_file() err_flag += parser.err_flag parser.err_flag = 0 # --- Generate C output --- if not err_flag and outcfp: print_mex_file(outcfp, ctx, parser.funcs) if outfp: outfp.close() if outcfp: outcfp.close() # POSIX keeps only 8 bits of the exit status; returning the raw # error count would report success at exact multiples of 256. return 1 if err_flag else 0 if __name__ == "__main__": sys.exit(main()) zgimbutas-mwrap-04cdb66/python/mwrap_ast.py000066400000000000000000000224571522673526200211710ustar00rootroot00000000000000""" mwrap_ast.py — AST nodes, type registries, and helpers for mwrap. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ from enum import IntEnum from dataclasses import dataclass, field from typing import Optional # --------------------------------------------------------------------------- # VT_* classification codes # --------------------------------------------------------------------------- class VT(IntEnum): unk = 0 obj = 1 array = 2 carray = 3 zarray = 4 rarray = 5 scalar = 6 cscalar = 7 zscalar = 8 string = 9 mx = 10 p_obj = 11 p_scalar = 12 p_cscalar = 13 p_zscalar = 14 r_obj = 15 r_scalar = 16 r_cscalar = 17 r_zscalar = 18 const = 19 # --------------------------------------------------------------------------- # iospec / VT classification helpers (single source of truth) # --------------------------------------------------------------------------- def iospec_is_input(c): return c in ('i', 'b') def iospec_is_output(c): return c in ('o', 'b') def iospec_is_inonly(c): return c == 'i' def is_array(tinfo): return tinfo in (VT.array, VT.carray, VT.zarray) def is_obj(tinfo): return tinfo in (VT.obj, VT.p_obj, VT.r_obj) def complex_tinfo(v): return v.tinfo in (VT.carray, VT.zarray, VT.cscalar, VT.zscalar, VT.r_cscalar, VT.r_zscalar, VT.p_cscalar, VT.p_zscalar) def nullable_return(f): return (f.ret and f.ret[0].tinfo in ( VT.string, VT.array, VT.carray, VT.zarray, VT.p_scalar, VT.p_cscalar, VT.p_zscalar)) # --------------------------------------------------------------------------- # AST node dataclasses # --------------------------------------------------------------------------- @dataclass class Expr: value: str input_label: int = -1 @dataclass class TypeQual: qual: str # '*', '&', 'a' (array), 'r' (array ref) args: list = field(default_factory=list) # list[Expr] @dataclass class Var: devicespec: str # 'c' (cpu) or 'g' (gpu) iospec: str # 'i','o','b' basetype: str qual: Optional[TypeQual] name: str tinfo: int = VT.unk input_label: int = -1 output_label: int = -1 @dataclass class Func: thisv: Optional[str] classv: Optional[str] funcv: str fname: str line: int fort: bool = False id: int = -1 args: list = field(default_factory=list) # list[Var] ret: list = field(default_factory=list) # list[Var] (0 or 1 elements) same: list = field(default_factory=list) # list[Func] — duplicate signatures # --------------------------------------------------------------------------- # MwrapContext — all mutable state in one object # --------------------------------------------------------------------------- class MwrapContext: def __init__(self): # CLI flags self.mw_use_gpu = False self.mw_generate_catch = False self.mw_use_c99_complex = False self.mw_use_cpp_complex = False self.mw_promote_int = 0 # Type registries self.scalar_decls = set() self.cscalar_decls = set() self.zscalar_decls = set() self.mxarray_decls = set() self.class_decls = {} # str -> list[str] # Type usage tracking self.mw_use_int32_t = 0 self.mw_use_int64_t = 0 self.mw_use_uint32_t = 0 self.mw_use_uint64_t = 0 self.mw_use_longlong = 0 self.mw_use_ulonglong = 0 self.mw_use_ulong = 0 self.mw_use_uint = 0 self.mw_use_ushort = 0 self.mw_use_uchar = 0 def init_scalar_types(self): self.scalar_decls.clear() for t in ("double", "float", "long", "int", "short", "char", "ulong", "uint", "ushort", "uchar", "int32_t", "int64_t", "uint32_t", "uint64_t", "bool", "size_t", "ptrdiff_t"): self.scalar_decls.add(t) def is_scalar_type(self, name): return name in self.scalar_decls def is_cscalar_type(self, name): return name in self.cscalar_decls def is_zscalar_type(self, name): return name in self.zscalar_decls def is_mxarray_type(self, name): return name in self.mxarray_decls def add_scalar_type(self, name): self.scalar_decls.add(name) def add_cscalar_type(self, name): self.cscalar_decls.add(name) def add_zscalar_type(self, name): self.zscalar_decls.add(name) def add_mxarray_type(self, name): self.mxarray_decls.add(name) # --------------------------------------------------------------------------- # Functions that need context # --------------------------------------------------------------------------- def promote_int(ctx, name: str) -> str: if name == "int32_t": ctx.mw_use_int32_t = 1 if name == "int64_t": ctx.mw_use_int64_t = 1 if name == "uint32_t": ctx.mw_use_uint32_t = 1 if name == "uint64_t": ctx.mw_use_uint64_t = 1 if name == "ulong": ctx.mw_use_ulong = 1 if name == "uint": ctx.mw_use_uint = 1 if name == "ushort": ctx.mw_use_ushort = 1 if name == "uchar": ctx.mw_use_uchar = 1 if ctx.mw_promote_int == 1: if name == "uint": ctx.mw_use_ulong = 1 if name == "int": return "long" if name == "uint": return "ulong" elif ctx.mw_promote_int == 2: if name == "uint": ctx.mw_use_ulong = 1 if name == "ulong": ctx.mw_use_ulong = 1 if name == "int": return "long" if name == "long": return "long" if name == "uint": return "ulong" if name == "ulong": return "ulong" elif ctx.mw_promote_int == 3: if name == "int": ctx.mw_use_int32_t = 1 if name == "long": ctx.mw_use_int64_t = 1 if name == "uint": ctx.mw_use_uint32_t = 1 if name == "ulong": ctx.mw_use_uint64_t = 1 if name == "int": return "int32_t" if name == "long": return "int32_t" if name == "uint": return "uint64_t" if name == "ulong": return "uint64_t" elif ctx.mw_promote_int == 4: if name == "int": ctx.mw_use_int64_t = 1 if name == "long": ctx.mw_use_int64_t = 1 if name == "uint": ctx.mw_use_uint64_t = 1 if name == "ulong": ctx.mw_use_uint64_t = 1 if name == "int": return "int64_t" if name == "long": return "int64_t" if name == "uint": return "uint64_t" if name == "ulong": return "uint64_t" return name def add_inherits(ctx, childname: str, parents: list): """Register childname as child of each parent in parents list.""" for parent in parents: if parent not in ctx.class_decls: ctx.class_decls[parent] = [] ctx.class_decls[parent].insert(0, childname) # --------------------------------------------------------------------------- # ID string (canonical signature for deduplication) # --------------------------------------------------------------------------- def _id_expr(args: list) -> str: return "x" * len(args) def _id_qual(q: Optional[TypeQual]) -> str: if not q: return "" if q.qual == 'a': return "[" + _id_expr(q.args) + "]" return q.qual def _id_var_single(ctx, v: Var) -> str: name = "" if v.devicespec == 'c': name += "c " elif v.devicespec == 'g': name += "g " io = v.iospec if io == 'i': name += "i " elif io == 'o': name += "o " else: name += "io " # basetype was already promoted at parse time (mirrors the C++ side, # where a second promote_int call would free a live string) name += v.basetype name += _id_qual(v.qual) if v.tinfo == VT.const: name += " " + v.name return name def _id_var(ctx, vars: list) -> str: return ", ".join(_id_var_single(ctx, v) for v in vars) def id_string(ctx, f) -> str: if not f: return "" name = "" if f.ret: name += _id_var(ctx, f.ret) + " = " if f.thisv: name += f.thisv + "->" + f.classv + "." name += f.funcv + "(" + _id_var(ctx, f.args) + ")" return name # --------------------------------------------------------------------------- # AST printing (for C comments) # --------------------------------------------------------------------------- def _print_expr(args: list) -> str: return ", ".join(e.value for e in args) def _print_qual(q): if not q: return "" if q.qual == 'a': return "[" + _print_expr(q.args) + "]" return q.qual def _print_devicespec(v): if v.devicespec == 'g': return "gpu " return "" def _print_iospec(v): m = {'o': "output ", 'b': "inout "} return m.get(v.iospec, "") def _print_var(v): return v.basetype + _print_qual(v.qual) + " " + v.name def _print_args(vars: list) -> str: return ", ".join( _print_devicespec(v) + _print_iospec(v) + _print_var(v) for v in vars ) def print_func(f): """Human-readable translation of Func AST (for C comments).""" if not f: return "" s = "" if f.ret: s += _print_var(f.ret[0]) + " = " if f.thisv: s += f"{f.thisv}->{f.classv}." s += f"{f.funcv}({_print_args(f.args)});\n" return s zgimbutas-mwrap-04cdb66/python/mwrap_cgen.py000066400000000000000000001163241522673526200213130ustar00rootroot00000000000000""" mwrap_cgen.py — C/MEX code generator. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ import sys from dataclasses import dataclass from mwrap_version import __version__ from mwrap_ast import ( VT, Expr, TypeQual, Var, Func, id_string, print_func, is_array, is_obj, complex_tinfo, nullable_return, ) # =================================================================== # Type property table — single source of truth for type dispatch # =================================================================== @dataclass(frozen=True) class TypeProps: mxclass: str # "mxDOUBLE_CLASS", "mxSINGLE_CLASS", etc. accessor: "str | None" # interleaved API accessor: "mxGetDoubles", etc. is_single: bool # True → use _single copier variants scalar_getter: str # "mxWrapGetScalar" / "_single" / "_char" scalar_class: str # mxClass for scalar validation direct_input: bool = False # True → use direct accessor for input-only arrays _DEFAULT_PROPS = TypeProps("mxVOID_CLASS", None, False, "mxWrapGetScalar", "mxDOUBLE_CLASS") TYPE_PROPS = { "double": TypeProps("mxDOUBLE_CLASS", "mxGetDoubles", False, "mxWrapGetScalar", "mxDOUBLE_CLASS", True), "float": TypeProps("mxSINGLE_CLASS", "mxGetSingles", True, "mxWrapGetScalar_single", "mxSINGLE_CLASS", True), "int32_t": TypeProps("mxINT32_CLASS", "mxGetInt32s", False, "mxWrapGetScalar", "mxDOUBLE_CLASS"), "int64_t": TypeProps("mxINT64_CLASS", "mxGetInt64s", False, "mxWrapGetScalar", "mxDOUBLE_CLASS"), "uint32_t": TypeProps("mxUINT32_CLASS", "mxGetUint32s", False, "mxWrapGetScalar", "mxDOUBLE_CLASS"), "uint64_t": TypeProps("mxUINT64_CLASS", "mxGetUint64s", False, "mxWrapGetScalar", "mxDOUBLE_CLASS"), "dcomplex": TypeProps("mxDOUBLE_CLASS", "mxGetComplexDoubles", False, "mxWrapGetScalar", "mxDOUBLE_CLASS"), "fcomplex": TypeProps("mxSINGLE_CLASS", "mxGetComplexSingles", True, "mxWrapGetScalar_single", "mxSINGLE_CLASS"), "char": TypeProps("mxCHAR_CLASS", None, False, "mxWrapGetScalar_char", "mxCHAR_CLASS"), } def _type_props(name): return TYPE_PROPS.get(name, _DEFAULT_PROPS) def _copier_suffix(bt): """Return 'single_' for float-precision types, '' otherwise.""" return "single_" if _type_props(bt).is_single else "" # =================================================================== # Utility functions # =================================================================== def basetype_to_cucomplex(name): if name == "fcomplex": return "cuFloatComplex" if name == "dcomplex": return "cuDoubleComplex" return name def basetype_to_mxclassid(name): return _type_props(name).mxclass def vname(v): if v.iospec == 'o': return f"out{v.output_label}_" return f"in{v.input_label}_" def has_fortran(funcs): return any(f.fort for f in funcs) def max_routine_id(funcs): maxid = 0 for f in funcs: if f.id > maxid: maxid = f.id return maxid def _alloc_size_expr(args): """Return C expression for product of dim args.""" if not args: return "1" return "*".join(f"dim{e.input_label}_" for e in args) def _interleaved_branch(fp, interleaved, fallback): """Emit #if MX_HAS_INTERLEAVED_COMPLEX / #else / #endif block.""" fp.write(f"#if MX_HAS_INTERLEAVED_COMPLEX\n{interleaved}#else\n{fallback}#endif\n") # =================================================================== # Complex type definitions # =================================================================== def mex_cpp_complex(fp): fp.write("#include \n\n" "typedef std::complex dcomplex;\n" "#define real_dcomplex(z) std::real(z)\n" "#define imag_dcomplex(z) std::imag(z)\n" "#define setz_dcomplex(z,r,i) *z = dcomplex(r,i)\n\n" "typedef std::complex fcomplex;\n" "#define real_fcomplex(z) std::real(z)\n" "#define imag_fcomplex(z) std::imag(z)\n" "#define setz_fcomplex(z,r,i) *z = fcomplex(r,i)\n\n") def mex_c99_complex(fp): fp.write("#include \n\n" "typedef _Complex double dcomplex;\n" "#define real_dcomplex(z) creal(z)\n" "#define imag_dcomplex(z) cimag(z)\n" "#define setz_dcomplex(z,r,i) *z = r + i*_Complex_I\n\n" "typedef _Complex float fcomplex;\n" "#define real_fcomplex(z) crealf(z)\n" "#define imag_fcomplex(z) cimagf(z)\n" "#define setz_fcomplex(z,r,i) *z = r + i*_Complex_I\n\n") def mex_gpucpp_complex(fp): fp.write("#include \n\n") # =================================================================== # Copier instantiation # =================================================================== def _mex_define_copiers_type(fp, ctx, name): """Emit copier macro calls for one scalar type.""" # Skip types not actually used if name == "int32_t" and not ctx.mw_use_int32_t: return if name == "int64_t" and not ctx.mw_use_int64_t: return if name == "uint32_t" and not ctx.mw_use_uint32_t: return if name == "uint64_t" and not ctx.mw_use_uint64_t: return if name == "ulong" and not ctx.mw_use_ulong: return if name == "uint" and not ctx.mw_use_uint: return if name == "ushort" and not ctx.mw_use_ushort: return if name == "uchar" and not ctx.mw_use_uchar: return fp.write(f"mxWrapGetArrayDef(mxWrapGetArray_{name}, {name})\n") fp.write(f"mxWrapCopyDef (mxWrapCopy_{name}, {name})\n") fp.write(f"mxWrapReturnDef (mxWrapReturn_{name}, {name})\n") fp.write(f"mxWrapGetArrayDef_single(mxWrapGetArray_single_{name}, {name})\n") fp.write(f"mxWrapCopyDef_single (mxWrapCopy_single_{name}, {name})\n") fp.write(f"mxWrapReturnDef_single (mxWrapReturn_single_{name}, {name})\n") def _mex_define_zcopiers(fp, name, ztype): """Emit complex copier macro calls for one complex type.""" fp.write(f"mxWrapGetScalarZDef(mxWrapGetScalar_{name}, {name},\n" f" {ztype}, setz_{name})\n") fp.write(f"mxWrapGetArrayZDef (mxWrapGetArray_{name}, {name},\n" f" {ztype}, setz_{name})\n") fp.write(f"mxWrapCopyZDef (mxWrapCopy_{name}, {name},\n" f" real_{name}, imag_{name})\n") fp.write(f"mxWrapReturnZDef (mxWrapReturn_{name}, {name},\n" f" real_{name}, imag_{name})\n") fp.write(f"mxWrapGetScalarZDef_single(mxWrapGetScalar_single_{name}, {name},\n" f" {ztype}, setz_{name})\n") fp.write(f"mxWrapGetArrayZDef_single (mxWrapGetArray_single_{name}, {name},\n" f" {ztype}, setz_{name})\n") fp.write(f"mxWrapCopyZDef_single (mxWrapCopy_single_{name}, {name},\n" f" real_{name}, imag_{name})\n") fp.write(f"mxWrapReturnZDef_single (mxWrapReturn_single_{name}, {name},\n" f" real_{name}, imag_{name})\n") def mex_define_copiers(fp, ctx): fp.write("\n\n\n/* Array copier definitions */\n") for name in sorted(ctx.scalar_decls): _mex_define_copiers_type(fp, ctx, name) for name in sorted(ctx.cscalar_decls): _mex_define_zcopiers(fp, name, "float") for name in sorted(ctx.zscalar_decls): _mex_define_zcopiers(fp, name, "double") fp.write("\n") # =================================================================== # Fortran name mangling # =================================================================== def _fortran_funcs(funcs): """Yield unique Func objects for fortran functions.""" seen = set() for f in funcs: if f.fort and f.funcv not in seen: seen.add(f.funcv) yield f def mex_define_fnames(fp, funcs): fp.write("#if defined(MWF77_CAPS)\n") for fc in _fortran_funcs(funcs): fp.write(f"#define MWF77_{fc.funcv} {fc.funcv.upper()}\n") fp.write("#elif defined(MWF77_UNDERSCORE1)\n") for fc in _fortran_funcs(funcs): fp.write(f"#define MWF77_{fc.funcv} {fc.funcv.lower()}_\n") fp.write("#elif defined(MWF77_UNDERSCORE0)\n") for fc in _fortran_funcs(funcs): fp.write(f"#define MWF77_{fc.funcv} {fc.funcv.lower()}\n") fp.write("#else /* f2c convention */\n") for fc in _fortran_funcs(funcs): low = fc.funcv.lower() suffix = "__" if '_' in low else "_" fp.write(f"#define MWF77_{fc.funcv} {low}{suffix}\n") fp.write("#endif\n\n") def _mex_fortran_arg(fp, args): parts = [] for v in args: if v.tinfo == VT.mx: parts.append("const mxArray*") else: parts.append(f"{v.basetype}*") fp.write(", ".join(parts)) def mex_fortran_decls(fp, funcs): fp.write("#ifdef __cplusplus\n" "extern \"C\" { /* Prevent C++ name mangling */\n" "#endif\n\n" "#ifndef MWF77_RETURN\n" "#define MWF77_RETURN int\n" "#endif\n\n") for fc in _fortran_funcs(funcs): if fc.ret: fp.write(f"{fc.ret[0].basetype} ") else: fp.write("MWF77_RETURN ") fp.write(f"MWF77_{fc.funcv}(") _mex_fortran_arg(fp, fc.args) fp.write(");\n") fp.write("\n#ifdef __cplusplus\n" "} /* end extern C */\n" "#endif\n\n") # =================================================================== # Class polymorphism getters # =================================================================== def _mex_casting_getter_type(fp, name): fp.write(f" {name}* p_{name} = NULL;\n" f" sscanf(pbuf, \"{name}:%p\", &p_{name});\n" f" if (p_{name})\n" f" return p_{name};\n\n") def _mex_casting_getter(fp, cname, inherits): fp.write(f"\n{cname}* mxWrapGetP_{cname}(const mxArray* a, const char** e)\n") fp.write("{\n" " char pbuf[128];\n" " if (mxGetClassID(a) == mxDOUBLE_CLASS &&\n" " mxGetM(a)*mxGetN(a) == 1 &&\n" "#if MX_HAS_INTERLEAVED_COMPLEX\n" " ((mxIsComplex(a) ? ((*mxGetComplexDoubles(a)).real == 0 && (*mxGetComplexDoubles(a)).imag == 0) : *mxGetDoubles(a) == 0))\n" "#else\n" " *mxGetPr(a) == 0\n" "#endif\n" " )\n" " return NULL;\n" " if (!mxIsChar(a)) {\n" "#ifdef R2008OO\n" f" mxArray* ap = mxGetProperty(a, 0, \"mwptr\");\n" f" if (ap)\n" f" return mxWrapGetP_{cname}(ap, e);\n" "#endif\n" " *e = \"Invalid pointer\";\n" " return NULL;\n" " }\n" " mxGetString(a, pbuf, sizeof(pbuf));\n\n") _mex_casting_getter_type(fp, cname) for name in inherits: _mex_casting_getter_type(fp, name) fp.write(f" *e = \"Invalid pointer to {cname}\";\n" f" return NULL;\n" f"}}\n\n") def mex_casting_getters(fp, ctx): for parent in sorted(ctx.class_decls.keys()): _mex_casting_getter(fp, parent, ctx.class_decls[parent]) # =================================================================== # Per-stub helpers: declarations, unpack, check, alloc, call, marshal, dealloc # =================================================================== def _declare_type(v): """Return C type string for a variable declaration.""" if is_obj(v.tinfo) or is_array(v.tinfo): if v.devicespec == 'g': return f"{basetype_to_cucomplex(v.basetype)}*" return f"{v.basetype}*" if v.tinfo == VT.rarray: if v.devicespec == 'g': return f"const {basetype_to_cucomplex(v.basetype)}*" return f"const {v.basetype}*" if v.tinfo in (VT.scalar, VT.cscalar, VT.zscalar, VT.r_scalar, VT.r_cscalar, VT.r_zscalar, VT.p_scalar, VT.p_cscalar, VT.p_zscalar): return v.basetype if v.tinfo == VT.string: return "char*" if v.tinfo == VT.mx: if v.iospec == 'i': return "const mxArray*" return "mxArray*" assert False, f"Unknown tinfo {v.tinfo} for {v.name}" # --- Step 1: Declare locals --- def _declare_in_args(fp, args): for v in args: if v.iospec != 'o' and v.tinfo != VT.const: tb = _declare_type(v) if is_array(v.tinfo) or is_obj(v.tinfo) or v.tinfo == VT.string: fp.write(f" {tb:10s} in{v.input_label}_ =0; /* {v.name:10s} */\n") if v.devicespec == 'g': fp.write(f" {'mxGPUArray const':10s} *mxGPUArray_in{v.input_label}_ =0; /* {v.name:10s} */\n") else: fp.write(f" {tb:10s} in{v.input_label}_; /* {v.name:10s} */\n") def _declare_out_args(fp, args): for v in args: if v.iospec == 'o' and v.tinfo != VT.mx: tb = _declare_type(v) if is_array(v.tinfo) or is_obj(v.tinfo) or v.tinfo == VT.string: fp.write(f" {tb:10s} out{v.output_label}_=0; /* {v.name:10s} */\n") if v.devicespec == 'g': fp.write(f" {'mxGPUArray':10s} *mxGPUArray_out{v.output_label}_ =0; /* {v.name:10s} */\n") fp.write(f" {'mwSize':10s} gpu_outdims{v.output_label}_[2] = {{0,0}}; /* {v.name:10s} dims*/\n") else: fp.write(f" {tb:10s} out{v.output_label}_; /* {v.name:10s} */\n") def _declare_dim_args_expr(fp, args): for e in args: fp.write(f" {'mwSize':10s} dim{e.input_label}_; /* {e.value:10s} */\n") def _declare_dim_args_var(fp, vars): for v in vars: if v.qual: _declare_dim_args_expr(fp, v.qual.args) def _declare_args(fp, f): if f.thisv: tb = f"{f.classv}*" fp.write(f" {tb:10s} in0_ =0; /* {f.thisv:10s} */\n") _declare_in_args(fp, f.args) if not nullable_return(f): _declare_out_args(fp, f.ret) _declare_out_args(fp, f.args) _declare_dim_args_var(fp, f.ret) _declare_dim_args_var(fp, f.args) if f.ret or f.args or f.thisv: fp.write("\n") # --- Step 2: Unpack dims --- def _unpack_dims_expr(fp, args): count = 0 for e in args: fp.write(f" dim{e.input_label}_ = (mwSize) mxWrapGetScalar(prhs[{e.input_label}], &mw_err_txt_);\n") count += 1 return count def _unpack_dims_var(fp, vars): count = 0 for v in vars: if v.qual: count += _unpack_dims_expr(fp, v.qual.args) return count def _unpack_dims(fp, f): c = _unpack_dims_var(fp, f.ret) + _unpack_dims_var(fp, f.args) if c: fp.write("\n") # --- Step 3: Check dim consistency --- def _check_dims(fp, args): for v in args: if (v.iospec != 'o' and is_array(v.tinfo) and v.qual and v.qual.args and v.devicespec != 'g'): a = v.qual.args if len(a) > 1: fp.write(f" if (mxGetM(prhs[{v.input_label}]) != dim{a[0].input_label}_ ||\n" f" mxGetN(prhs[{v.input_label}]) != dim{a[1].input_label}_) {{\n" f" mw_err_txt_ = \"Bad argument size: {v.name}\";\n" f" goto mw_err_label;\n" f" }}\n\n") else: fp.write(f" if (mxGetM(prhs[{v.input_label}])*mxGetN(prhs[{v.input_label}]) != dim{a[0].input_label}_) {{\n" f" mw_err_txt_ = \"Bad argument size: {v.name}\";\n" f" goto mw_err_label;\n" f" }}\n\n") # --- Step 4: Unpack inputs --- def _cast_get_p(fp, ctx, basetype, input_label): fp.write(f" in{input_label}_ = ") if ctx.is_mxarray_type(basetype): fp.write(f"mxWrapGet_{basetype}(prhs[{input_label}], &mw_err_txt_);\n") elif basetype not in ctx.class_decls: fp.write(f"({basetype}*) mxWrapGetP(prhs[{input_label}], \"{basetype}:%p\", &mw_err_txt_);\n") else: fp.write(f"mxWrapGetP_{basetype}(prhs[{input_label}], &mw_err_txt_);\n") fp.write(" if (mw_err_txt_)\n" " goto mw_err_label;\n\n") def _unpack_input_array(fp, v): il = v.input_label bt = v.basetype # --- Regular (copy) path for CPU --- if v.devicespec != 'g': tp = _type_props(bt) cs = _copier_suffix(bt) fp.write(f" if (mxGetM(prhs[{il}])*mxGetN(prhs[{il}]) != 0) {{\n") if complex_tinfo(v) and bt in TYPE_PROPS: # Known complex types: class check + copier fp.write(f" if( mxGetClassID(prhs[{il}]) != {tp.mxclass} )\n" f" mw_err_txt_ = \"Invalid array argument, {tp.mxclass} expected\";\n" f" if (mw_err_txt_) goto mw_err_label;\n" f" in{il}_ = mxWrapGetArray_{cs}{bt}(prhs[{il}], &mw_err_txt_);\n" f" if (mw_err_txt_)\n" f" goto mw_err_label;\n") elif tp.direct_input and v.iospec == 'i': # float/double input-only: class check + direct accessor fp.write(f" if( mxGetClassID(prhs[{il}]) != {tp.mxclass} )\n" f" mw_err_txt_ = \"Invalid array argument, {tp.mxclass} expected\";\n" f" if (mw_err_txt_) goto mw_err_label;\n" f"#if MX_HAS_INTERLEAVED_COMPLEX\n" f" if( mxIsComplex(prhs[{il}]) )\n" f" mw_err_txt_ = \"Invalid array argument, real data expected\";\n" f" if (mw_err_txt_) goto mw_err_label;\n" f" in{il}_ = {tp.accessor}(prhs[{il}]);\n" f"#else\n") if bt == "double": fp.write(f" in{il}_ = mxGetPr(prhs[{il}]);\n") else: fp.write(f" in{il}_ = ({bt}*) mxGetData(prhs[{il}]);\n") fp.write(f"#endif\n") else: # All other types (including float/double inout): copier fp.write(f" in{il}_ = mxWrapGetArray_{cs}{bt}(prhs[{il}], &mw_err_txt_);\n" f" if (mw_err_txt_)\n" f" goto mw_err_label;\n") fp.write(f" }} else\n" f" in{il}_ = NULL;\n") fp.write("\n") # --- GPU path --- if v.devicespec == 'g': if v.iospec in ('i', 'b'): cutype = basetype_to_cucomplex(bt) fp.write(f" // extract input GPU array pointer\n" f" if(!(mxIsGPUArray(prhs[{il}])))\n" f" mw_err_txt_ = \"Invalid array argument, gpuArray expected\";\n" f" if (mw_err_txt_) goto mw_err_label;\n" f" mxGPUArray_in{il}_ = mxGPUCreateFromMxArray(prhs[{il}]);\n" f" in{il}_ = ({cutype} *)mxGPUGetDataReadOnly(mxGPUArray_in{il}_);\n\n") def _unpack_input_string(fp, v): il = v.input_label if not (v.qual and v.qual.args): fp.write(f" in{il}_ = mxWrapGetString(prhs[{il}], &mw_err_txt_);\n" f" if (mw_err_txt_)\n" f" goto mw_err_label;\n") else: sz = _alloc_size_expr(v.qual.args) fp.write(f" in{il}_ = (char*) mxMalloc({sz}*sizeof(char));\n") fp.write(f" if (mxGetString(prhs[{il}], in{il}_, {sz}) != 0) {{\n" f" mw_err_txt_ = \"Invalid string argument\";\n" f" goto mw_err_label;\n" f" }}\n") fp.write("\n") def _unpack_inputs_var(fp, ctx, args): for v in args: if v.iospec == 'o': continue if is_obj(v.tinfo): _cast_get_p(fp, ctx, v.basetype, v.input_label) elif is_array(v.tinfo): _unpack_input_array(fp, v) elif v.tinfo in (VT.scalar, VT.r_scalar, VT.p_scalar): il = v.input_label bt = v.basetype tp = _type_props(bt) fp.write(f" if( mxGetClassID(prhs[{il}]) != {tp.scalar_class} )\n" f" mw_err_txt_ = \"Invalid scalar argument, {tp.scalar_class} expected\";\n" f" if (mw_err_txt_) goto mw_err_label;\n" f" in{il}_ = ({bt}) {tp.scalar_getter}(prhs[{il}], &mw_err_txt_);\n" f" if (mw_err_txt_)\n" f" goto mw_err_label;\n") if bt != "char": fp.write("\n") elif v.tinfo in (VT.cscalar, VT.zscalar, VT.r_cscalar, VT.r_zscalar, VT.p_cscalar, VT.p_zscalar): il = v.input_label bt = v.basetype tp = _type_props(bt) fp.write(f" if( mxGetClassID(prhs[{il}]) != {tp.scalar_class} )\n" f" mw_err_txt_ = \"Invalid scalar argument, {tp.scalar_class} expected\";\n" f" if( mxGetM(prhs[{il}])*mxGetN(prhs[{il}]) != 1 )\n" f" mw_err_txt_ = \"Invalid scalar argument, scalar expected\";\n" f" if (mw_err_txt_) goto mw_err_label;\n") cs = _copier_suffix(bt) fp.write(f" mxWrapGetScalar_{cs}{bt}(&in{il}_, prhs[{il}]);\n\n") elif v.tinfo == VT.string: _unpack_input_string(fp, v) elif v.tinfo == VT.mx: fp.write(f" in{v.input_label}_ = prhs[{v.input_label}];\n\n") def _unpack_inputs(fp, ctx, f): if f.thisv: _cast_get_p(fp, ctx, f.classv, 0) _unpack_inputs_var(fp, ctx, f.args) # --- Step 5: Null-check objects/this --- def _check_inputs(fp, args): for v in args: if v.iospec != 'o' and v.tinfo in (VT.obj, VT.r_obj): fp.write(f" if (!in{v.input_label}_) {{\n" f" mw_err_txt_ = \"Argument {v.name} cannot be null\";\n" f" goto mw_err_label;\n" f" }}\n") # --- Step 6: Allocate outputs --- def _alloc_output(fp, ctx, args, return_flag): for v in args: if v.iospec == 'o': if v.devicespec != 'g': if not return_flag and is_obj(v.tinfo) and ctx.is_mxarray_type(v.basetype): fp.write(f" out{v.output_label}_ = mxWrapAlloc_{v.basetype}();\n") elif is_array(v.tinfo): fp.write(f" out{v.output_label}_ = ({v.basetype}*) mxMalloc({_alloc_size_expr(v.qual.args)}*sizeof({v.basetype}));\n") elif v.tinfo == VT.rarray: fp.write(f" out{v.output_label}_ = ({v.basetype}*) NULL;\n") elif v.tinfo == VT.string: fp.write(f" out{v.output_label}_ = (char*) mxMalloc({_alloc_size_expr(v.qual.args)}*sizeof(char));\n") if v.devicespec == 'g': da = v.qual.args ndims = 2 if len(da) == 2 else 1 mtype = "mxCOMPLEX" if complex_tinfo(v) else "mxREAL" mxcid = basetype_to_mxclassid(v.basetype) cutype = basetype_to_cucomplex(v.basetype) if ndims == 2: fp.write(f" gpu_outdims{v.output_label}_[0] = dim{da[0].input_label}_; gpu_outdims{v.output_label}_[1] = dim{da[1].input_label}_;\n") else: fp.write(f" gpu_outdims{v.output_label}_[0] = dim{da[0].input_label}_;\n") fp.write(f" mxGPUArray_out{v.output_label}_ = mxGPUCreateGPUArray({ndims}, gpu_outdims{v.output_label}_, {mxcid}, {mtype}, MX_GPU_DO_NOT_INITIALIZE);\n") fp.write(f" out{v.output_label}_ = ({cutype} *)mxGPUGetData(mxGPUArray_out{v.output_label}_);\n\n") def _alloc_outputs(fp, ctx, f): if not nullable_return(f): _alloc_output(fp, ctx, f.ret, True) _alloc_output(fp, ctx, f.args, False) # --- Step 7: Profiler --- def _record_call(fp, f): fp.write(f" if (mexprofrecord_)\n" f" mexprofrecord_[{f.id}]++;\n") # --- Step 8: Make the call --- def _make_call_args(fp, args, first): for v in args: if not first: fp.write(", ") n = vname(v) if v.tinfo in (VT.obj, VT.r_obj): fp.write(f"*{n}") elif v.tinfo == VT.mx and v.iospec == 'o': fp.write(f"plhs+{v.output_label}") elif v.tinfo in (VT.p_scalar, VT.p_cscalar, VT.p_zscalar): fp.write(f"&{n}") elif v.tinfo == VT.const: fp.write(v.name) else: fp.write(n) first = False def _make_call_expr(fp, f): """Write the function call expression (without assignment/semicolon).""" if f.thisv: fp.write("in0_->") if f.funcv == "new": fp.write(f"new {f.classv}(") else: if f.fort: fp.write("MWF77_") fp.write(f"{f.funcv}(") _make_call_args(fp, f.args, True) fp.write(")") def _make_stmt(fp, ctx, f): if f.thisv: fp.write(" if (!in0_) {\n" " mw_err_txt_ = \"Cannot dispatch to NULL\";\n" " goto mw_err_label;\n" " }\n") if ctx.mw_generate_catch: fp.write(" try {\n ") if f.ret: v = f.ret[0] if v.tinfo == VT.obj: if ctx.is_mxarray_type(v.basetype): fp.write(f" plhs[0] = mxWrapSet_{v.basetype}(&(") _make_call_expr(fp, f) fp.write("));\n") else: fp.write(f" out0_ = new {v.basetype}(") _make_call_expr(fp, f) fp.write(");\n") elif is_array(v.tinfo): fp.write(f" plhs[0] = mxWrapReturn_{v.basetype}(") _make_call_expr(fp, f) fp.write(", ") args = v.qual.args if len(args) == 2: fp.write(f" dim{args[0].input_label}_, dim{args[1].input_label}_);\n") else: fp.write(f"{_alloc_size_expr(args)}, 1);\n") elif v.tinfo in (VT.scalar, VT.r_scalar, VT.cscalar, VT.r_cscalar, VT.zscalar, VT.r_zscalar): fp.write(" out0_ = ") _make_call_expr(fp, f) fp.write(";\n") elif v.tinfo == VT.string: fp.write(" plhs[0] = mxWrapStrncpy(") _make_call_expr(fp, f) fp.write(");\n") elif v.tinfo == VT.mx: fp.write(" plhs[0] = ") _make_call_expr(fp, f) fp.write(";\n") elif v.tinfo == VT.p_obj: if ctx.is_mxarray_type(v.basetype): fp.write(f" plhs[0] = mxWrapSet_{v.basetype}(") _make_call_expr(fp, f) fp.write(");\n") else: fp.write(" out0_ = ") _make_call_expr(fp, f) fp.write(";\n") elif v.tinfo in (VT.p_scalar, VT.p_cscalar, VT.p_zscalar): fp.write(f" plhs[0] = mxWrapReturn_{v.basetype}(") _make_call_expr(fp, f) fp.write(", 1, 1);\n") elif v.tinfo == VT.r_obj: if ctx.is_mxarray_type(v.basetype): fp.write(f" plhs[0] = mxWrapSet_{v.basetype}(&(") _make_call_expr(fp, f) fp.write("));\n") else: fp.write(" out0_ = &(") _make_call_expr(fp, f) fp.write(");\n") else: fp.write(" ") _make_call_expr(fp, f) fp.write(";\n") if ctx.mw_generate_catch: fp.write(f" }} catch(...) {{\n" f" mw_err_txt_ = \"Caught C++ exception from {f.funcv}\";\n" f" }}\n" f" if (mw_err_txt_)\n" f" goto mw_err_label;\n") # --- Step 9: Marshal results --- def _marshal_array(fp, v): il = v.input_label ol = v.output_label bt = v.basetype n = vname(v) if v.devicespec != 'g': da = v.qual.args mtype = "mxCOMPLEX" if complex_tinfo(v) else "mxREAL" ws = " " is_single = _type_props(bt).is_single if v.tinfo == VT.rarray: ws = " " fp.write(f" if (out{ol}_ == NULL) {{\n") fp.write(f" plhs[{ol}] = mxCreateDoubleMatrix(0,0, mxREAL);\n") fp.write(f" }} else {{\n") if not da: # No dims — inout array if is_single: fp.write(f"{ws}plhs[{ol}] = mxCreateNumericMatrix(mxGetM(prhs[{il}]), mxGetN(prhs[{il}]), mxSINGLE_CLASS, {mtype});\n") fp.write(f"{ws}mxWrapCopy_single_{bt}(plhs[{ol}], in{il}_, ") else: fp.write(f"{ws}plhs[{ol}] = mxCreateDoubleMatrix(mxGetM(prhs[{il}]), mxGetN(prhs[{il}]), {mtype});\n") fp.write(f"{ws}mxWrapCopy_{bt}(plhs[{ol}], in{il}_, ") fp.write(f"mxGetM(prhs[{il}])*mxGetN(prhs[{il}])") fp.write(");\n") elif len(da) == 1: # 1D if is_single: fp.write(f"{ws}plhs[{ol}] = mxCreateNumericMatrix(dim{da[0].input_label}_, 1, mxSINGLE_CLASS, {mtype});\n") fp.write(f"{ws}mxWrapCopy_single_{bt}(plhs[{ol}], {n}, ") else: fp.write(f"{ws}plhs[{ol}] = mxCreateDoubleMatrix(dim{da[0].input_label}_, 1, {mtype});\n") fp.write(f"{ws}mxWrapCopy_{bt}(plhs[{ol}], {n}, ") fp.write(f"dim{da[0].input_label}_") fp.write(");\n") elif len(da) == 2: # 2D if is_single: fp.write(f"{ws}plhs[{ol}] = mxCreateNumericMatrix(dim{da[0].input_label}_, dim{da[1].input_label}_, mxSINGLE_CLASS, {mtype});\n") fp.write(f"{ws}mxWrapCopy_single_{bt}(plhs[{ol}], {n}, ") else: fp.write(f"{ws}plhs[{ol}] = mxCreateDoubleMatrix(dim{da[0].input_label}_, dim{da[1].input_label}_, {mtype});\n") fp.write(f"{ws}mxWrapCopy_{bt}(plhs[{ol}], {n}, ") fp.write(f"dim{da[0].input_label}_*dim{da[1].input_label}_") fp.write(");\n") else: # 3D+ — flatten to 1D sz = _alloc_size_expr(da) if is_single: fp.write(f"{ws}plhs[{ol}] = mxCreateNumericMatrix({sz}, 1, mxSINGLE_CLASS, {mtype});\n") fp.write(f"{ws}mxWrapCopy_single_{bt}(plhs[{ol}], {n}, ") else: fp.write(f"{ws}plhs[{ol}] = mxCreateDoubleMatrix({sz}, 1, {mtype});\n") fp.write(f"{ws}mxWrapCopy_{bt}(plhs[{ol}], {n}, ") fp.write(sz) fp.write(");\n") if v.tinfo == VT.rarray: fp.write(" }\n") # GPU marshal if v.devicespec == 'g': if v.iospec == 'b': fp.write(f" plhs[{ol}] = prhs[{il}];\n") if v.iospec == 'o': fp.write(f" plhs[{ol}] = mxGPUCreateMxArrayOnGPU(mxGPUArray_out{ol}_);\n") def _marshal_result(fp, ctx, v, return_flag): n = vname(v) ol = v.output_label bt = v.basetype if is_obj(v.tinfo) and ctx.is_mxarray_type(bt): if not return_flag: fp.write(f" plhs[{ol}] = mxWrapSet_{bt}({n});\n") elif is_obj(v.tinfo): fp.write(f" plhs[{ol}] = mxWrapCreateP(out{ol}_, \"{bt}:%p\");\n") elif is_array(v.tinfo) or v.tinfo == VT.rarray: _marshal_array(fp, v) elif v.tinfo in (VT.scalar, VT.r_scalar, VT.p_scalar): _interleaved_branch(fp, f" plhs[{ol}] = mxCreateDoubleMatrix(1, 1, mxREAL);\n" f" *mxGetDoubles(plhs[{ol}]) = {n};\n", f" plhs[{ol}] = mxCreateDoubleMatrix(1, 1, mxREAL);\n" f" *mxGetPr(plhs[{ol}]) = {n};\n") elif v.tinfo in (VT.cscalar, VT.zscalar, VT.r_cscalar, VT.r_zscalar, VT.p_cscalar, VT.p_zscalar): _interleaved_branch(fp, f" plhs[{ol}] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);\n" f" mxGetComplexDoubles(plhs[{ol}])->real = real_{bt}({n});\n" f" mxGetComplexDoubles(plhs[{ol}])->imag = imag_{bt}({n});\n", f" plhs[{ol}] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);\n" f" *mxGetPr(plhs[{ol}]) = real_{bt}({n});\n" f" *mxGetPi(plhs[{ol}]) = imag_{bt}({n});\n") elif v.tinfo == VT.string: fp.write(f" plhs[{ol}] = mxCreateString({n});\n") def _marshal_results_var(fp, ctx, vars, return_flag): for v in vars: if v.iospec != 'i': _marshal_result(fp, ctx, v, return_flag) def _marshal_results(fp, ctx, f): if not nullable_return(f): _marshal_results_var(fp, ctx, f.ret, True) _marshal_results_var(fp, ctx, f.args, False) # --- Step 10: Dealloc --- def _dealloc_var(fp, ctx, vars, return_flag): for v in vars: if v.devicespec != 'g': if is_array(v.tinfo) or v.tinfo == VT.string: if v.iospec == 'o': fp.write(f" if (out{v.output_label}_) mxFree(out{v.output_label}_);\n") elif v.iospec == 'b' or not (v.basetype == "double" or v.basetype == "float"): fp.write(f" if (in{v.input_label}_) mxFree(in{v.input_label}_);\n") elif is_obj(v.tinfo) and ctx.is_mxarray_type(v.basetype): if v.iospec in ('i', 'b'): fp.write(f" if (in{v.input_label}_) mxWrapFree_{v.basetype}(in{v.input_label}_);\n") elif v.iospec == 'o' and not return_flag: fp.write(f" if (out{v.output_label}_) mxWrapFree_{v.basetype}(out{v.output_label}_);\n") if v.devicespec == 'g': if v.iospec in ('i', 'b'): fp.write(f" if (mxGPUArray_in{v.input_label}_) mxGPUDestroyGPUArray(mxGPUArray_in{v.input_label}_);\n") if v.iospec == 'o': fp.write(f" if (mxGPUArray_out{v.output_label}_) mxGPUDestroyGPUArray(mxGPUArray_out{v.output_label}_);\n") def _dealloc(fp, ctx, f): if not nullable_return(f): _dealloc_var(fp, ctx, f.ret, True) _dealloc_var(fp, ctx, f.args, False) # =================================================================== # Print a single MEX stub # =================================================================== def _print_c_comment(fp, f): fp.write(f"/* ---- {f.fname}: {f.line} ----\n") fp.write(f" * {print_func(f)}") for fsame in f.same: fp.write(f" * Also at {fsame.fname}: {fsame.line}\n") fp.write(" */\n") def _print_mex_stub(fp, ctx, f): _print_c_comment(fp, f) ids = id_string(ctx, f) fp.write(f"static const char* stubids{f.id}_ = \"{ids}\";\n\n") fp.write(f"void mexStub{f.id}(int nlhs, mxArray* plhs[],\n" f" int nrhs, const mxArray* prhs[])\n" f"{{\n" f" const char* mw_err_txt_ = 0;\n") _declare_args(fp, f) _unpack_dims(fp, f) _check_dims(fp, f.args) _unpack_inputs(fp, ctx, f) _check_inputs(fp, f.args) _alloc_outputs(fp, ctx, f) _record_call(fp, f) _make_stmt(fp, ctx, f) _marshal_results(fp, ctx, f) fp.write("\nmw_err_label:\n") _dealloc(fp, ctx, f) fp.write(" if (mw_err_txt_)\n" " mexErrMsgTxt(mw_err_txt_);\n" "}\n\n") # =================================================================== # Print all stubs, dispatch table, mexFunction # =================================================================== def _print_mex_stubs(fp, ctx, funcs): for f in funcs: _print_mex_stub(fp, ctx, f) def _make_profile_output(fp, funcs, printfunc): fp.write(f" if (!mexprofrecord_)\n" f" {printfunc}\"Profiler inactive\\n\");\n" f" else {{\n") for fc in funcs: fp.write(f" {printfunc}\"%d calls to {fc.fname}:{fc.line}") for fsame in fc.same: fp.write(f" ({fsame.fname}:{fsame.line})") fp.write(f"\\n\", mexprofrecord_[{fc.id}]);\n") fp.write(" }\n") def _print_mex_else_cases(fp, funcs): for fc in funcs: fp.write(f" else if (strcmp(id, stubids{fc.id}_) == 0)\n" f" mexStub{fc.id}(nlhs,plhs, nrhs-1,prhs+1);\n") maxid = max_routine_id(funcs) fp.write(f" else if (strcmp(id, \"*profile on*\") == 0) {{\n" f" if (!mexprofrecord_) {{\n" f" mexprofrecord_ = (int*) malloc({maxid+1} * sizeof(int));\n" f" mexLock();\n" f" }}\n" f" memset(mexprofrecord_, 0, {maxid+1} * sizeof(int));\n" f" }} else if (strcmp(id, \"*profile off*\") == 0) {{\n" f" if (mexprofrecord_) {{\n" f" free(mexprofrecord_);\n" f" mexUnlock();\n" f" }}\n" f" mexprofrecord_ = NULL;\n" f" }} else if (strcmp(id, \"*profile report*\") == 0) {{\n") _make_profile_output(fp, funcs, "mexPrintf(") fp.write(f" }} else if (strcmp(id, \"*profile log*\") == 0) {{\n" f" FILE* logfp;\n" f" if (nrhs != 2 || mxGetString(prhs[1], id, sizeof(id)) != 0)\n" f" mexErrMsgTxt(\"Must have two string arguments\");\n" f" logfp = fopen(id, \"w+\");\n" f" if (!logfp)\n" f" mexErrMsgTxt(\"Cannot open log for output\");\n") _make_profile_output(fp, funcs, "fprintf(logfp, ") fp.write(" fclose(logfp);\n") fp.write(" } else\n" " mexErrMsgTxt(\"Unknown identifier\");\n") # =================================================================== # Top-level: print_mex_init + print_mex_file # =================================================================== MWRAP_BANNER = ( "/* --------------------------------------------------- */\n" "/* Automatically generated by mwrap */\n" "/* --------------------------------------------------- */\n\n" ) MEX_BASE = ( "/* ----\n" " */\n" "void mexFunction(int nlhs, mxArray* plhs[],\n" " int nrhs, const mxArray* prhs[])\n" "{\n" " char id[1024];\n" " if (nrhs == 0) {\n" " mexPrintf(\"Mex function installed\\n\");\n" " return;\n" " }\n\n" ) MEX_BASE_IF = ( " if (mxGetString(prhs[0], id, sizeof(id)) != 0)\n" " mexErrMsgTxt(\"Identifier should be a string\");\n" ) def print_mex_init(fp, ctx, support_text): """Write the MEX file header: banner + runtime support + complex/GPU includes.""" fp.write(MWRAP_BANNER) fp.write(f"/* Code generated by mwrap {__version__} */\n") fp.write(support_text) fp.write("\n") if ctx.mw_use_gpu: fp.write("#include \n\n") if ctx.mw_use_c99_complex: mex_c99_complex(fp) elif ctx.mw_use_cpp_complex: mex_cpp_complex(fp) if ctx.mw_use_gpu: mex_gpucpp_complex(fp) def print_mex_file(fp, ctx, funcs): """Write the rest of the MEX file: copiers, getters, stubs, dispatch.""" if ctx.mw_use_int32_t or ctx.mw_use_int64_t or ctx.mw_use_uint32_t or ctx.mw_use_uint64_t: fp.write("#include \n\n") mex_define_copiers(fp, ctx) mex_casting_getters(fp, ctx) if has_fortran(funcs): mex_define_fnames(fp, funcs) mex_fortran_decls(fp, funcs) _print_mex_stubs(fp, ctx, funcs) fp.write(MEX_BASE) fp.write("\n") if ctx.mw_use_gpu: fp.write(" mxInitGPU();\n") fp.write("\n") fp.write(MEX_BASE_IF) _print_mex_else_cases(fp, funcs) fp.write("}\n\n") zgimbutas-mwrap-04cdb66/python/mwrap_lexer.py000066400000000000000000000304011522673526200215050ustar00rootroot00000000000000""" mwrap_lexer.py — Line-oriented lexer for .mw files. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ import re import sys import os from enum import Enum, auto from dataclasses import dataclass from typing import Optional, TextIO, List class TokenType(Enum): ID = auto() NUMBER = auto() STRING = auto() NEW = auto() FORTRAN = auto() INPUT = auto() OUTPUT = auto() INOUT = auto() CLASS = auto() TYPEDEF = auto() CPU = auto() GPU = auto() PUNCT = auto() # single characters: ( ) , ; * & [ ] . - > = : NON_C_LINE = auto() EOF = auto() KEYWORDS = { "new": TokenType.NEW, "FORTRAN": TokenType.FORTRAN, "input": TokenType.INPUT, "output": TokenType.OUTPUT, "inout": TokenType.INOUT, "class": TokenType.CLASS, "typedef": TokenType.TYPEDEF, "cpu": TokenType.CPU, "gpu": TokenType.GPU, } # Regex for tokenising a '#' line body _TOKEN_RE = re.compile( r"(//[^\n]*)" # comment (rest of line) — skip r"|('[^'\n]*'?)" # string literal (single-quoted) r"|((?:::)?[_a-zA-Z][_a-zA-Z0-9]*(?:::(?:[_a-zA-Z][_a-zA-Z0-9]*))*)" # ID (may have ::) r"|([0-9]+)" # number r"|([->()\[\],;*&=:.])" # punctuation r"|[ \t\r]+" # whitespace — skip r"|(.)" # anything else — passed through like flex's # CSTATE catch-all so the parser rejects it ) @dataclass class Token: type: TokenType value: str line: int def _is_name_char(c): return c.isalnum() or c == '_' def _fname_scan_line(text): """Extract function name from '@function ...' tail, return name.m.""" paren = text.find('(') if paren < 0: paren = len(text) # Walk back from paren to find last alnum end = paren while end > 0 and not _is_name_char(text[end - 1]): end -= 1 start = end while start > 0 and _is_name_char(text[start - 1]): start -= 1 name = text[start:end] return name + ".m" class Lexer: """Line-oriented lexer for .mw files. Usage: lex = Lexer(outfp, outcfp, mbatching_flag, listing_flag) for tok in lex.lex_file(filename): ... # only tokens from '#' lines; NON_C_LINE for other lines """ def __init__(self, outfp=None, outcfp=None, mbatching_flag=False, listing_flag=False): self.outfp: Optional[TextIO] = outfp self.outcfp: Optional[TextIO] = outcfp self.mbatching_flag: bool = mbatching_flag self.listing_flag: bool = listing_flag self.linenum: int = 0 self.current_ifname: str = "" # File include stack self._file_stack: List = [] # [(fp, linenum, ifname), ...] self._current_fp: Optional[TextIO] = None # ------------------------------------------------------------------ # public interface # ------------------------------------------------------------------ def lex_file(self, filename): """Yield Token objects from *filename*.""" fp = open(filename, "r") self._current_fp = fp self.current_ifname = filename self.linenum = 1 yield from self._lex_stream() # ------------------------------------------------------------------ # directive handlers # ------------------------------------------------------------------ def _handle_block_c(self, line): """Process a line in block C mode. Returns False when block ends. Like flex's BSTATE, the "$]" terminator may appear anywhere in the line as long as only whitespace follows it; any characters before it are written to the C output. """ m = re.search(r'\$\][ \t\r]*\n$', line) if m: if self.outcfp: self.outcfp.write(line[:m.start()]) self.linenum += 1 return False if self.outcfp: self.outcfp.write(line) self.linenum += 1 return True def _handle_comment(self): """Handle // comment line.""" self.linenum += 1 yield Token(TokenType.NON_C_LINE, "", self.linenum - 1) def _handle_function(self, stripped): """Handle @function directive.""" tail = stripped[len("@function"):] tail = tail.rstrip('\r\n') fname = _fname_scan_line(tail) if self.mbatching_flag: if self.outfp: self.outfp.close() try: self.outfp = open(fname, "w") except OSError: print(f"Error: Could not write {fname}", file=sys.stderr) sys.exit(1) if self.listing_flag: print(fname) if self.outfp: self.outfp.write(f"function{tail}\n") self.linenum += 1 yield Token(TokenType.NON_C_LINE, "", self.linenum - 1) def _handle_include(self, stripped): """Handle @include directive. Pushes current file onto stack. Like flex, this emits a NON_C_LINE token (so a missing ';' on the preceding declaration is reported at the @include line of the parent file), takes whitespace-separated tokens as filenames (several tokens are included in sequence), and resumes the parent file with the line after the @include line. """ rest = stripped[len("@include"):].rstrip('\r\n') names = rest.split() yield Token(TokenType.NON_C_LINE, "", self.linenum) resume_linenum = self.linenum + 1 if not names: self.linenum = resume_linenum return if len(self._file_stack) >= 10: print("Error: Includes nested too deeply", file=sys.stderr) sys.exit(1) self._file_stack.append( (self._current_fp, resume_linenum, self.current_ifname, names[1:])) self._open_include(names[0]) def _open_include(self, name): """Switch the token stream to an included file.""" try: new_fp = open(name, "r") except OSError: print(f"Error: Could not read '{name}'", file=sys.stderr) sys.exit(1) self.current_ifname = name self.linenum = 1 self._current_fp = new_fp def _handle_redirect(self, stripped): """Handle @ redirect directive. Only the first whitespace-separated token names the output file, matching flex's ASTATE rules.""" names = stripped[1:].rstrip('\r\n').split() rest = names[0] if names else "" if self.mbatching_flag: if self.outfp: self.outfp.close() self.outfp = None if rest: try: self.outfp = open(rest, "w") except OSError: print(f"Error: Could not write {rest}", file=sys.stderr) sys.exit(1) if self.listing_flag and rest: print(rest) self.linenum += 1 yield Token(TokenType.NON_C_LINE, "", self.linenum - 1) def _handle_dollar_line(self, stripped): """Handle $ single-line C pass-through.""" rest = stripped[1:] if self.outcfp: self.outcfp.write(rest) self.linenum += 1 yield Token(TokenType.NON_C_LINE, "", self.linenum - 1) def _handle_hash_line(self, stripped): """Handle # C declaration line — tokenise.""" body = stripped[1:].rstrip('\r\n') yield from self._tokenize_c_line(body, self.linenum) self.linenum += 1 # ------------------------------------------------------------------ # internal line-by-line driver # ------------------------------------------------------------------ def _lex_stream(self): """Process all lines from self._current_fp, yielding tokens.""" in_block_c = False while True: raw_line = self._current_fp.readline() if raw_line == "": # End of current file: pop the include stack. Like flex # (whose EOF rule leaves BSTATE), an unterminated $[ block # does not leak into the parent file. if self._file_stack: self._current_fp.close() in_block_c = False fp, lnum, ifname, pending = self._file_stack.pop() if pending: # Additional filenames on the same @include line self._file_stack.append( (fp, lnum, ifname, pending[1:])) self._open_include(pending[0]) else: self._current_fp = fp self.linenum = lnum self.current_ifname = ifname continue else: return # real EOF # Strip the trailing newline for processing but track it line = raw_line # --- block C mode ($[ ... $]) --- if in_block_c: in_block_c = self._handle_block_c(line) continue # Determine line prefix — compute leading whitespace stripped = line.lstrip(' \t') leading_ws = line[:len(line) - len(stripped)] # In the original Flex lexer, leading [ \t] in INITIAL state is # always written to outfp regardless of what prefix follows. # We replicate this for all prefix types except pure text lines # (which include their own leading whitespace in the full line). if leading_ws and self.outfp and ( stripped.startswith("$") or stripped.startswith("#") or stripped.startswith("@") or stripped.startswith("//")): self.outfp.write(leading_ws) # $[ block start (only when nothing but whitespace follows, # matching flex's \$\[[ \t\r]*\n rule; otherwise the line # falls through to the single-$ rule) if re.match(r'^\$\[[ \t\r]*\n$', stripped): in_block_c = True self.linenum += 1 continue if stripped.startswith("//"): yield from self._handle_comment() continue if stripped.startswith("@function"): yield from self._handle_function(stripped) continue if stripped.startswith("@include"): yield from self._handle_include(stripped) continue if stripped.startswith("@"): yield from self._handle_redirect(stripped) continue if stripped.startswith("$"): yield from self._handle_dollar_line(stripped) continue if stripped.startswith("#"): yield from self._handle_hash_line(stripped) continue # Text line — copy to MATLAB output if self.outfp: self.outfp.write(line) self.linenum += 1 yield Token(TokenType.NON_C_LINE, "", self.linenum - 1) # ------------------------------------------------------------------ # tokenise a single '#' line body # ------------------------------------------------------------------ def _tokenize_c_line(self, body, line): """Yield tokens for the body of a '#' line.""" for m in _TOKEN_RE.finditer(body): comment, string, ident, number, punct, other = m.groups() if comment: break # rest of line is a comment if string: yield Token(TokenType.STRING, string, line) elif ident: tt = KEYWORDS.get(ident, TokenType.ID) yield Token(tt, ident, line) elif number: yield Token(TokenType.NUMBER, number, line) elif punct: yield Token(TokenType.PUNCT, punct, line) elif other: yield Token(TokenType.PUNCT, other, line) zgimbutas-mwrap-04cdb66/python/mwrap_mgen.py000066400000000000000000000026761522673526200213310ustar00rootroot00000000000000""" mwrap_mgen.py — MATLAB stub generator. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ from mwrap_ast import VT, id_string def _output_arg_names(args): """Collect names of output/inout arguments.""" return [v.name for v in args if v.iospec in ('o', 'b')] def _input_arg_strs(args): """Collect input argument strings (with leading ', ').""" parts = [] for v in args: if v.tinfo == VT.const: parts.append(", 0") elif v.iospec in ('i', 'b'): parts.append(f", {v.name}") return parts def _dim_arg_strs(vars): """Collect dimension argument strings (with leading ', ').""" parts = [] for v in vars: if v.qual: for e in v.qual.args: parts.append(f", {e.value}") return parts def print_matlab_call(fp, ctx, f, mexfunc): """Emit MATLAB stub for function call f.""" fp.write(f"mex_id_ = '{id_string(ctx, f)}';\n") out_names = _output_arg_names(f.ret) + _output_arg_names(f.args) if out_names: fp.write(f"[{', '.join(out_names)}] = ") rhs = [f"mex_id_"] if f.thisv: rhs.append(f", {f.thisv}") rhs.extend(_input_arg_strs(f.args)) rhs.extend(_dim_arg_strs(f.ret)) rhs.extend(_dim_arg_strs(f.args)) fp.write(f"{mexfunc}({''.join(rhs)});\n") zgimbutas-mwrap-04cdb66/python/mwrap_parser.py000066400000000000000000000354221522673526200216720ustar00rootroot00000000000000""" mwrap_parser.py — Recursive descent parser for mwrap .mw files. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ import sys from mwrap_ast import ( Expr, TypeQual, Var, Func, VT, promote_int, id_string, add_inherits, ) from mwrap_lexer import Lexer, Token, TokenType from mwrap_typecheck import typecheck from mwrap_mgen import print_matlab_call class ParseError(Exception): pass class Parser: """Recursive descent parser for mwrap '#' lines. Usage: parser = Parser(lexer, ctx, mexfunc=mexfunc) for tok in lexer.lex_file(filename): parser.feed(tok) # After all files: funcs = parser.funcs # list[Func] err_flag = parser.err_flag """ def __init__(self, lexer, ctx, mexfunc="mexfunction"): self.lexer = lexer self.ctx = ctx self.mexfunc = mexfunc # Function list self.funcs = [] self.func_id = 0 self.func_lookup = {} # id_string -> first Func with that sig # Error tracking self.type_errs = 0 self.err_flag = 0 # Token buffer for current '#' line self._tokens = [] self._pos = 0 self._collecting = False # True while inside a '#' line # ------------------------------------------------------------------ # Token stream interface # ------------------------------------------------------------------ def feed(self, tok): """Feed one token from the lexer.""" if tok.type == TokenType.EOF: self._flush_pending(tok) return if tok.type == TokenType.NON_C_LINE: self._flush_pending(tok) return # Accumulate tokens from a '#' line self._tokens.append(tok) # A ';' terminates a statement — parse it if tok.type == TokenType.PUNCT and tok.value == ';': self._parse_line() self._tokens.clear() def _flush_pending(self, tok): """Report error if there are tokens pending without a ';'.""" if self._tokens: tname = "NON_C_LINE" if tok.type == TokenType.NON_C_LINE else "$end" fname = self.lexer.current_ifname line = tok.line print(f"Parse error ({fname}:{line}): syntax error, unexpected {tname}, expecting ';'", file=sys.stderr) self.err_flag += 1 self._tokens.clear() def finish_file(self): """Call after all tokens from one input file have been fed.""" # Flush any pending tokens (missing ';' at EOF) if self._tokens: fname = self.lexer.current_ifname line = self.lexer.linenum print(f"Parse error ({fname}:{line}): syntax error, unexpected $end, expecting ';'", file=sys.stderr) self.err_flag += 1 self._tokens.clear() if self.type_errs: print(f"{self.lexer.current_ifname}: {self.type_errs} type errors detected", file=sys.stderr) self.err_flag += self.type_errs self.type_errs = 0 # ------------------------------------------------------------------ # Token access helpers # ------------------------------------------------------------------ def _peek(self): if self._pos < len(self._tokens): return self._tokens[self._pos] return Token(TokenType.EOF, "", 0) def _advance(self): tok = self._peek() self._pos += 1 return tok def _expect(self, ttype, value=None): tok = self._advance() if tok.type != ttype or (value is not None and tok.value != value): exp = f"{ttype.name}" if value: exp += f" '{value}'" self._error(f"Expected {exp}, got {tok.type.name} '{tok.value}'") return tok def _expect_punct(self, ch): return self._expect(TokenType.PUNCT, ch) def _at(self, ttype, value=None): t = self._peek() if t.type != ttype: return False if value is not None and t.value != value: return False return True def _at_punct(self, ch): return self._at(TokenType.PUNCT, ch) def _line(self): """Current line number for error messages.""" if self._tokens: return self._tokens[0].line return self.lexer.linenum def _error(self, msg): fname = self.lexer.current_ifname line = self._line() print(f"Parse error ({fname}:{line}): {msg}", file=sys.stderr) raise ParseError(msg) # ------------------------------------------------------------------ # Top-level statement parse # ------------------------------------------------------------------ def _parse_line(self): """Parse one complete '#' line (already accumulated in _tokens).""" self._pos = 0 try: self._statement() except ParseError: self.err_flag += 1 def _statement(self): """statement ::= tdef | classdef | basevar '=' funcall | funcall""" tok = self._peek() if tok.type == TokenType.TYPEDEF: self._tdef() return if tok.type == TokenType.CLASS: self._classdef() return # Distinguish: basevar = funcall vs funcall # A funcall starts with: ID -> ... | ID ( | FORTRAN ID | NEW ID # A basevar starts with: ID ID or ID qual ID # The disambiguator: look for '=' before '(' or ';' if self._has_assignment(): bv = self._basevar() self._expect_punct('=') fc = self._funcall() fc.ret = [bv] self._finish_func(fc) else: fc = self._funcall() self._finish_func(fc) def _has_assignment(self): """Lookahead: is there a '=' before '(' or ';'?""" depth = 0 for i in range(len(self._tokens)): t = self._tokens[i] if t.type == TokenType.PUNCT: if t.value == '[': depth += 1 elif t.value == ']': depth -= 1 elif depth == 0 and t.value == '=': return True elif depth == 0 and t.value == '(': return False elif depth == 0 and t.value == ';': return False return False # ------------------------------------------------------------------ # typedef / classdef # ------------------------------------------------------------------ def _tdef(self): """TYPEDEF ID ID ';'""" self._expect(TokenType.TYPEDEF) space = self._expect(TokenType.ID).value name = self._expect(TokenType.ID).value self._expect_punct(';') if space == "numeric": self.ctx.add_scalar_type(name) elif space == "dcomplex": self.ctx.add_zscalar_type(name) elif space == "fcomplex": self.ctx.add_cscalar_type(name) elif space == "mxArray": self.ctx.add_mxarray_type(name) else: print(f"Unrecognized typespace: {space}", file=sys.stderr) self.type_errs += 1 def _classdef(self): """CLASS ID ':' inheritslist ';'""" self._expect(TokenType.CLASS) child = self._expect(TokenType.ID).value self._expect_punct(':') parents = self._inheritslist() self._expect_punct(';') add_inherits(self.ctx, child, parents) def _inheritslist(self): """ID (',' ID)* — returns list[str]""" result = [self._expect(TokenType.ID).value] while self._at_punct(','): self._advance() result.append(self._expect(TokenType.ID).value) return result # ------------------------------------------------------------------ # funcall / func # ------------------------------------------------------------------ def _funcall(self): """funcall ::= func '(' args ')' ';'""" f = self._func() self._expect_punct('(') f.args = self._args() self._expect_punct(')') self._expect_punct(';') return f def _func(self): """func ::= ID '->' ID '.' ID | ID | FORTRAN ID | NEW ID""" tok = self._peek() if tok.type == TokenType.FORTRAN: self._advance() name = self._expect(TokenType.ID).value f = Func(None, None, name, self.lexer.current_ifname, self._line()) f.fort = True return f if tok.type == TokenType.NEW: self._advance() classname = self._expect(TokenType.ID).value return Func(None, classname, "new", self.lexer.current_ifname, self._line()) # ID — could be plain func, or thisvar -> classname . method name = self._expect(TokenType.ID).value if self._at_punct('-'): # thisvar -> classname . method self._advance() # '-' self._expect_punct('>') classname = self._expect(TokenType.ID).value self._expect_punct('.') method = self._expect(TokenType.ID).value return Func(name, classname, method, self.lexer.current_ifname, self._line()) return Func(None, None, name, self.lexer.current_ifname, self._line()) # ------------------------------------------------------------------ # args / var # ------------------------------------------------------------------ def _args(self): """args ::= var (',' var)* | ε — returns list[Var]""" if self._at_punct(')'): return [] result = [self._var()] while self._at_punct(','): self._advance() result.append(self._var()) return result def _var(self): """var ::= [devicespec] [iospec] TYPE [qual] (NAME | NUMBER | STRING) OR: [devicespec] [iospec] TYPE NAME [aqual] (post-name array) """ devicespec = self._devicespec() iospec = self._iospec() basetype = promote_int(self.ctx, self._expect(TokenType.ID).value) # Now we may see: # quals then NAME/NUMBER/STRING # NAME/NUMBER/STRING then optional aqual # Just NAME/NUMBER/STRING (no qual) tok = self._peek() if tok.type == TokenType.PUNCT and tok.value in ('*', '&', '['): # quals before name qual = self._quals() name = self._name_or_literal() return Var(devicespec, iospec, basetype, qual, name) # NAME/NUMBER/STRING first, then optional aqual name = self._name_or_literal() # Check for post-name aqual: name [ ... ] or name [ ... ] & if self._at_punct('['): qual = self._aqual() return Var(devicespec, iospec, basetype, qual, name) return Var(devicespec, iospec, basetype, None, name) def _name_or_literal(self): tok = self._peek() if tok.type in (TokenType.ID, TokenType.NUMBER, TokenType.STRING): self._advance() return tok.value self._error(f"Expected name, number or string, got {tok.type.name} '{tok.value}'") def _devicespec(self): tok = self._peek() if tok.type == TokenType.CPU: self._advance() return 'c' if tok.type == TokenType.GPU: self._advance() return 'g' return 'c' def _iospec(self): tok = self._peek() m = { TokenType.INPUT: 'i', TokenType.OUTPUT: 'o', TokenType.INOUT: 'b', } if tok.type in m: self._advance() return m[tok.type] return 'i' def _quals(self): """quals ::= '*' | '&' | aqual""" if self._at_punct('*'): self._advance() return TypeQual('*') if self._at_punct('&'): self._advance() return TypeQual('&') return self._aqual() def _aqual(self): """aqual ::= arrayspec ['&']""" exprs = self._arrayspec() if self._at_punct('&'): self._advance() return TypeQual('r', exprs) return TypeQual('a', exprs) def _arrayspec(self): """'[' exprs ']' — returns list[Expr]""" self._expect_punct('[') e = self._exprs() self._expect_punct(']') return e def _exprs(self): """exprs ::= expr (',' expr)* | ε — returns list[Expr]""" if self._at_punct(']'): return [] result = [self._expr()] while self._at_punct(','): self._advance() result.append(self._expr()) return result def _expr(self): """expr ::= ID | NUMBER""" tok = self._peek() if tok.type in (TokenType.ID, TokenType.NUMBER): self._advance() return Expr(tok.value) self._error(f"Expected expression, got {tok.type.name} '{tok.value}'") def _basevar(self): """basevar ::= ID [quals] ID — always output, cpu""" basetype = promote_int(self.ctx, self._expect(TokenType.ID).value) # Peek: quals or name? tok = self._peek() if tok.type == TokenType.PUNCT and tok.value in ('*', '&', '['): qual = self._quals() name = self._expect(TokenType.ID).value return Var('c', 'o', basetype, qual, name) # NAME then optional aqual name = self._expect(TokenType.ID).value if self._at_punct('['): qual = self._aqual() return Var('c', 'o', basetype, qual, name) return Var('c', 'o', basetype, None, name) # ------------------------------------------------------------------ # Post-parse: typecheck, MATLAB stub, add to func list # ------------------------------------------------------------------ def _finish_func(self, func): """Typecheck, emit MATLAB stub, add to function list.""" self.func_id += 1 func.id = self.func_id self.type_errs += typecheck(self.ctx, func, self._line()) if self.lexer.outfp: print_matlab_call(self.lexer.outfp, self.ctx, func, self.mexfunc) self._add_func(func) def _add_func(self, func): """Add func to list; deduplicate via id_string.""" ids = id_string(self.ctx, func) first = self.func_lookup.get(ids) if first: # Same signature — add to first occurrence's same list first.same.append(func) else: # New unique signature — append to main list self.funcs.append(func) self.func_lookup[ids] = func zgimbutas-mwrap-04cdb66/python/mwrap_support.c000066400000000000000000000550611522673526200217050ustar00rootroot00000000000000/* Copyright statement for mwrap: mwrap -- MEX file generation for MATLAB and Octave Copyright (c) 2007-2008 David Bindel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. You may distribute a work that contains part or all of the source code generated by mwrap under the terms of your choice. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #if MX_HAS_INTERLEAVED_COMPLEX #include #endif /* * Records for call profile. */ int* mexprofrecord_= NULL; double mxWrapGetScalar_char(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxCHAR_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid char argument"; return 0; } return (char) (*mxGetChars(a)); } /* * Support routines for copying data into and out of the MEX stubs, R2018a */ #if MX_HAS_INTERLEAVED_COMPLEX void* mxWrapGetP(const mxArray* a, const char* fmt, const char** e) { void* p = NULL; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxDOUBLE_CLASS && mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && (*mxGetComplexDoubles(a)).real == 0 ) return NULL; } if (mxGetClassID(a) == mxDOUBLE_CLASS && !mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && *mxGetDoubles(a) == 0) return NULL; } if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetDoubles(z) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetDoubles(z) = 0; return z; } } char* mxWrapGetString(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } double mxWrapGetScalar(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxDOUBLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } if( mxIsComplex(a) ) return (double) (*mxGetComplexDoubles(a)).real; else return (double) (*mxGetDoubles(a)); } #define mxWrapGetArrayDef(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* q; \ mxComplexDouble* z; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*z++).real; \ } \ else \ { \ q = mxGetDoubles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ } \ return array; \ } #define mxWrapCopyDef(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* p; \ mxComplexDouble* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < n; ++i) { \ z[i].real = (double) *q++; \ z[i].imag = 0; \ } \ } \ else \ { \ p = mxGetDoubles(a); \ for (i = 0; i < n; ++i) \ *p++ = (double) *q++; \ } \ } #define mxWrapReturnDef(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ double* p; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxREAL); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxREAL); \ p = mxGetDoubles(a); \ for (i = 0; i < m*n; ++i) \ *p++ = (double) *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ if( mxIsComplex(a) ) \ { \ setz(z, (ZT) (*mxGetComplexDoubles(a)).real, (ZT) (*mxGetComplexDoubles(a)).imag); \ } \ else \ { \ setz(z, (ZT) *mxGetDoubles(a), (ZT) 0); \ } \ } #define mxWrapGetArrayZDef(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* q; \ mxComplexDouble* z; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*z).real, (ZT) (*z).imag); \ ++p; ++z; } \ } \ else \ { \ q = mxGetDoubles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*q), (ZT) 0 ); \ ++p; ++q; } \ } \ return array; \ } #define mxWrapCopyZDef(func, T, freal, fimag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* p; \ mxComplexDouble* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < n; ++i) { \ (*z).real = freal(*q); \ (*z).imag = fimag(*q); \ ++z; ++q; } \ } \ else \ { \ p = mxGetDoubles(a); \ for (i = 0; i < n; ++i) \ *p++ = freal(*q++); \ } \ } #define mxWrapReturnZDef(func, T, freal, fimag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ mxComplexDouble* p; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxCOMPLEX); \ p = mxGetComplexDoubles(a); \ for (i = 0; i < m*n; ++i) { \ (*p).real = freal(*q); \ (*p).imag = fimag(*q); \ ++p; ++q; } \ return a; \ } \ } void* mxWrapGetP_single(const mxArray* a, const char* fmt, const char** e) { void* p = NULL; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxSINGLE_CLASS && mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && (*mxGetComplexSingles(a)).real == 0 ) return NULL; } if (mxGetClassID(a) == mxSINGLE_CLASS && !mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && *mxGetSingles(a) == 0) return NULL; } if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP_single(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *mxGetSingles(z) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy_single(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *mxGetSingles(z) = 0; return z; } } float mxWrapGetScalar_single(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxSINGLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } if( mxIsComplex(a) ) return (float) (*mxGetComplexSingles(a)).real; else return (float) (*mxGetSingles(a)); } char* mxWrapGetString_single(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } #define mxWrapGetArrayDef_single(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* q; \ mxComplexSingle* z; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*z++).real; \ } \ else \ { \ q = mxGetSingles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ } \ return array; \ } #define mxWrapCopyDef_single(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* p; \ mxComplexSingle* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < n; ++i) { \ z[i].real = (float) *q++; \ z[i].imag = 0; \ } \ } \ else \ { \ p = mxGetSingles(a); \ for (i = 0; i < n; ++i) \ *p++ = (float) *q++; \ } \ } #define mxWrapReturnDef_single(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ float* p; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxREAL); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxREAL); \ p = mxGetSingles(a); \ for (i = 0; i < m*n; ++i) \ *p++ = (float) *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef_single(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ if( mxIsComplex(a) ) \ { \ setz(z, (ZT) (*mxGetComplexSingles(a)).real, (ZT) (*mxGetComplexSingles(a)).imag); \ } \ else \ { \ setz(z, (ZT) *mxGetSingles(a), (ZT) 0); \ } \ } #define mxWrapGetArrayZDef_single(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* q; \ mxComplexSingle* z; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*z).real, (ZT) (*z).imag); \ ++p; ++z; } \ } \ else \ { \ q = mxGetSingles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*q), (ZT) 0 ); \ ++p; ++q; } \ } \ return array; \ } #define mxWrapCopyZDef_single(func, T, freal, fimag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* p; \ mxComplexSingle* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < n; ++i) { \ (*z).real = freal(*q); \ (*z).imag = fimag(*q); \ ++z; ++q; } \ } \ else \ { \ p = mxGetSingles(a); \ for (i = 0; i < n; ++i) \ *p++ = freal(*q++); \ } \ } #define mxWrapReturnZDef_single(func, T, freal, fimag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ mxComplexSingle* p; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxCOMPLEX); \ p = mxGetComplexSingles(a); \ for (i = 0; i < m*n; ++i) { \ (*p).real = freal(*q); \ (*p).imag = fimag(*q); \ ++p; ++q; } \ return a; \ } \ } #else /* * Support routines for copying data into and out of the MEX stubs, -R2017b */ void* mxWrapGetP(const mxArray* a, const char* fmt, const char** e) { void* p = 0; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxDOUBLE_CLASS && mxGetM(a)*mxGetN(a) == 1 && *mxGetPr(a) == 0) return p; if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetPr(z) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetPr(z) = 0; return z; } } double mxWrapGetScalar(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxDOUBLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } return *mxGetPr(a); } char* mxWrapGetString(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } #define mxWrapGetArrayDef(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* q; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ q = mxGetPr(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ return array; \ } #define mxWrapCopyDef(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* p = mxGetPr(a); \ for (i = 0; i < n; ++i) \ *p++ = *q++; \ } #define mxWrapReturnDef(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ double* p; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxREAL); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxREAL); \ p = mxGetPr(a); \ for (i = 0; i < m*n; ++i) \ *p++ = *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ double* pr = mxGetPr(a); \ double* pi = mxGetPi(a); \ setz(z, (ZT) *pr, (pi ? (ZT) *pi : (ZT) 0)); \ } #define mxWrapGetArrayZDef(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* qr; \ double* qi; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ qr = mxGetPr(a); \ qi = mxGetPi(a); \ for (i = 0; i < arraylen; ++i) { \ ZT val_qr = *qr++; \ ZT val_qi = (qi ? (ZT) *qi++ : (ZT) 0); \ setz(p, val_qr, val_qi); \ ++p; \ } \ return array; \ } #define mxWrapCopyZDef(func, T, real, imag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* pr = mxGetPr(a); \ double* pi = mxGetPi(a); \ for (i = 0; i < n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ } #define mxWrapReturnZDef(func, T, real, imag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ double* pr; \ double* pi; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxCOMPLEX); \ pr = mxGetPr(a); \ pi = mxGetPi(a); \ for (i = 0; i < m*n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ return a; \ } \ } void* mxWrapGetP_single(const mxArray* a, const char* fmt, const char** e) { void* p = 0; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxSINGLE_CLASS && mxGetM(a)*mxGetN(a) == 1 && *((float*)mxGetData(a)) == 0) return p; if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP_single(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *((float*)mxGetData(z)) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy_single(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *((float*)mxGetData(z)) = 0; return z; } } float mxWrapGetScalar_single(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxSINGLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } return *((float*)mxGetData(a)); } char* mxWrapGetString_single(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument, mxSINGLE_CLASS expected"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } #define mxWrapGetArrayDef_single(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* q; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ q = (float*) mxGetData(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ return array; \ } #define mxWrapCopyDef_single(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* p = (float*) mxGetData(a); \ for (i = 0; i < n; ++i) \ *p++ = *q++; \ } #define mxWrapReturnDef_single(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ float* p; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxREAL); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxREAL);\ p = (float*) mxGetData(a); \ for (i = 0; i < m*n; ++i) \ *p++ = *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef_single(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ float* pr = (float*) mxGetData(a); \ float* pi = (float*) mxGetImagData(a); \ setz(z, (ZT) *pr, (pi ? (ZT) *pi : (ZT) 0)); \ } #define mxWrapGetArrayZDef_single(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* qr; \ float* qi; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ qr = (float*) mxGetData(a); \ qi = (float*) mxGetImagData(a); \ for (i = 0; i < arraylen; ++i) { \ ZT val_qr = *qr++; \ ZT val_qi = (qi ? (ZT) *qi++ : (ZT) 0); \ setz(p, val_qr, val_qi); \ ++p; \ } \ return array; \ } #define mxWrapCopyZDef_single(func, T, real, imag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* pr = (float*) mxGetData(a); \ float* pi = (float*) mxGetImagData(a); \ for (i = 0; i < n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ } #define mxWrapReturnZDef_single(func, T, real, imag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ float* pr; \ float* pi; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxCOMPLEX);\ pr = (float*) mxGetData(a); \ pi = (float*) mxGetImagData(a); \ for (i = 0; i < m*n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ return a; \ } \ } #endif zgimbutas-mwrap-04cdb66/python/mwrap_typecheck.py000066400000000000000000000254351522673526200223600ustar00rootroot00000000000000""" mwrap_typecheck.py — Semantic analysis and type classification. Copyright (c) 2007-2008 David Bindel See the file COPYING for copying permissions Converted to Python by Zydrunas Gimbutas (2026), with assistance from Claude Code / Claude Opus 4.6 (Anthropic). """ import sys from mwrap_ast import ( VT, Expr, TypeQual, Var, Func, promote_int, iospec_is_input, iospec_is_output, iospec_is_inonly, ) # --------------------------------------------------------------------------- # Label assignment # --------------------------------------------------------------------------- def _label_dim_args_expr(args, icount): """Assign input_label to dimension expressions; returns updated icount.""" for e in args: e.input_label = icount icount += 1 return icount def _label_dim_args_var(vars, icount): """Walk var list, label dimension expressions.""" for v in vars: if v.qual: icount = _label_dim_args_expr(v.qual.args, icount) return icount def _label_args_var(vars, icount, ocount): """Assign input/output labels to vars; returns (icount, ocount).""" for v in vars: if iospec_is_input(v.iospec): v.input_label = icount icount += 1 if iospec_is_output(v.iospec): v.output_label = ocount ocount += 1 return icount, ocount def label_args(f): """Assign prhs[] / plhs[] indices to a Func's variables.""" icount = 1 if f.thisv else 0 ocount = 0 icount, ocount = _label_args_var(f.ret, icount, ocount) icount, ocount = _label_args_var(f.args, icount, ocount) icount = _label_dim_args_var(f.ret, icount) icount = _label_dim_args_var(f.args, icount) # --------------------------------------------------------------------------- # Type-info assignment # --------------------------------------------------------------------------- def _assign_scalar_tinfo(v, line, tags, tagp, tagr, taga, tagar): """Assign tinfo for a scalar/complex type. Returns error count.""" if not v.qual: v.tinfo = tags elif v.qual.qual == '*': v.tinfo = tagp elif v.qual.qual == '&': v.tinfo = tagr elif v.qual.qual == 'a': v.tinfo = taga # check max 2D if len(v.qual.args) > 2: print(f"Error ({line}): Array {v.name} should be 1D or 2D", file=sys.stderr) return 1 elif v.qual.qual == 'r': v.tinfo = tagar if tagar == VT.unk: print(f"Error ({line}): Array ref {v.name} must be to a real array", file=sys.stderr) return 1 if len(v.qual.args) > 2: print(f"Error ({line}): Array {v.name} should be 1D or 2D", file=sys.stderr) return 1 else: assert False, f"Unknown qual '{v.qual.qual}'" return 0 def assign_tinfo(ctx, v, line): """Assign VT_* tinfo to a single Var. Returns error count.""" bt = v.basetype if ctx.is_scalar_type(bt): return _assign_scalar_tinfo(v, line, VT.scalar, VT.p_scalar, VT.r_scalar, VT.array, VT.rarray) elif ctx.is_cscalar_type(bt): return _assign_scalar_tinfo(v, line, VT.cscalar, VT.p_cscalar, VT.r_cscalar, VT.carray, VT.unk) elif ctx.is_zscalar_type(bt): return _assign_scalar_tinfo(v, line, VT.zscalar, VT.p_zscalar, VT.r_zscalar, VT.zarray, VT.unk) elif bt == "const": if v.qual: print(f"Error ({line}): Constant {v.name} cannot have modifiers", file=sys.stderr) return 1 v.tinfo = VT.const # Strip quotes from name if present if v.name and v.name.startswith("'"): v.name = v.name.replace("'", "") elif bt == "cstring": if v.qual and v.qual.qual != 'a': print(f"Error ({line}): String type {v.name} cannot have modifiers", file=sys.stderr) return 1 if v.qual and len(v.qual.args) > 1: print(f"Error ({line}): Strings are one dimensional", file=sys.stderr) return 1 v.tinfo = VT.string elif bt == "mxArray": if v.qual: print(f"Error ({line}): mxArray {v.name} cannot have modifiers", file=sys.stderr) return 1 v.tinfo = VT.mx else: # Object type if not v.qual: v.tinfo = VT.obj elif v.qual.qual == '*': v.tinfo = VT.p_obj elif v.qual.qual == '&': v.tinfo = VT.r_obj elif v.qual.qual in ('a', 'r'): print(f"Error ({line}): {v.name} cannot be an array of object {bt}", file=sys.stderr) return 1 else: assert False return 0 # --------------------------------------------------------------------------- # Return-value validation # --------------------------------------------------------------------------- def _typecheck_return(ctx, ret, line): if not ret: return 0 v = ret[0] err = assign_tinfo(ctx, v, line) if v.tinfo in (VT.array, VT.carray, VT.zarray): if not (v.qual and v.qual.args): print(f"Error ({line}): Return array {v.name} must have dims", file=sys.stderr) err += 1 elif v.tinfo == VT.const: print(f"Error ({line}): Cannot return constant", file=sys.stderr) err += 1 elif v.tinfo == VT.rarray: print(f"Error ({line}): Ref to array {v.name} looks just like array on return", file=sys.stderr) err += 1 elif v.tinfo == VT.string and v.qual: print(f"Error ({line}): Return string {v.name} cannot have dims", file=sys.stderr) err += 1 return err # --------------------------------------------------------------------------- # Argument validation # --------------------------------------------------------------------------- def _typecheck_args(ctx, args, line): err = 0 for v in args: err += assign_tinfo(ctx, v, line) if v.devicespec == 'g' and v.tinfo not in (VT.array, VT.carray, VT.zarray): if (v.iospec != 'o' and v.tinfo in (VT.obj, VT.p_obj, VT.r_obj, VT.string)): # Object/string inputs compiled with the gpu qualifier as a # no-op in 1.2; keep accepting them with a warning, and # clear the qualifier so codegen really does ignore it. # Relies on typecheck running before print_matlab_call and # codegen read devicespec (parser calls typecheck first). print(f"Warning ({line}): gpu qualifier on non-array {v.name} is ignored", file=sys.stderr) v.devicespec = 'c' else: print(f"Error ({line}): gpu variable {v.name} must be a numeric array", file=sys.stderr) err += 1 if iospec_is_inonly(v.iospec): continue # Output / inout checks if v.name and v.name[0:1].isdigit(): print(f"Error ({line}): Number {v.name} cannot be output", file=sys.stderr) err += 1 if (v.tinfo in (VT.obj, VT.p_obj, VT.r_obj) and not ctx.is_mxarray_type(v.basetype)): print(f"Error ({line}): Object {v.name} cannot be output", file=sys.stderr) err += 1 elif (v.tinfo in (VT.array, VT.carray, VT.zarray, VT.rarray) and v.iospec == 'o' and not (v.qual and v.qual.args)): print(f"Error ({line}): Output array {v.name} must have dims", file=sys.stderr) err += 1 elif v.tinfo == VT.rarray and v.iospec != 'o': print(f"Error ({line}): Array ref {v.name} *must* be output", file=sys.stderr) err += 1 elif v.tinfo == VT.scalar: print(f"Error ({line}): Scalar {v.name} cannot be output", file=sys.stderr) err += 1 elif v.tinfo == VT.const: print(f"Error ({line}): Constant {v.name} cannot be output", file=sys.stderr) err += 1 elif v.tinfo == VT.string and not (v.qual and v.qual.args): print(f"Error ({line}): String {v.name} cannot be output without size", file=sys.stderr) err += 1 elif v.tinfo == VT.mx and v.iospec == 'b': print(f"Error ({line}): mxArray {v.name} cannot be used for inout", file=sys.stderr) err += 1 return err # --------------------------------------------------------------------------- # Fortran-ize arguments # --------------------------------------------------------------------------- def _fortranize_args_var(args, line): """Convert scalar args to pointer-to-scalar for FORTRAN. Returns error count.""" err = 0 for v in args: if v.tinfo in (VT.obj, VT.p_obj, VT.r_obj): print(f"Error ({line}): Cannot pass object {v.name} to FORTRAN", file=sys.stderr) err += 1 elif v.tinfo == VT.rarray: print(f"Error ({line}): Cannot pass pointer ref {v.name} to FORTRAN", file=sys.stderr) err += 1 elif v.tinfo == VT.string: print(f"Warning ({line}): Danger passing C string {v.name} to FORTRAN", file=sys.stderr) elif v.tinfo in (VT.scalar, VT.r_scalar): v.tinfo = VT.p_scalar elif v.tinfo in (VT.cscalar, VT.r_cscalar): v.tinfo = VT.p_cscalar elif v.tinfo in (VT.zscalar, VT.r_zscalar): v.tinfo = VT.p_zscalar return err def _fortranize_ret(v, line): if not v: return 0 if v.tinfo in (VT.cscalar, VT.zscalar): print(f"Warning ({line}): Danger returning complex from FORTRAN", file=sys.stderr) elif v.tinfo != VT.scalar: print(f"Error ({line}): Can only return scalars from FORTRAN", file=sys.stderr) return 1 return 0 def _fortranize_args(f, line): if not f.fort: return 0 err = _fortranize_args_var(f.args, line) if f.ret: err += _fortranize_ret(f.ret[0], line) return err # --------------------------------------------------------------------------- # Top-level typecheck # --------------------------------------------------------------------------- def typecheck(ctx, f, line): """Run full semantic analysis on a Func. Returns error count.""" label_args(f) return (_typecheck_return(ctx, f.ret, line) + _typecheck_args(ctx, f.args, line) + _fortranize_args(f, line)) zgimbutas-mwrap-04cdb66/python/mwrap_version.py000066400000000000000000000004311522673526200220530ustar00rootroot00000000000000"""Single source of truth for the mwrap version (Python implementation). Keep in sync with src/mwrap-version.h; the parity suite (testing/test_python.sh) fails on any mismatch because both implementations stamp this version into their generated output. """ __version__ = "1.3.5" zgimbutas-mwrap-04cdb66/src/000077500000000000000000000000001522673526200160565ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/src/CMakeLists.txt000066400000000000000000000046231522673526200206230ustar00rootroot00000000000000set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(stringify stringify.c) set(MWRAP_SUPPORT_HEADER ${CMAKE_CURRENT_BINARY_DIR}/mwrap-support.h) add_custom_command( OUTPUT ${MWRAP_SUPPORT_HEADER} COMMAND ${CMAKE_COMMAND} -DSTRINGIFY=$ -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/mwrap-support.c -DOUTPUT=${MWRAP_SUPPORT_HEADER} -DNAME=mex_header -P ${PROJECT_SOURCE_DIR}/cmake/run_stringify.cmake DEPENDS stringify ${CMAKE_CURRENT_SOURCE_DIR}/mwrap-support.c COMMENT "Generating mwrap-support.h" VERBATIM ) set_source_files_properties(${MWRAP_SUPPORT_HEADER} PROPERTIES GENERATED TRUE) add_custom_target(mwrap_support_header DEPENDS ${MWRAP_SUPPORT_HEADER}) set(MWRAP_HANDWRITTEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/mwrap-ast.cc ${CMAKE_CURRENT_SOURCE_DIR}/mwrap-typecheck.cc ${CMAKE_CURRENT_SOURCE_DIR}/mwrap-cgen.cc ${CMAKE_CURRENT_SOURCE_DIR}/mwrap-mgen.cc ) set(MWRAP_HANDWRITTEN_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/mwrap-ast.h ) find_package(BISON REQUIRED) find_package(FLEX REQUIRED) if(BISON_VERSION VERSION_GREATER_EQUAL "3.0") set(ERROR_VERBOSE "%define parse.error verbose") else() set(ERROR_VERBOSE "%error-verbose") endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/mwrap.y.in ${CMAKE_CURRENT_BINARY_DIR}/mwrap.y @ONLY ) BISON_TARGET(MwrapParser ${CMAKE_CURRENT_BINARY_DIR}/mwrap.y ${CMAKE_CURRENT_BINARY_DIR}/mwrap.cc DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/mwrap.hh VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/mwrap.output ) FLEX_TARGET(MwrapLexer ${CMAKE_CURRENT_SOURCE_DIR}/mwrap.l ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c ) ADD_FLEX_BISON_DEPENDENCY(MwrapLexer MwrapParser) set(MWRAP_GENERATED_SOURCES ${BISON_MwrapParser_OUTPUT_SOURCE} ${FLEX_MwrapLexer_OUTPUTS} ) set(MWRAP_GENERATED_HEADERS ${BISON_MwrapParser_OUTPUT_HEADER} ) set_source_files_properties(${MWRAP_GENERATED_SOURCES} ${MWRAP_GENERATED_HEADERS} PROPERTIES GENERATED TRUE) add_executable(mwrap ${MWRAP_HANDWRITTEN_SOURCES} ${MWRAP_GENERATED_SOURCES} ${MWRAP_SUPPORT_HEADER} ) add_dependencies(mwrap mwrap_support_header) target_sources(mwrap PRIVATE ${MWRAP_HANDWRITTEN_HEADERS} ${MWRAP_GENERATED_HEADERS}) target_include_directories(mwrap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) install(TARGETS mwrap RUNTIME DESTINATION bin) zgimbutas-mwrap-04cdb66/src/Makefile000066400000000000000000000025461522673526200175250ustar00rootroot00000000000000include ../make.inc # === Primary targets === ../mwrap: mwrap.o lex.yy.o mwrap-ast.o mwrap-typecheck.o \ mwrap-mgen.o mwrap-cgen.o mwrap-ast.h $(CXX) -o ../mwrap mwrap.o mwrap-ast.o \ mwrap-typecheck.o mwrap-mgen.o mwrap-cgen.o \ lex.yy.o mwrap.o: mwrap.cc lex.yy.c mwrap-ast.h mwrap-version.h $(CXX) -c mwrap.cc mwrap.cc mwrap.hh: mwrap.y $(BISON) -d -v mwrap.y -o mwrap.cc ifeq ($(shell bison --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\).*$$/\1/p'), 3) ERROR_VERBOSE = %define parse.error verbose else ERROR_VERBOSE = %error-verbose endif mwrap.y: mwrap.y.in sed -e 's/@ERROR_VERBOSE@/$(ERROR_VERBOSE)/' < mwrap.y.in > mwrap.y lex.yy.o: lex.yy.c mwrap.hh $(CC) -c lex.yy.c lex.yy.c: mwrap.l $(FLEX) mwrap.l mwrap-ast.o: mwrap-ast.cc mwrap-ast.h $(CXX) -c -g mwrap-ast.cc mwrap-typecheck.o: mwrap-typecheck.cc mwrap-ast.h $(CXX) -c -g mwrap-typecheck.cc mwrap-cgen.o: mwrap-cgen.cc mwrap-ast.h mwrap-support.h mwrap-version.h $(CXX) -c -g mwrap-cgen.cc mwrap-mgen.o: mwrap-mgen.cc mwrap-ast.h $(CXX) -c -g mwrap-mgen.cc mwrap-support.h: mwrap-support.c stringify ./stringify mex_header < mwrap-support.c > mwrap-support.h stringify: stringify.c $(CC) -o stringify stringify.c # === Clean-up targets === clean: rm -f mwrap.output rm -f *.o *~ rm -f stringify realclean: clean rm -f mwrap.y lex.yy.c mwrap.cc mwrap.hh mwrap-support.h zgimbutas-mwrap-04cdb66/src/mwrap-ast.cc000066400000000000000000000221321522673526200203000ustar00rootroot00000000000000/* * mwrap-ast.cc * Recursive routines for AST printing, identifier construction, * and destruction. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include #include "mwrap-ast.h" /* -- Add inheritance relationship -- */ /* * We represent inheritance relationships as a list of child classes * associated with each parent. This makes sense when we generate the * type casting routines, since we only allow casts to the parent * types. But it's the opposite of how inheritance relations are * specified syntactically. */ map class_decls; void add_inherits(const char* childname, InheritsDecl* ilist) { for (; ilist; ilist = ilist->next) { InheritsDecl*& children = class_decls[ilist->name]; children = new InheritsDecl(mwrap_strdup(childname), children); } } /* -- Add scalar type data -- */ /* * The names of the numeric scalar types are stored in the set scalar_decls. * We provide a basic list, but the user can specify more -- the only real * constraint is there has to be a meaningful typecast to/from a double. */ set scalar_decls; set cscalar_decls; set zscalar_decls; set mxarray_decls; void init_scalar_types() { const char* scalar_types[] = { "double", "float", "long", "int", "short", "char", "ulong", "uint", "ushort", "uchar", "int32_t", "int64_t", "uint32_t", "uint64_t", "bool", "size_t", "ptrdiff_t", NULL}; for (const char** s = scalar_types; *s; ++s) add_scalar_type(*s); } static char* promote_to(char* name, const char* newtype) { delete[] name; return mwrap_strdup(newtype); } char *promote_int(char* name) { /* Detect C99 types: int32_t, int64_t, uint32_t, uint64_t */ if( strcmp(name,"int32_t") == 0 ) mw_use_int32_t = 1; if( strcmp(name,"int64_t") == 0 ) mw_use_int64_t = 1; if( strcmp(name,"uint32_t") == 0 ) mw_use_uint32_t = 1; if( strcmp(name,"uint64_t") == 0 ) mw_use_uint64_t = 1; if( strcmp(name,"ulong") == 0 ) mw_use_ulong = 1; if( strcmp(name,"uint") == 0 ) mw_use_uint = 1; if( strcmp(name,"ushort") == 0 ) mw_use_ushort = 1; if( strcmp(name,"uchar") == 0 ) mw_use_uchar = 1; /* Promote integers. The replacement string must be allocated with mwrap_strdup (new[]), matching destroy(Var)'s delete[]; the caller's original name string is released here. */ if( mw_promote_int == 1 ) { if( strcmp(name,"uint") == 0 ) mw_use_ulong = 1; if( strcmp(name,"int") == 0 ) return promote_to(name, "long"); if( strcmp(name,"uint") == 0 ) return promote_to(name, "ulong"); } if( mw_promote_int == 2 ) { if( strcmp(name,"uint") == 0 ) mw_use_ulong = 1; if( strcmp(name,"ulong") == 0 ) mw_use_ulong = 1; if( strcmp(name,"int") == 0 ) return promote_to(name, "long"); if( strcmp(name,"long") == 0 ) return promote_to(name, "long"); if( strcmp(name,"uint") == 0 ) return promote_to(name, "ulong"); if( strcmp(name,"ulong") == 0 ) return promote_to(name, "ulong"); } if( mw_promote_int == 3 ) { if( strcmp(name,"int") == 0 ) mw_use_int32_t = 1; if( strcmp(name,"long") == 0 ) mw_use_int64_t = 1; if( strcmp(name,"uint") == 0 ) mw_use_uint32_t = 1; if( strcmp(name,"ulong") == 0 ) mw_use_uint64_t = 1; if( strcmp(name,"int") == 0 ) return promote_to(name, "int32_t"); if( strcmp(name,"long") == 0 ) return promote_to(name, "int32_t"); if( strcmp(name,"uint") == 0 ) return promote_to(name, "uint64_t"); if( strcmp(name,"ulong") == 0 ) return promote_to(name, "uint64_t"); } if( mw_promote_int == 4 ) { if( strcmp(name,"int") == 0 ) mw_use_int64_t = 1; if( strcmp(name,"long") == 0 ) mw_use_int64_t = 1; if( strcmp(name,"uint") == 0 ) mw_use_uint64_t = 1; if( strcmp(name,"ulong") == 0 ) mw_use_uint64_t = 1; if( strcmp(name,"int") == 0 ) return promote_to(name, "int64_t"); if( strcmp(name,"long") == 0 ) return promote_to(name, "int64_t"); if( strcmp(name,"uint") == 0 ) return promote_to(name, "uint64_t"); if( strcmp(name,"ulong") == 0 ) return promote_to(name, "uint64_t"); } return name; } void add_scalar_type(const char* name) { scalar_decls.insert(name); } void add_cscalar_type(const char* name) { cscalar_decls.insert(name); } void add_zscalar_type(const char* name) { zscalar_decls.insert(name); } void add_mxarray_type(const char* name) { mxarray_decls.insert(name); } bool is_scalar_type(const char* name) { return (scalar_decls.find(name) != scalar_decls.end()); } bool is_cscalar_type(const char* name) { return (cscalar_decls.find(name) != cscalar_decls.end()); } bool is_zscalar_type(const char* name) { return (zscalar_decls.find(name) != zscalar_decls.end()); } bool is_mxarray_type(const char* name) { return (mxarray_decls.find(name) != mxarray_decls.end()); } /* -- Print the AST -- */ /* * Print a human-readable translation of the abstract syntax tree to * the output. This is only used for generating comprehensible comments * in the C code. */ void print(FILE* fp, Expr* e) { if (!e) return; fprintf(fp, "%s", e->value); if (e->next) { fprintf(fp, ", "); print(fp, e->next); } } void print(FILE* fp, TypeQual* q) { if (!q) return; if (q->qual == 'a') { fprintf(fp, "["); print(fp, q->args); fprintf(fp, "]"); } else fprintf(fp, "%c", q->qual); } void print_devicespec(FILE* fp, Var* v) { if (v->devicespec == 'g') fprintf(fp, "gpu "); } void print_iospec(FILE* fp, Var* v) { if (v->iospec == 'o') fprintf(fp, "output "); else if (v->iospec == 'b') fprintf(fp, "inout "); } void print_var(FILE* fp, Var* v) { fprintf(fp, "%s", v->basetype); print(fp, v->qual); fprintf(fp, " %s", v->name); } void print_args(FILE* fp, Var* v) { if (!v) return; print_devicespec(fp, v); print_iospec(fp, v); print_var(fp, v); if (v->next) { fprintf(fp, ", "); print_args(fp, v->next); } } void print(FILE* fp, Func* f) { if (!f) return; if (f->ret) { print_var(fp, f->ret); fprintf(fp, " = "); } if (f->thisv) fprintf(fp, "%s->%s.", f->thisv, f->classv); fprintf(fp, "%s(", f->funcv); print_args(fp, f->args); fprintf(fp, ");\n"); } /* -- ID string -- */ /* * The ID string is a compressed text representation of the AST for * a function. We replace all the variable names by the letter 'x' * so that when we have the same function call with different arguments, * we will generate the same signature. */ string id_string(Expr* e) { if (!e) return ""; return "x" + id_string(e->next); } string id_string(TypeQual* q) { if (!q) return ""; if (q->qual == 'a') return "[" + id_string(q->args) + "]"; else return (string() + q->qual); } string id_string(Var* v) { if (!v) return ""; string name; if (v->devicespec == 'c') name += "c "; else if (v->devicespec == 'g') name += "g "; if (v->iospec == 'i') name += "i "; else if (v->iospec == 'o') name += "o "; else name += "io "; /* basetype was already promoted at parse time; promote_int would free and replace a string this Var still owns. */ name += v->basetype; name += id_string(v->qual); if (v->tinfo == VT_const) { name += " "; name += v->name; } if (v->next) { name += ", "; name += id_string(v->next); } return name; } string id_string(Func* f) { if (!f) return ""; string name; if (f->ret) { name += id_string(f->ret); name += " = "; } if (f->thisv) { name += f->thisv; name += "->"; name += f->classv; name += "."; } name += f->funcv; name += "("; name += id_string(f->args); name += ")"; return name; } /* -- Destroy an AST -- */ void destroy(Expr* e) { if (!e) return; destroy(e->next); delete[] e->value; delete e; } void destroy(TypeQual* q) { if (!q) return; destroy(q->args); delete q; } void destroy(Var* v) { if (!v) return; destroy(v->next); destroy(v->qual); delete[] v->basetype; delete[] v->name; delete v; } void destroy(Func* f) { while (f) { Func* oldf = f; f = f->next; destroy(oldf->same_next); destroy(oldf->ret); if (oldf->thisv) delete[] oldf->thisv; if (oldf->classv) delete[] oldf->classv; delete[] oldf->funcv; destroy(oldf->args); delete oldf; } } void destroy(InheritsDecl* i) { if (!i) return; destroy(i->next); delete i; } void destroy_inherits() { map::iterator i = class_decls.begin(); for (; i != class_decls.end(); ++i) { destroy(i->second); i->second = NULL; } } zgimbutas-mwrap-04cdb66/src/mwrap-ast.h000066400000000000000000000117371522673526200201530ustar00rootroot00000000000000/* * mwrap-ast.h * MWrap abstract syntax tree declarations. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #ifndef MWRAP_AST_H #define MWRAP_AST_H #include #include #include #include using std::string; using std::map; using std::set; enum { VT_unk, // Unknown VT_obj, // Object VT_array, // Numeric array (real) VT_carray, // Numeric array (single complex) VT_zarray, // Numeric array (double complex) VT_rarray, // Reference to numeric array VT_scalar, // Numeric scalar (real) VT_cscalar, // Numeric scalar (single complex) VT_zscalar, // Numeric scalar (double complex) VT_string, // Char string VT_mx, // mxArray VT_p_obj, // Pointer to object VT_p_scalar, // Pointer to scalar (real) VT_p_cscalar, // Pointer to scalar (single complex) VT_p_zscalar, // Pointer to scalar (double complex) VT_r_obj, // Reference to object VT_r_scalar, // Reference to scalar (real) VT_r_cscalar, // Reference to scalar (single complex) VT_r_zscalar, // Reference to scalar (double complex) VT_const, // Constant expression }; struct InheritsDecl { InheritsDecl(char* name, InheritsDecl* next) : name(name), next(next) {} char* name; // Name of inherited class InheritsDecl* next; }; struct Expr { Expr(char* value) : value(value), next(NULL), input_label(-1) {} int input_label; // Index of dim variable in input arg list char* value; // Expression to be evaluated to get dimension Expr* next; }; struct TypeQual { TypeQual(char qual, Expr* args) : qual(qual), args(args) {} char qual; // Pointer ('*'), ref ('&'), array ('a'), or nothing (0) Expr* args; // Array / cstring dimension list }; struct Var { Var(char devicespec, char iospec, char* basetype, TypeQual* qual, char* name) : devicespec(devicespec), iospec(iospec), basetype(basetype), qual(qual), tinfo(VT_unk), name(name), next(NULL), input_label(-1), output_label(-1) {} int input_label; // Index in input arg list int output_label; // Index in output arg list char iospec; // Indicate input ('i'), output ('o'), or both ('b') char devicespec; // Indicate cpu ('c'), gpu ('g') char* basetype; // Name of the base type TypeQual* qual; // Type qualifier (pointer, ref, etc) int tinfo; // General type identifier (see VT_* list above) char* name; // MATLAB text for variable name (or value) Var* next; }; struct Func { Func(char* thisv, char* classv, char* funcv, const string& fname, int line) : fname(fname), line(line), fort(false), id(-1), thisv(thisv), classv(classv), funcv(funcv), args(NULL), ret(NULL), next(NULL), same_next(NULL) {} string fname; // Name of file where this function was defined int line; // Number of the line where the function was defined bool fort; // Flag whether this is a FORTRAN function int id; // Identifier (used for numbering stub functions) char* thisv; // This var (only for methods) char* classv; // Class type (only for methods or 'new' calls) char* funcv; // Function or method name Var* args; // Arguments Var* ret; // Return variable Func* next; // Next in ordinary linked list Func* same_next; // Next in list of type-identical calls }; extern "C" char* mwrap_strdup(const char* s); extern map class_decls; void add_inherits(const char* childname, InheritsDecl* ilist); extern set scalar_decls; extern set cscalar_decls; extern set zscalar_decls; extern set mxarray_decls; void init_scalar_types(); char *promote_int(char* name); void add_scalar_type(const char* name); void add_cscalar_type(const char* name); void add_zscalar_type(const char* name); void add_mxarray_type(const char* name); bool is_scalar_type(const char* name); bool is_cscalar_type(const char* name); bool is_zscalar_type(const char* name); bool is_mxarray_type(const char* name); string id_string(Func* f); int typecheck(Func* func, int line); void print(FILE* fp, Func* func); void print_matlab_call(FILE* fp, Func* func, const char* mexfunc); void print_mex_init(FILE* fp); void print_mex_file(FILE* fp, Func* f); void mex_c99_complex(FILE* fp); void mex_cpp_complex(FILE* fp); void destroy(Func* func); void destroy(InheritsDecl* ilist); void destroy_inherits(); extern bool mw_use_gpu; extern bool mw_generate_catch; extern bool mw_use_c99_complex; extern bool mw_use_cpp_complex; extern int mw_promote_int; extern int mw_use_int32_t; extern int mw_use_int64_t; extern int mw_use_uint32_t; extern int mw_use_uint64_t; extern int mw_use_longlong; extern int mw_use_ulonglong; extern int mw_use_ulong; extern int mw_use_uint; extern int mw_use_ushort; extern int mw_use_uchar; #endif /* MWRAP_AST_H */ zgimbutas-mwrap-04cdb66/src/mwrap-cgen.cc000066400000000000000000001564311522673526200204370ustar00rootroot00000000000000/* * mwrap-cgen.cc * Generate C MEX file from MWrap AST. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include #include "mwrap-ast.h" #include "mwrap-support.h" #include "mwrap-version.h" /* -- General utility functions -- */ /* * map dcomplex/fcomplex to cuda complex types */ char *basetype_to_cucomplex(const char* name) { if( strcmp(name,"fcomplex") == 0 ) return strdup("cuFloatComplex"); if( strcmp(name,"dcomplex") == 0 ) return strdup("cuDoubleComplex"); return strdup(name); } /* * map type to mxClassID */ char *basetype_to_mxclassid(const char* name) { if( strcmp(name,"int32_t") == 0 ) return strdup("mxINT32_CLASS"); if( strcmp(name,"int64_t") == 0 ) return strdup("mxINT64_CLASS"); if( strcmp(name,"uint32_t") == 0 ) return strdup("mxUINT32_CLASS"); if( strcmp(name,"uint64_t") == 0 ) return strdup("mxUINT64_CLASS"); if( strcmp(name,"float") == 0 ) return strdup("mxSINGLE_CLASS"); if( strcmp(name,"fcomplex") == 0 ) return strdup("mxSINGLE_CLASS"); if( strcmp(name,"double") == 0 ) return strdup("mxDOUBLE_CLASS"); if( strcmp(name,"dcomplex") == 0 ) return strdup("mxDOUBLE_CLASS"); return strdup("mxVOID_CLASS"); } /* * Check whether a tinfo is an array type */ bool is_array(int tinfo) { return (tinfo == VT_array || tinfo == VT_carray || tinfo == VT_zarray); } /* * Check whether a tinfo is an array type */ bool is_obj(int tinfo) { return (tinfo == VT_obj || tinfo == VT_p_obj || tinfo == VT_r_obj); } /* * A function has a nullable return value if it returns a non-object * type for which a NULL pointer value makes sense. */ bool nullable_return(Func* f) { return (f->ret && (f->ret->tinfo == VT_string || is_array(f->ret->tinfo) || f->ret->tinfo == VT_p_scalar || f->ret->tinfo == VT_p_cscalar || f->ret->tinfo == VT_p_zscalar)); } /* * Get the name of a variable into the provided buffer, which must * have room for at least VNAME_BUFSIZE bytes. */ static const size_t VNAME_BUFSIZE = 128; const char* vname(Var* v, char* buf) { if (v->iospec == 'o') snprintf(buf, VNAME_BUFSIZE, "out%d_", v->output_label); else snprintf(buf, VNAME_BUFSIZE, "in%d_", v->input_label); return buf; } /* * Check whether there are any FORTRAN routines in the list. */ bool has_fortran(Func* f) { for (; f; f=f->next) if (f->fort) return true; return false; } /* * Check whether the specified variable is complex */ bool complex_tinfo(Var* v) { return (v->tinfo == VT_carray || v->tinfo == VT_zarray || v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar); } /* * Count routines in the list. */ int max_routine_id(Func* f) { int maxid = 0; for (; f; f=f->next) if (f->id > maxid) maxid = f->id; return maxid; } void mex_use_gpu(FILE* fp) { fprintf(fp, "#include \n\n"); } /* -- Generate standard complex type definitions -- */ void mex_cpp_complex(FILE* fp) { fprintf(fp, "#include \n" "\n" "typedef std::complex dcomplex;\n" "#define real_dcomplex(z) std::real(z)\n" "#define imag_dcomplex(z) std::imag(z)\n" "#define setz_dcomplex(z,r,i) *z = dcomplex(r,i)\n" "\n" "typedef std::complex fcomplex;\n" "#define real_fcomplex(z) std::real(z)\n" "#define imag_fcomplex(z) std::imag(z)\n" "#define setz_fcomplex(z,r,i) *z = fcomplex(r,i)\n\n"); } void mex_gpucpp_complex(FILE* fp) { fprintf(fp,"#include \n\n"); } void mex_c99_complex(FILE* fp) { fprintf(fp, "#include \n" "\n" "typedef _Complex double dcomplex;\n" "#define real_dcomplex(z) creal(z)\n" "#define imag_dcomplex(z) cimag(z)\n" "#define setz_dcomplex(z,r,i) *z = r + i*_Complex_I\n" "\n" "typedef _Complex float fcomplex;\n" "#define real_fcomplex(z) crealf(z)\n" "#define imag_fcomplex(z) cimagf(z)\n" "#define setz_fcomplex(z,r,i) *z = r + i*_Complex_I\n\n"); } /* -- Generate scalar conversion info -- */ /* * For each scalar type T, we define methods to convert a double array * to/from a T array, and to handle return of a pointer to a single T * (which might be NULL). */ void mex_define_copiers(FILE* fp, const char* name) { /* Skip C99 int32_t, int64_t, uint32_t, uint64_t, if not detected */ if( (strcmp(name, "int32_t") == 0 ) && ( mw_use_int32_t == 0 ) ) return; if( (strcmp(name, "int64_t") == 0 ) && ( mw_use_int64_t == 0 ) ) return; if( (strcmp(name, "uint32_t") == 0 ) && ( mw_use_uint32_t == 0 ) ) return; if( (strcmp(name, "uint64_t") == 0 ) && ( mw_use_uint64_t == 0 ) ) return; if( (strcmp(name, "ulong") == 0 ) && ( mw_use_ulong == 0 ) ) return; if( (strcmp(name, "uint") == 0 ) && ( mw_use_uint == 0 ) ) return; if( (strcmp(name, "ushort") == 0 ) && ( mw_use_ushort == 0 ) ) return; if( (strcmp(name, "uchar") == 0 ) && ( mw_use_uchar == 0 ) ) return; /* Define copiers */ fprintf(fp, "mxWrapGetArrayDef(mxWrapGetArray_%s, %s)\n", name, name); fprintf(fp, "mxWrapCopyDef (mxWrapCopy_%s, %s)\n", name, name); fprintf(fp, "mxWrapReturnDef (mxWrapReturn_%s, %s)\n", name, name); /* Single precision */ fprintf(fp, "mxWrapGetArrayDef_single(mxWrapGetArray_single_%s, %s)\n", name, name); fprintf(fp, "mxWrapCopyDef_single (mxWrapCopy_single_%s, %s)\n", name, name); fprintf(fp, "mxWrapReturnDef_single (mxWrapReturn_single_%s, %s)\n", name, name); } void mex_define_zcopiers(FILE* fp, const char* name, const char* ztype) { /* Define complex copiers */ fprintf(fp, "mxWrapGetScalarZDef(mxWrapGetScalar_%s, %s,\n" " %s, setz_%s)\n", name, name, ztype, name); fprintf(fp, "mxWrapGetArrayZDef (mxWrapGetArray_%s, %s,\n" " %s, setz_%s)\n", name, name, ztype, name); fprintf(fp, "mxWrapCopyZDef (mxWrapCopy_%s, %s,\n" " real_%s, imag_%s)\n", name, name, name, name); fprintf(fp, "mxWrapReturnZDef (mxWrapReturn_%s, %s,\n" " real_%s, imag_%s)\n", name, name, name, name); /* Single precision */ fprintf(fp, "mxWrapGetScalarZDef_single(mxWrapGetScalar_single_%s, %s,\n" " %s, setz_%s)\n", name, name, ztype, name); fprintf(fp, "mxWrapGetArrayZDef_single (mxWrapGetArray_single_%s, %s,\n" " %s, setz_%s)\n", name, name, ztype, name); fprintf(fp, "mxWrapCopyZDef_single (mxWrapCopy_single_%s, %s,\n" " real_%s, imag_%s)\n", name, name, name, name); fprintf(fp, "mxWrapReturnZDef_single (mxWrapReturn_single_%s, %s,\n" " real_%s, imag_%s)\n", name, name, name, name); } void mex_define_copiers(FILE* fp) { fprintf(fp, "\n\n\n"); fprintf(fp, "/* Array copier definitions */\n"); for( set::iterator iter = scalar_decls.begin(); iter != scalar_decls.end(); ++iter){ mex_define_copiers(fp, iter->c_str()); } for( set::iterator iter = cscalar_decls.begin(); iter != cscalar_decls.end(); ++iter) mex_define_zcopiers(fp, iter->c_str(), "float"); for( set::iterator iter = zscalar_decls.begin(); iter != zscalar_decls.end(); ++iter) mex_define_zcopiers(fp, iter->c_str(), "double"); fprintf(fp, "\n"); } /* -- Handle FORTRAN name mangling -- */ /* * For each FORTRAN function, we define a macro version of the name * starting with MWF77_, which will handle the various name-mangling * issues that come up. We support three name mangling conventions for * now: all upper (no underscore mangling); all lower, single underscore; * and all lower, single underscore, double underscore for names that * contain an underscore (f2c). */ void mex_define_caps_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); for (char* s = f->funcv; *s; ++s) fputc(toupper(*s), fp); fprintf(fp, "\n"); } void mex_define_caps_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_caps_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_underscore1_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); for (char* s = f->funcv; *s; ++s) fputc(tolower(*s), fp); fprintf(fp, "_\n"); } void mex_define_underscore1_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_underscore1_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_underscore0_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); for (char* s = f->funcv; *s; ++s) fputc(tolower(*s), fp); fprintf(fp, "\n"); } void mex_define_underscore0_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_underscore0_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_f2c_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); bool has_underscore = false; for (char* s = f->funcv; *s; ++s) { has_underscore = (has_underscore || (*s == '_')); fputc(tolower(*s), fp); } if (has_underscore) fprintf(fp, "__\n"); else fprintf(fp, "_\n"); } void mex_define_f2c_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_f2c_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_fnames(FILE* fp, Func* f) { fprintf(fp, "#if defined(MWF77_CAPS)\n"); mex_define_caps_fnames(fp, f); fprintf(fp, "#elif defined(MWF77_UNDERSCORE1)\n"); mex_define_underscore1_fnames(fp, f); fprintf(fp, "#elif defined(MWF77_UNDERSCORE0)\n"); mex_define_underscore0_fnames(fp, f); fprintf(fp, "#else /* f2c convention */\n"); mex_define_f2c_fnames(fp, f); fprintf(fp, "#endif\n\n"); } /* -- Handle FORTRAN declarations -- */ /* * We assume unless told otherwise that all FORTRAN functions need * prototypes, which we will generate. */ void mex_fortran_arg(FILE* fp, Var* v) { if (!v) return; if (v->tinfo == VT_mx) fprintf(fp, "const mxArray*"); else fprintf(fp, "%s*", v->basetype); if (v->next) { fprintf(fp, ", "); mex_fortran_arg(fp, v->next); } } void mex_fortran_decl(FILE* fp, Func* f) { if (f->ret) fprintf(fp, "%s ", f->ret->basetype); else fprintf(fp, "MWF77_RETURN "); fprintf(fp, "MWF77_%s(", f->funcv); mex_fortran_arg(fp, f->args); fprintf(fp, ");\n"); } void mex_fortran_decls(FILE* fp, Func* f) { fprintf(fp, "#ifdef __cplusplus\n" "extern \"C\" { /* Prevent C++ name mangling */\n" "#endif\n\n" "#ifndef MWF77_RETURN\n" "#define MWF77_RETURN int\n" "#endif\n\n"); set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_fortran_decl(fp, f); fnames.insert(f->funcv); } } fprintf(fp, "\n#ifdef __cplusplus\n" "} /* end extern C */\n" "#endif\n\n"); } /* -- Generate class conversion info -- */ /* * For every parent class, we define a getter method that can take a * string of the form "T:value" and interpret the value as a pointer * to T for every child class T. We want to get an *exact* match with * the child pointer type and then cast that pointer to the parent type * in the C++ code, because otherwise we don't get the right behavior * with multiple inheritance (i.e. when a cast actually changes the pointer * value). */ void mex_casting_getter_type(FILE* fp, const char* name) { fprintf(fp, " %s* p_%s = NULL;\n" " sscanf(pbuf, \"%s:%%p\", &p_%s);\n" " if (p_%s)\n" " return p_%s;\n\n", name, name, name, name, name, name); } void mex_casting_getter(FILE* fp, const char* cname, InheritsDecl* inherits) { fprintf(fp, "\n%s* mxWrapGetP_%s(const mxArray* a, const char** e)\n", cname, cname); fprintf(fp, "{\n" " char pbuf[128];\n" " if (mxGetClassID(a) == mxDOUBLE_CLASS &&\n" " mxGetM(a)*mxGetN(a) == 1 &&\n" "#if MX_HAS_INTERLEAVED_COMPLEX\n" " ((mxIsComplex(a) ? ((*mxGetComplexDoubles(a)).real == 0 && (*mxGetComplexDoubles(a)).imag == 0) : *mxGetDoubles(a) == 0))\n" "#else\n" " *mxGetPr(a) == 0\n" "#endif\n" " )\n" " return NULL;\n" " if (!mxIsChar(a)) {\n" "#ifdef R2008OO\n" " mxArray* ap = mxGetProperty(a, 0, \"mwptr\");\n" " if (ap)\n" " return mxWrapGetP_%s(ap, e);\n" "#endif\n" " *e = \"Invalid pointer\";\n" " return NULL;\n" " }\n" " mxGetString(a, pbuf, sizeof(pbuf));\n\n", cname); mex_casting_getter_type(fp, cname); for (InheritsDecl* i = inherits; i; i = i->next) mex_casting_getter_type(fp, i->name); fprintf(fp, " *e = \"Invalid pointer to %s\";\n" " return NULL;\n" "}\n\n", cname); } void mex_casting_getters(FILE* fp) { map::iterator i = class_decls.begin(); for (; i != class_decls.end(); ++i) mex_casting_getter(fp, i->first.c_str(), i->second); } void mex_cast_get_p(FILE* fp, const char* basetype, int input_label) { fprintf(fp, " in%d_ = ", input_label); if (is_mxarray_type(basetype)) fprintf(fp, "mxWrapGet_%s(prhs[%d], &mw_err_txt_);\n", basetype, input_label); else if (class_decls.find(basetype) == class_decls.end()) fprintf(fp, "(%s*) mxWrapGetP(prhs[%d], \"%s:%%p\", &mw_err_txt_);\n", basetype, input_label, basetype); else fprintf(fp, "mxWrapGetP_%s(prhs[%d], &mw_err_txt_);\n", basetype, input_label); fprintf(fp, " if (mw_err_txt_)\n" " goto mw_err_label;\n\n"); } /* -- Mex stub variable declarations -- */ /* * In each stub function, we declare local variables corresponding to * arguments (input and output), return values, and dimension parameters. * We use the names inX_, outX_, and dimX_ to represent input/inout, output * and dimension parameters, where X is the index in the prhs array or * plhs array. */ string mex_declare_type(Var* v) { if (is_obj(v->tinfo) || is_array(v->tinfo)){ if(v->devicespec == 'g'){ return string(basetype_to_cucomplex(v->basetype)) + "*"; } else{ return string(v->basetype) + "*"; } } else if (v->tinfo == VT_rarray){ if(v->devicespec == 'g'){ return string("const ") + basetype_to_cucomplex(v->basetype) + "*"; } else{ return string("const ") + v->basetype + "*"; } } else if (v->tinfo == VT_scalar || v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_scalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_scalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) return string(v->basetype); else if (v->tinfo == VT_string) return string("char*"); else if (v->tinfo == VT_mx && v->iospec == 'i') return string("const mxArray*"); else if (v->tinfo == VT_mx && v->iospec == 'o') return string("mxArray*"); else { fprintf(stderr, "v->tinfo == %d; v->name = %s\n", v->tinfo, v->name); assert(0); return string(); } } void mex_declare_in_args(FILE* fp, Var* v) { if (!v) return; if (v->iospec != 'o' && v->tinfo != VT_const) { string typebuf = mex_declare_type(v); if (is_array(v->tinfo) || is_obj(v->tinfo) || v->tinfo == VT_string) { fprintf(fp, " %-10s in%d_ =0; /* %-10s */\n", typebuf.c_str(), v->input_label, v->name); if(v->devicespec == 'g'){ fprintf(fp, " %-10s *mxGPUArray_in%d_ =0; /* %-10s */\n", "mxGPUArray const", v->input_label, v->name); } } else { fprintf(fp, " %-10s in%d_; /* %-10s */\n", typebuf.c_str(), v->input_label, v->name); } } mex_declare_in_args(fp, v->next); } void mex_declare_out_args(FILE* fp, Var* v) { if (!v) return; if (v->iospec == 'o' && v->tinfo != VT_mx) { string typebuf = mex_declare_type(v); if (is_array(v->tinfo) || is_obj(v->tinfo) || v->tinfo == VT_string) { fprintf(fp, " %-10s out%d_=0; /* %-10s */\n", typebuf.c_str(), v->output_label, v->name); if(v->devicespec == 'g'){ fprintf(fp, " %-10s *mxGPUArray_out%d_ =0; /* %-10s */\n", "mxGPUArray", v->output_label, v->name); fprintf(fp, " %-10s gpu_outdims%d_[2] = {0,0}; /* %-10s dims*/\n", "mwSize", v->output_label, v->name); } } else { fprintf(fp, " %-10s out%d_; /* %-10s */\n", typebuf.c_str(), v->output_label, v->name); } } mex_declare_out_args(fp, v->next); } void mex_declare_dim_args(FILE* fp, Expr* e) { if (!e) return; fprintf(fp, " %-10s dim%d_; /* %-10s */\n", "mwSize", e->input_label, e->value); mex_declare_dim_args(fp, e->next); } void mex_declare_dim_args(FILE* fp, Var* v) { if (!v) return; if (v->qual) mex_declare_dim_args(fp, v->qual->args); mex_declare_dim_args(fp, v->next); } void mex_declare_return(FILE* fp, Var* v) { mex_declare_out_args(fp, v); } void mex_declare_args(FILE* fp, Func* f) { if (f->thisv) { string typebuf = string(f->classv) + "*"; fprintf(fp, " %-10s in%d_ =0; /* %-10s */\n", typebuf.c_str(), 0, f->thisv); } mex_declare_in_args(fp, f->args); if (!nullable_return(f)) mex_declare_return(fp, f->ret); mex_declare_out_args(fp, f->args); mex_declare_dim_args(fp, f->ret); mex_declare_dim_args(fp, f->args); if (f->ret || f->args || f->thisv) fprintf(fp, "\n"); } /* -- Mex stub dimension retrieval -- */ /* * After declaring local variables, we copy in the dimension arguments. */ int mex_unpack_dims(FILE* fp, Expr* e) { if (!e) return 0; fprintf(fp, " dim%d_ = (mwSize) mxWrapGetScalar(prhs[%d], &mw_err_txt_);\n", e->input_label, e->input_label); return mex_unpack_dims(fp, e->next)+1; } int mex_unpack_dims(FILE* fp, Var* v) { if (!v) return 0; if (v->qual) return mex_unpack_dims(fp, v->qual->args) + mex_unpack_dims(fp, v->next); return mex_unpack_dims(fp, v->next); } void mex_unpack_dims(FILE* fp, Func* f) { int count = mex_unpack_dims(fp, f->ret); count += mex_unpack_dims(fp, f->args); if (count) fprintf(fp, "\n"); } /* -- Mex stub dimension checks -- */ /* * For input and inout arrays where the dimensions are given explicitly * in the call, we check that the dimensions of the MATLAB array passed * in agree with the dimensions in the dimension arguments. */ void mex_check_dims(FILE* fp, Var* v) { if (!v) return; if (v->iospec != 'o' && is_array(v->tinfo) && v->qual && v->qual->args && v->devicespec != 'g') { Expr* a = v->qual->args; if (a->next) { fprintf(fp, " if (mxGetM(prhs[%d]) != dim%d_ ||\n" " mxGetN(prhs[%d]) != dim%d_) {\n" " mw_err_txt_ = \"Bad argument size: %s\";\n" " goto mw_err_label;\n" " }\n\n", v->input_label, a->input_label, v->input_label, a->next->input_label, v->name); } else { fprintf(fp, " if (mxGetM(prhs[%d])*mxGetN(prhs[%d]) != dim%d_) {\n" " mw_err_txt_ = \"Bad argument size: %s\";\n" " goto mw_err_label;\n" " }\n\n", v->input_label, v->input_label, a->input_label, v->name); } } mex_check_dims(fp, v->next); } void mex_check_dims(FILE* fp, Func* f) { if (!fp) return; mex_check_dims(fp, f->args); } /* -- Output size data -- */ /* * Generate code to compute the size of an array from dim arguments. */ void mex_alloc_size1(FILE* fp, Expr* e) { if (!e) return; fprintf(fp, "dim%d_", e->input_label); if (e->next) { fprintf(fp, "*"); mex_alloc_size1(fp, e->next); } } void mex_alloc_size(FILE* fp, Expr* e) { if (!e) fprintf(fp, "1"); else mex_alloc_size1(fp, e); } /* -- Unpack input data -- */ /* * Generate code to copy data from the prhs array into the inX_ * local variables in a stub function. Note that for input strings, * we take any explicit dimension information as what the user wants -- * if the string is too long to fit in that explicit dimension, our * generated code will return with an error message. */ void mex_unpack_input_array(FILE* fp, Var* v) { if (v->devicespec != 'g'){ fprintf(fp, " if (mxGetM(prhs[%d])*mxGetN(prhs[%d]) != 0) {\n", v->input_label, v->input_label); if (strcmp(v->basetype, "fcomplex") == 0) fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxSINGLE_CLASS )\n" " mw_err_txt_ = \"Invalid array argument, mxSINGLE_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = mxWrapGetArray_single_%s(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->input_label, v->basetype, v->input_label); else if (strcmp(v->basetype, "dcomplex") == 0) fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxDOUBLE_CLASS )\n" " mw_err_txt_ = \"Invalid array argument, mxDOUBLE_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = mxWrapGetArray_%s(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->input_label, v->basetype, v->input_label); else if (strcmp(v->basetype, "float") == 0) if (v->iospec == 'i') fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxSINGLE_CLASS )\n" " mw_err_txt_ = \"Invalid array argument, mxSINGLE_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" "#if MX_HAS_INTERLEAVED_COMPLEX\n" " if( mxIsComplex(prhs[%d]) )\n" " mw_err_txt_ = \"Invalid array argument, real data expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = mxGetSingles(prhs[%d]);\n" "#else\n" " in%d_ = (float*) mxGetData(prhs[%d]);\n" "#endif\n", v->input_label, v->input_label, v->input_label, v->input_label, v->input_label, v->input_label); else fprintf(fp, " in%d_ = mxWrapGetArray_single_%s(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->basetype, v->input_label); else if (strcmp(v->basetype, "double") == 0) if (v->iospec == 'i'){ fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxDOUBLE_CLASS )\n" " mw_err_txt_ = \"Invalid array argument, mxDOUBLE_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" "#if MX_HAS_INTERLEAVED_COMPLEX\n" " if( mxIsComplex(prhs[%d]) )\n" " mw_err_txt_ = \"Invalid array argument, real data expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = mxGetDoubles(prhs[%d]);\n" "#else\n" " in%d_ = mxGetPr(prhs[%d]);\n" "#endif\n", v->input_label, v->input_label, v->input_label, v->input_label, v->input_label, v->input_label); } else fprintf(fp, " in%d_ = mxWrapGetArray_%s(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->basetype, v->input_label); else fprintf(fp, " in%d_ = mxWrapGetArray_%s(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->basetype, v->input_label); fprintf(fp, " } else\n" " in%d_ = NULL;\n", v->input_label); fprintf(fp, "\n"); } if (v->devicespec == 'g'){ if (v->iospec == 'i' || v->iospec == 'b'){ fprintf(fp, " // extract input GPU array pointer\n" " if(!(mxIsGPUArray(prhs[%d])))\n" " mw_err_txt_ = \"Invalid array argument, gpuArray expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " mxGPUArray_in%d_ = mxGPUCreateFromMxArray(prhs[%d]);\n" " in%d_ = (%s *)mxGPUGetDataReadOnly(mxGPUArray_in%d_);\n\n", v->input_label, v->input_label, v->input_label, v->input_label, basetype_to_cucomplex(v->basetype), v->input_label); } } } void mex_unpack_input_string(FILE* fp, Var* v) { if (!(v->qual && v->qual->args)) fprintf(fp, " in%d_ = mxWrapGetString(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->input_label); else { fprintf(fp, " in%d_ = (char*) mxMalloc(", v->input_label); mex_alloc_size(fp, v->qual->args); fprintf(fp, "*sizeof(char));\n"); fprintf(fp, " if (mxGetString(prhs[%d], in%d_, ", v->input_label, v->input_label); mex_alloc_size(fp, v->qual->args); fprintf(fp, ") != 0) {\n" " mw_err_txt_ = \"Invalid string argument\";\n" " goto mw_err_label;\n" " }\n"); } fprintf(fp, "\n"); } void mex_unpack_inputs(FILE* fp, Var* v) { if (!v) return; if (v->iospec == 'o') { mex_unpack_inputs(fp, v->next); return; } if (is_obj(v->tinfo)) mex_cast_get_p(fp, v->basetype, v->input_label); else if (is_array(v->tinfo)) mex_unpack_input_array(fp, v); else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar || v->tinfo == VT_p_scalar) { if ( strcmp(v->basetype,"float") == 0 ) fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxSINGLE_CLASS )\n" " mw_err_txt_ = \"Invalid scalar argument, mxSINGLE_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = (%s) mxWrapGetScalar_single(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n\n", v->input_label, v->input_label, v->basetype, v->input_label); else if ( strcmp(v->basetype,"char") == 0 ) fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxCHAR_CLASS )\n" " mw_err_txt_ = \"Invalid scalar argument, mxCHAR_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = (%s) mxWrapGetScalar_char(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->input_label, v->basetype, v->input_label); else fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxDOUBLE_CLASS )\n" " mw_err_txt_ = \"Invalid scalar argument, mxDOUBLE_CLASS expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n" " in%d_ = (%s) mxWrapGetScalar(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n\n", v->input_label, v->input_label, v->basetype, v->input_label); } else if (v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) { if ( strcmp(v->basetype,"fcomplex") == 0 ) { fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxSINGLE_CLASS )\n" " mw_err_txt_ = \"Invalid scalar argument, mxSINGLE_CLASS expected\";\n" " if( mxGetM(prhs[%d])*mxGetN(prhs[%d]) != 1 )\n" " mw_err_txt_ = \"Invalid scalar argument, scalar expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n", v->input_label, v->input_label, v->input_label); fprintf(fp, " mxWrapGetScalar_single_%s(&in%d_, prhs[%d]);\n\n", v->basetype, v->input_label, v->input_label); } else { fprintf(fp, " if( mxGetClassID(prhs[%d]) != mxDOUBLE_CLASS )\n" " mw_err_txt_ = \"Invalid scalar argument, mxDOUBLE_CLASS expected\";\n" " if( mxGetM(prhs[%d])*mxGetN(prhs[%d]) != 1 )\n" " mw_err_txt_ = \"Invalid scalar argument, scalar expected\";\n" " if (mw_err_txt_) goto mw_err_label;\n", v->input_label, v->input_label, v->input_label); fprintf(fp, " mxWrapGetScalar_%s(&in%d_, prhs[%d]);\n\n", v->basetype, v->input_label, v->input_label); } } else if (v->tinfo == VT_string) mex_unpack_input_string(fp, v); else if (v->tinfo == VT_mx) fprintf(fp, " in%d_ = prhs[%d];\n\n", v->input_label, v->input_label); mex_unpack_inputs(fp, v->next); } void mex_unpack_inputs(FILE* fp, Func* f) { if (f->thisv) mex_cast_get_p(fp, f->classv, 0); mex_unpack_inputs(fp, f->args); } /* -- Check input data -- */ /* * If an input variable is declared to be an object or a reference to * an object, we check to make sure the pointer passed in is not NULL. */ void mex_check_inputs(FILE* fp, Var* v) { if (!v) return; if (v->iospec != 'o' && (v->tinfo == VT_obj || v->tinfo == VT_r_obj)) fprintf(fp, " if (!in%d_) {\n" " mw_err_txt_ = \"Argument %s cannot be null\";\n" " goto mw_err_label;\n" " }\n", v->input_label, v->name); mex_check_inputs(fp, v->next); } void mex_check_inputs(FILE* fp, Func* f) { mex_check_inputs(fp, f->args); } /* -- Allocate output data -- */ /* * When we allocate data for output variables, we only allocate data * for *pure* output variables. For input/output variables, we'll use * the scratch copy of the input as the scratch copy of the output, too. */ void mex_alloc_output(FILE* fp, Var* v, bool return_flag) { if (!v) return; if (v->iospec == 'o') { if (v->devicespec != 'g'){ if (!return_flag && is_obj(v->tinfo) && is_mxarray_type(v->basetype)) { fprintf(fp, " out%d_ = mxWrapAlloc_%s();\n", v->output_label, v->basetype); } else if (is_array(v->tinfo)) { fprintf(fp, " out%d_ = (%s*) mxMalloc(", v->output_label, v->basetype); mex_alloc_size(fp, v->qual->args); fprintf(fp, "*sizeof(%s));\n", v->basetype); } else if (v->tinfo == VT_rarray) { fprintf(fp, " out%d_ = (%s*) NULL;\n", v->output_label, v->basetype); } else if (v->tinfo == VT_string) { fprintf(fp, " out%d_ = (char*) mxMalloc(", v->output_label); mex_alloc_size(fp, v->qual->args); fprintf(fp, "*sizeof(char));\n"); } } if (v->devicespec == 'g') { // need to allocate Expr* e = v->qual->args; int ndims = 0; if (e && e->next && !e->next->next) { ndims = 2; } else { ndims = 1; } const char* mtype = complex_tinfo(v) ? "mxCOMPLEX" : "mxREAL"; const char* mxclassid = basetype_to_mxclassid(v->basetype); if (ndims == 2) { fprintf(fp, " gpu_outdims%d_[0] = dim%d_; gpu_outdims%d_[1] = dim%d_;\n", v->output_label, e->input_label, v->output_label, e->next->input_label); } if (ndims == 1) { fprintf(fp, " gpu_outdims%d_[0] = dim%d_;\n", v->output_label, e->input_label); } fprintf(fp, " mxGPUArray_out%d_ = mxGPUCreateGPUArray(%d, gpu_outdims%d_, %s, %s, MX_GPU_DO_NOT_INITIALIZE);\n", v->output_label, ndims, v->output_label, mxclassid, mtype); fprintf(fp, " out%d_ = (%s *)mxGPUGetData(mxGPUArray_out%d_);\n\n", v->output_label, basetype_to_cucomplex(v->basetype), v->output_label); } } mex_alloc_output(fp, v->next, return_flag); } void mex_alloc_output(FILE* fp, Func* f) { if (!nullable_return(f)) mex_alloc_output(fp, f->ret, true); mex_alloc_output(fp, f->args, false); } /* -- Record that this routine was called -- */ /* * For coverage testing, we keep a counter of the number of times each * stub is called. */ void mex_record_call(FILE* fp, Func* f) { fprintf(fp, " if (mexprofrecord_)\n" " mexprofrecord_[%d]++;\n", f->id); } /* -- Make the call -- */ /* * The only non-obvious aspect of the call line is the way that return * values are treated. The biggest issue is that functions can return * NULL, and we need to present that to the user in some reasonable * way that won't be confused with an ordinary return -- this is why * we use 0 to represent a null return for a string or an object, but * we use an empty array to represent a null return for a numeric * object. A secondary issue has to do with strings -- we won't know the * size of a return string until after we have it in hand. Both because * of the possibility of NULL returns and because of the indeterminate size * of strings, we allocate space to hold return values *with* the call, * rather than setting up space before the call. */ void mex_make_call(FILE* fp, Var* v, int first) { if (!v) return; if (!first) fprintf(fp, ", "); char namebuf[VNAME_BUFSIZE]; if (v->tinfo == VT_obj || v->tinfo == VT_r_obj) fprintf(fp, "*%s", vname(v, namebuf)); else if (v->tinfo == VT_mx && v->iospec == 'o') fprintf(fp, "plhs+%d", v->output_label); else if (v->tinfo == VT_p_scalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) fprintf(fp, "&%s", vname(v, namebuf)); else if (v->tinfo == VT_const) fprintf(fp, "%s", v->name); else fprintf(fp, "%s", vname(v, namebuf)); mex_make_call(fp, v->next, 0); } void mex_make_call(FILE* fp, Func* f) { if (f->thisv) fprintf(fp, "in0_->"); if (strcmp(f->funcv, "new") == 0) fprintf(fp, "new %s(", f->classv); else { if (f->fort) fprintf(fp, "MWF77_"); fprintf(fp, "%s(", f->funcv); } mex_make_call(fp, f->args, 1); fprintf(fp, ")"); } void mex_make_stmt(FILE* fp, Func* f) { if (!f) return; if (f->thisv) fprintf(fp, " if (!in0_) {\n" " mw_err_txt_ = \"Cannot dispatch to NULL\";\n" " goto mw_err_label;\n" " }\n"); if (mw_generate_catch) fprintf(fp, " try {\n "); if (f->ret) { Var* v = f->ret; if (v->tinfo == VT_obj) { if (is_mxarray_type(v->basetype)) { fprintf(fp, " plhs[0] = mxWrapSet_%s(&(", v->basetype); mex_make_call(fp, f); fprintf(fp ,"));\n"); } else { fprintf(fp, " out0_ = new %s(", v->basetype); mex_make_call(fp, f); fprintf(fp ,");\n"); } } else if (is_array(v->tinfo)) { fprintf(fp, " plhs[0] = mxWrapReturn_%s(", v->basetype); mex_make_call(fp, f); fprintf(fp, ", "); Expr* e = v->qual->args; if (e && e->next && !e->next->next) { fprintf(fp, " dim%d_, dim%d_);\n", e->input_label, e->next->input_label); } else { mex_alloc_size(fp, v->qual->args); fprintf(fp, ", 1);\n"); } } else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar || v->tinfo == VT_cscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_zscalar) { fprintf(fp, " out0_ = "); mex_make_call(fp, f); fprintf(fp, ";\n"); } else if (v->tinfo == VT_string) { fprintf(fp, " plhs[0] = mxWrapStrncpy("); mex_make_call(fp, f); fprintf(fp, ");\n"); } else if (v->tinfo == VT_mx) { fprintf(fp, " plhs[0] = "); mex_make_call(fp, f); fprintf(fp, ";\n"); } else if (v->tinfo == VT_p_obj) { if (is_mxarray_type(v->basetype)) { fprintf(fp, " plhs[0] = mxWrapSet_%s(", v->basetype); mex_make_call(fp, f); fprintf(fp, ");\n"); } else { fprintf(fp, " out0_ = "); mex_make_call(fp, f); fprintf(fp, ";\n"); } } else if (v->tinfo == VT_p_scalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) { fprintf(fp, " plhs[0] = mxWrapReturn_%s(", v->basetype); mex_make_call(fp, f); fprintf(fp, ", 1, 1);\n"); } else if (v->tinfo == VT_r_obj) { if (is_mxarray_type(v->basetype)) { fprintf(fp, " plhs[0] = mxWrapSet_%s(&(", v->basetype); mex_make_call(fp, f); fprintf(fp, "));\n"); } else { fprintf(fp, " out0_ = &("); mex_make_call(fp, f); fprintf(fp, ");\n"); } } } else { fprintf(fp, " "); mex_make_call(fp, f); fprintf(fp, ";\n"); } if (mw_generate_catch) fprintf(fp, " } catch(...) {\n" " mw_err_txt_ = \"Caught C++ exception from %s\";\n" " }\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", f->funcv); } /* -- Marshal the results -- */ /* * Copy inout arguments, output arguments, and any simple return * arguments into the plhs array. */ void mex_marshal_array(FILE* fp, Var* v) { if (v->devicespec != 'g'){ Expr* e = v->qual->args; char namebuf[VNAME_BUFSIZE]; const char* mtype = complex_tinfo(v) ? "mxCOMPLEX" : "mxREAL"; const char* ws = " "; if (v->tinfo == VT_rarray) { ws = " "; fprintf(fp, " if (out%d_ == NULL) {\n", v->output_label); fprintf(fp, " plhs[%d] = mxCreateDoubleMatrix(0,0, mxREAL);\n", v->output_label); fprintf(fp, " } else {\n"); } if (!e) { // No dimension info -- must be an inout array if (strcmp(v->basetype, "float") == 0 || strcmp(v->basetype, "fcomplex") == 0) { fprintf(fp, "%splhs[%d] = mxCreateNumericMatrix(" "mxGetM(prhs[%d]), mxGetN(prhs[%d]), mxSINGLE_CLASS, %s);\n", ws, v->output_label, v->input_label, v->input_label, mtype); fprintf(fp, "%smxWrapCopy_single_%s(plhs[%d], in%d_, ", ws, v->basetype, v->output_label, v->input_label); } else { fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(" "mxGetM(prhs[%d]), mxGetN(prhs[%d]), %s);\n", ws, v->output_label, v->input_label, v->input_label, mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], in%d_, ", ws, v->basetype, v->output_label, v->input_label); } fprintf(fp, "mxGetM(prhs[%d])*mxGetN(prhs[%d])", v->input_label, v->input_label); fprintf(fp, ");\n"); } else if (!(e->next)) { // Only size given if (strcmp(v->basetype, "float") == 0 || strcmp(v->basetype, "fcomplex") == 0) { fprintf(fp, "%splhs[%d] = mxCreateNumericMatrix(dim%d_, 1, mxSINGLE_CLASS, %s);\n", ws, v->output_label, e->input_label, mtype); fprintf(fp, "%smxWrapCopy_single_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); } else { fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(dim%d_, 1, %s);\n", ws, v->output_label, e->input_label, mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); } fprintf(fp, "dim%d_", e->input_label); fprintf(fp, ");\n"); } else if (!(e->next->next)) { // Two dimensions given if (strcmp(v->basetype, "float") == 0 || strcmp(v->basetype, "fcomplex") == 0) { fprintf(fp, "%splhs[%d] = mxCreateNumericMatrix(" "dim%d_, dim%d_, mxSINGLE_CLASS, %s);\n", ws, v->output_label, e->input_label, e->next->input_label, mtype); fprintf(fp, "%smxWrapCopy_single_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); } else { fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(" "dim%d_, dim%d_, %s);\n", ws, v->output_label, e->input_label, e->next->input_label, mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); } fprintf(fp, "dim%d_*dim%d_", e->input_label, e->next->input_label); fprintf(fp, ");\n"); } else { // Three or more dimensions given -- punt and make it 1D if (strcmp(v->basetype, "float") == 0 || strcmp(v->basetype, "fcomplex") == 0) { fprintf(fp, "%splhs[%d] = mxCreateNumericMatrix(", ws, v->output_label); mex_alloc_size(fp, e); fprintf(fp, ", 1, mxSINGLE_CLASS, %s);\n", mtype); fprintf(fp, "%smxWrapCopy_single_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); } else { fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(", ws, v->output_label); mex_alloc_size(fp, e); fprintf(fp, ", 1, %s);\n", mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); } mex_alloc_size(fp, e); fprintf(fp, ");\n"); } if (v->tinfo == VT_rarray) { fprintf(fp, " }\n"); } } if (v->devicespec == 'g'){ if (v->iospec == 'b') { fprintf(fp, " plhs[%d] = prhs[%d];\n", v->output_label, v->input_label); } if (v->iospec == 'o') { fprintf(fp, " plhs[%d] = mxGPUCreateMxArrayOnGPU(mxGPUArray_out%d_);\n", v->output_label, v->output_label); } } } void mex_marshal_result(FILE* fp, Var* v, bool return_flag) { char namebuf[VNAME_BUFSIZE]; if (is_obj(v->tinfo) && is_mxarray_type(v->basetype)) { if (!return_flag) fprintf(fp, " plhs[%d] = mxWrapSet_%s(%s);\n", v->output_label, v->basetype, vname(v,namebuf)); } else if (is_obj(v->tinfo)) fprintf(fp, " plhs[%d] = mxWrapCreateP(out%d_, \"%s:%%p\");\n", v->output_label, v->output_label, v->basetype); else if (is_array(v->tinfo) || v->tinfo == VT_rarray) mex_marshal_array(fp, v); else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar || v->tinfo == VT_p_scalar) fprintf(fp, "#if MX_HAS_INTERLEAVED_COMPLEX\n" " plhs[%d] = mxCreateDoubleMatrix(1, 1, mxREAL);\n" " *mxGetDoubles(plhs[%d]) = %s;\n" "#else\n" " plhs[%d] = mxCreateDoubleMatrix(1, 1, mxREAL);\n" " *mxGetPr(plhs[%d]) = %s;\n" "#endif\n", v->output_label, v->output_label, vname(v, namebuf), v->output_label, v->output_label, vname(v, namebuf)); else if (v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) fprintf(fp, "#if MX_HAS_INTERLEAVED_COMPLEX\n" " plhs[%d] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);\n" " mxGetComplexDoubles(plhs[%d])->real = real_%s(%s);\n" " mxGetComplexDoubles(plhs[%d])->imag = imag_%s(%s);\n" "#else\n" " plhs[%d] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);\n" " *mxGetPr(plhs[%d]) = real_%s(%s);\n" " *mxGetPi(plhs[%d]) = imag_%s(%s);\n" "#endif\n", v->output_label, v->output_label, v->basetype, vname(v, namebuf), v->output_label, v->basetype, vname(v, namebuf), v->output_label, v->output_label, v->basetype, vname(v, namebuf), v->output_label, v->basetype, vname(v, namebuf)); else if (v->tinfo == VT_string) fprintf(fp, " plhs[%d] = mxCreateString(%s);\n", v->output_label, vname(v, namebuf)); } void mex_marshal_results(FILE* fp, Var* v, bool return_flag) { if (!v) return; if (v->iospec != 'i') mex_marshal_result(fp, v, return_flag); mex_marshal_results(fp, v->next, return_flag); } void mex_marshal_results(FILE* fp, Func* f) { if (!nullable_return(f)) mex_marshal_results(fp, f->ret, true); mex_marshal_results(fp, f->args, false); } /* -- Deallocate temporary data -- */ /* * Free scratch arrays allocated to hold input copies and output * storage. */ void mex_dealloc(FILE* fp, Var* v, bool return_flag) { if (!v) return; if (v->devicespec != 'g'){ if (is_array(v->tinfo) || v->tinfo == VT_string) { if (v->iospec == 'o') fprintf(fp, " if (out%d_) mxFree(out%d_);\n", v->output_label, v->output_label); else if (v->iospec == 'b' || !(strcmp(v->basetype, "double") == 0 || strcmp(v->basetype, "float") == 0) ) fprintf(fp, " if (in%d_) mxFree(in%d_);\n", v->input_label, v->input_label); } else if (is_obj(v->tinfo) && is_mxarray_type(v->basetype)) { if (v->iospec == 'i' || v->iospec == 'b') fprintf(fp, " if (in%d_) mxWrapFree_%s(in%d_);\n", v->input_label, v->basetype, v->input_label); else if (v->iospec == 'o' && !return_flag) fprintf(fp, " if (out%d_) mxWrapFree_%s(out%d_);\n", v->output_label, v->basetype, v->output_label); } } if (v->devicespec == 'g'){ if (v->iospec == 'i' || v->iospec == 'b'){ fprintf(fp, " if (mxGPUArray_in%d_) mxGPUDestroyGPUArray(mxGPUArray_in%d_);\n", v->input_label, v->input_label); } if (v->iospec == 'o'){ fprintf(fp, " if (mxGPUArray_out%d_) mxGPUDestroyGPUArray(mxGPUArray_out%d_);\n", v->output_label, v->output_label); } } mex_dealloc(fp, v->next, return_flag); } void mex_dealloc(FILE* fp, Func* f) { if (!nullable_return(f)) mex_dealloc(fp, f->ret, true); mex_dealloc(fp, f->args, false); } /* -- Generate mex stub -- */ /* * Print a MEX stub function. We generate one of these for each * distinct call line in the input files. */ void print_c_comment(FILE* fp, Func* f) { fprintf(fp, "/* ---- %s: %d ----\n * ", f->fname.c_str(), f->line); print(fp, f); for (Func* fsame = f->same_next; fsame; fsame = fsame->same_next) fprintf(fp, " * Also at %s: %d\n", fsame->fname.c_str(), fsame->line); fprintf(fp, " */\n"); } void print_mex_stub(FILE* fp, Func* f) { print_c_comment(fp, f); fprintf(fp, "static const char* stubids%d_ = \"%s\";\n\n" "void mexStub%d(int nlhs, mxArray* plhs[],\n" " int nrhs, const mxArray* prhs[])\n" "{\n" " const char* mw_err_txt_ = 0;\n", f->id, id_string(f).c_str(), f->id); mex_declare_args(fp, f); mex_unpack_dims(fp, f); mex_check_dims(fp, f); mex_unpack_inputs(fp, f); mex_check_inputs(fp, f); mex_alloc_output(fp, f); mex_record_call(fp, f); mex_make_stmt(fp, f); mex_marshal_results(fp, f); fprintf(fp, "\nmw_err_label:\n"); mex_dealloc(fp, f); fprintf(fp, " if (mw_err_txt_)\n" " mexErrMsgTxt(mw_err_txt_);\n" "}\n\n"); } /* -- Generate profiler output routine -- */ /* * The profiler / coverage testing routines include starting, * ending, and reporting. Starting the profiler locks the MEX file * in memory. */ void make_profile_output(FILE* fp, Func* f, const char* printfunc) { fprintf(fp, " if (!mexprofrecord_)\n" " %s\"Profiler inactive\\n\");\n" " else {\n", printfunc); for (; f; f = f->next) { fprintf(fp, " %s\"%%d calls to %s:%d", printfunc, f->fname.c_str(), f->line); for (Func* fsame = f->same_next; fsame; fsame = fsame->same_next) fprintf(fp, " (%s:%d)", fsame->fname.c_str(), fsame->line); fprintf(fp, "\\n\", mexprofrecord_[%d]);\n", f->id); } fprintf(fp, " }\n"); } /* -- Generate MEX file -- */ /* * Generate the overall C file to be fed into MEX. This consists of * basic support code (from mwrap-support.c), type conversion * routines, all the call stubs, and a main dispatcher that decides * which stub to call. */ void print_mex_stubs(FILE* fp, Func* f) { for (; f; f = f->next) print_mex_stub(fp, f); } void print_mex_else_cases(FILE* fp, Func* f) { for (Func* fcall = f; fcall; fcall = fcall->next) fprintf(fp, " else if (strcmp(id, stubids%d_) == 0)\n" " mexStub%d(nlhs,plhs, nrhs-1,prhs+1);\n", fcall->id, fcall->id); int maxid = max_routine_id(f); fprintf(fp, " else if (strcmp(id, \"*profile on*\") == 0) {\n" " if (!mexprofrecord_) {\n" " mexprofrecord_ = (int*) malloc(%d * sizeof(int));\n" " mexLock();\n" " }\n" " memset(mexprofrecord_, 0, %d * sizeof(int));\n" " } else if (strcmp(id, \"*profile off*\") == 0) {\n" " if (mexprofrecord_) {\n" " free(mexprofrecord_);\n" " mexUnlock();\n" " }\n" " mexprofrecord_ = NULL;\n" " } else if (strcmp(id, \"*profile report*\") == 0) {\n", maxid+1, maxid+1); make_profile_output(fp, f, "mexPrintf("); fprintf(fp, " } else if (strcmp(id, \"*profile log*\") == 0) {\n" " FILE* logfp;\n" " if (nrhs != 2 || mxGetString(prhs[1], id, sizeof(id)) != 0)\n" " mexErrMsgTxt(\"Must have two string arguments\");\n" " logfp = fopen(id, \"w+\");\n" " if (!logfp)\n" " mexErrMsgTxt(\"Cannot open log for output\");\n"); make_profile_output(fp, f, "fprintf(logfp, "); fprintf(fp, " fclose(logfp);\n"); fprintf(fp, " } else\n" " mexErrMsgTxt(\"Unknown identifier\");\n"); } const char* mwrap_banner = "/* --------------------------------------------------- */\n" "/* Automatically generated by mwrap */\n" "/* --------------------------------------------------- */\n\n"; const char* mexBase = "/* ----\n" " */\n" "void mexFunction(int nlhs, mxArray* plhs[],\n" " int nrhs, const mxArray* prhs[])\n" "{\n" " char id[1024];\n" " if (nrhs == 0) {\n" " mexPrintf(\"Mex function installed\\n\");\n" " return;\n" " }\n\n"; const char* mexBaseIf = " if (mxGetString(prhs[0], id, sizeof(id)) != 0)\n" " mexErrMsgTxt(\"Identifier should be a string\");\n"; const char* mwrap_compat_type_support = "/*\n" " * Compiler compatibility extensions\n" " */\n" "\n" "typedef unsigned long ulong;\n" "typedef unsigned int uint;\n" "typedef unsigned short ushort;\n" "typedef unsigned char uchar;\n" "typedef long long longlong;\n" "typedef unsigned long long ulonglong;\n"; // "#ifndef ulong\n" // "# define ulong unsigned long\n" // "#endif\n" // "#ifndef uint\n" // "# define uint unsigned int\n" // "#endif\n" // "#ifndef ushort\n" // "# define ushort unsigned short\n" // "#endif\n" // "#ifndef uchar\n" // "# define uchar unsigned char\n" // "#endif\n" // "\n" // "#ifndef longlong\n" // "# define longlong long long\n" // "#endif\n" // "#ifndef ulonglong\n" // "# define ulonglong unsigned long long\n" // "#endif\n"; void print_mex_init(FILE* fp) { fprintf(fp, "%s", mwrap_banner); fprintf(fp, "/* Code generated by mwrap %s */\n", MWRAP_VERSION); fprintf(fp, "%s", mex_header); fprintf(fp, "\n"); if (mw_use_gpu) mex_use_gpu(fp); if (mw_use_c99_complex) mex_c99_complex(fp); else if (mw_use_cpp_complex) { mex_cpp_complex(fp); if (mw_use_gpu) mex_gpucpp_complex(fp); } // fprintf(fp, "\n"); // fprintf(fp, "%s", mwrap_compat_type_support); // fprintf(fp, "\n"); } void print_mex_file(FILE* fp, Func* f) { if (mw_use_int32_t || mw_use_int64_t || mw_use_uint32_t || mw_use_uint64_t) fprintf(fp, "#include \n\n"); mex_define_copiers(fp); mex_casting_getters(fp); if (has_fortran(f)) { mex_define_fnames(fp, f); mex_fortran_decls(fp, f); } print_mex_stubs(fp, f); fprintf(fp, "%s", mexBase); fprintf(fp, "\n"); if (mw_use_gpu) fprintf(fp, " mxInitGPU();\n"); fprintf(fp, "\n"); fprintf(fp, "%s", mexBaseIf); print_mex_else_cases(fp, f); fprintf(fp, "}\n\n"); } zgimbutas-mwrap-04cdb66/src/mwrap-mgen.cc000066400000000000000000000042301522673526200204360ustar00rootroot00000000000000/* * mwrap-mgen.cc * Generate MATLAB scripts from MWrap AST. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include #include "mwrap-ast.h" /* -- Output MATLAB call code -- */ /* * Each call is translated to MATLAB code of the form * * mex_id_ = 'identifier from id_string'; * [result1, result2, ...] = mexfunc(mex_id_, in1, ..., dim1, dim2, ...); * * where dim1, dim2, ... are any explicitly provided array dimensions. */ int has_output_args(Var* v) { if (!v) return 0; if (v->iospec == 'o' || v->iospec == 'b') return 1; else return has_output_args(v->next); } void print_output_args(FILE* fp, Var* v, int& first) { if (!v) return; if (v->iospec == 'o' || v->iospec == 'b') { if (!first) fprintf(fp, ", "); fprintf(fp, "%s", v->name); first = 0; } print_output_args(fp, v->next, first); } void print_input_args(FILE* fp, Var* v) { if (!v) return; if (v->tinfo == VT_const) fprintf(fp, ", 0"); else if (v->iospec == 'i' || v->iospec == 'b') fprintf(fp, ", %s", v->name); print_input_args(fp, v->next); } void print_dimension_args(FILE* fp, Expr* e) { if (!e) return; fprintf(fp, ", %s", e->value); print_dimension_args(fp, e->next); } void print_dimension_args(FILE* fp, Var* v) { if (!v) return; if (v->qual) print_dimension_args(fp, v->qual->args); print_dimension_args(fp, v->next); } void print_matlab_call(FILE* fp, Func* f, const char* mexfunc) { fprintf(fp, "mex_id_ = '%s';\n", id_string(f).c_str()); if (f->ret || has_output_args(f->args)) { int first = 1; fprintf(fp, "["); print_output_args(fp, f->ret, first); print_output_args(fp, f->args, first); fprintf(fp, "] = "); } fprintf(fp, "%s(mex_id_", mexfunc); if (f->thisv) fprintf(fp, ", %s", f->thisv); print_input_args(fp, f->args); print_dimension_args(fp, f->ret); print_dimension_args(fp, f->args); fprintf(fp, ");\n"); } zgimbutas-mwrap-04cdb66/src/mwrap-support.c000066400000000000000000000550611522673526200210710ustar00rootroot00000000000000/* Copyright statement for mwrap: mwrap -- MEX file generation for MATLAB and Octave Copyright (c) 2007-2008 David Bindel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. You may distribute a work that contains part or all of the source code generated by mwrap under the terms of your choice. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #if MX_HAS_INTERLEAVED_COMPLEX #include #endif /* * Records for call profile. */ int* mexprofrecord_= NULL; double mxWrapGetScalar_char(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxCHAR_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid char argument"; return 0; } return (char) (*mxGetChars(a)); } /* * Support routines for copying data into and out of the MEX stubs, R2018a */ #if MX_HAS_INTERLEAVED_COMPLEX void* mxWrapGetP(const mxArray* a, const char* fmt, const char** e) { void* p = NULL; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxDOUBLE_CLASS && mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && (*mxGetComplexDoubles(a)).real == 0 ) return NULL; } if (mxGetClassID(a) == mxDOUBLE_CLASS && !mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && *mxGetDoubles(a) == 0) return NULL; } if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetDoubles(z) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetDoubles(z) = 0; return z; } } char* mxWrapGetString(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } double mxWrapGetScalar(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxDOUBLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } if( mxIsComplex(a) ) return (double) (*mxGetComplexDoubles(a)).real; else return (double) (*mxGetDoubles(a)); } #define mxWrapGetArrayDef(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* q; \ mxComplexDouble* z; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*z++).real; \ } \ else \ { \ q = mxGetDoubles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ } \ return array; \ } #define mxWrapCopyDef(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* p; \ mxComplexDouble* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < n; ++i) { \ z[i].real = (double) *q++; \ z[i].imag = 0; \ } \ } \ else \ { \ p = mxGetDoubles(a); \ for (i = 0; i < n; ++i) \ *p++ = (double) *q++; \ } \ } #define mxWrapReturnDef(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ double* p; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxREAL); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxREAL); \ p = mxGetDoubles(a); \ for (i = 0; i < m*n; ++i) \ *p++ = (double) *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ if( mxIsComplex(a) ) \ { \ setz(z, (ZT) (*mxGetComplexDoubles(a)).real, (ZT) (*mxGetComplexDoubles(a)).imag); \ } \ else \ { \ setz(z, (ZT) *mxGetDoubles(a), (ZT) 0); \ } \ } #define mxWrapGetArrayZDef(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* q; \ mxComplexDouble* z; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*z).real, (ZT) (*z).imag); \ ++p; ++z; } \ } \ else \ { \ q = mxGetDoubles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*q), (ZT) 0 ); \ ++p; ++q; } \ } \ return array; \ } #define mxWrapCopyZDef(func, T, freal, fimag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* p; \ mxComplexDouble* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexDoubles(a); \ for (i = 0; i < n; ++i) { \ (*z).real = freal(*q); \ (*z).imag = fimag(*q); \ ++z; ++q; } \ } \ else \ { \ p = mxGetDoubles(a); \ for (i = 0; i < n; ++i) \ *p++ = freal(*q++); \ } \ } #define mxWrapReturnZDef(func, T, freal, fimag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ mxComplexDouble* p; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxCOMPLEX); \ p = mxGetComplexDoubles(a); \ for (i = 0; i < m*n; ++i) { \ (*p).real = freal(*q); \ (*p).imag = fimag(*q); \ ++p; ++q; } \ return a; \ } \ } void* mxWrapGetP_single(const mxArray* a, const char* fmt, const char** e) { void* p = NULL; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxSINGLE_CLASS && mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && (*mxGetComplexSingles(a)).real == 0 ) return NULL; } if (mxGetClassID(a) == mxSINGLE_CLASS && !mxIsComplex(a) ) { if( mxGetM(a)*mxGetN(a) == 1 && *mxGetSingles(a) == 0) return NULL; } if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP_single(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *mxGetSingles(z) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy_single(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *mxGetSingles(z) = 0; return z; } } float mxWrapGetScalar_single(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxSINGLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } if( mxIsComplex(a) ) return (float) (*mxGetComplexSingles(a)).real; else return (float) (*mxGetSingles(a)); } char* mxWrapGetString_single(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } #define mxWrapGetArrayDef_single(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* q; \ mxComplexSingle* z; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*z++).real; \ } \ else \ { \ q = mxGetSingles(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ } \ return array; \ } #define mxWrapCopyDef_single(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* p; \ mxComplexSingle* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < n; ++i) { \ z[i].real = (float) *q++; \ z[i].imag = 0; \ } \ } \ else \ { \ p = mxGetSingles(a); \ for (i = 0; i < n; ++i) \ *p++ = (float) *q++; \ } \ } #define mxWrapReturnDef_single(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ float* p; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxREAL); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxREAL); \ p = mxGetSingles(a); \ for (i = 0; i < m*n; ++i) \ *p++ = (float) *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef_single(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ if( mxIsComplex(a) ) \ { \ setz(z, (ZT) (*mxGetComplexSingles(a)).real, (ZT) (*mxGetComplexSingles(a)).imag); \ } \ else \ { \ setz(z, (ZT) *mxGetSingles(a), (ZT) 0); \ } \ } #define mxWrapGetArrayZDef_single(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* q; \ mxComplexSingle* z; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*z).real, (ZT) (*z).imag); \ ++p; ++z; } \ } \ else \ { \ q = mxGetSingles(a); \ for (i = 0; i < arraylen; ++i) { \ setz(p, (ZT) (*q), (ZT) 0 ); \ ++p; ++q; } \ } \ return array; \ } #define mxWrapCopyZDef_single(func, T, freal, fimag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* p; \ mxComplexSingle* z; \ if( mxIsComplex(a) ) \ { \ z = mxGetComplexSingles(a); \ for (i = 0; i < n; ++i) { \ (*z).real = freal(*q); \ (*z).imag = fimag(*q); \ ++z; ++q; } \ } \ else \ { \ p = mxGetSingles(a); \ for (i = 0; i < n; ++i) \ *p++ = freal(*q++); \ } \ } #define mxWrapReturnZDef_single(func, T, freal, fimag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ mxComplexSingle* p; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxCOMPLEX); \ p = mxGetComplexSingles(a); \ for (i = 0; i < m*n; ++i) { \ (*p).real = freal(*q); \ (*p).imag = fimag(*q); \ ++p; ++q; } \ return a; \ } \ } #else /* * Support routines for copying data into and out of the MEX stubs, -R2017b */ void* mxWrapGetP(const mxArray* a, const char* fmt, const char** e) { void* p = 0; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxDOUBLE_CLASS && mxGetM(a)*mxGetN(a) == 1 && *mxGetPr(a) == 0) return p; if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetPr(z) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateDoubleMatrix(1,1, mxREAL); *mxGetPr(z) = 0; return z; } } double mxWrapGetScalar(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxDOUBLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } return *mxGetPr(a); } char* mxWrapGetString(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } #define mxWrapGetArrayDef(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* q; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ q = mxGetPr(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ return array; \ } #define mxWrapCopyDef(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* p = mxGetPr(a); \ for (i = 0; i < n; ++i) \ *p++ = *q++; \ } #define mxWrapReturnDef(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ double* p; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxREAL); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxREAL); \ p = mxGetPr(a); \ for (i = 0; i < m*n; ++i) \ *p++ = *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ double* pr = mxGetPr(a); \ double* pi = mxGetPi(a); \ setz(z, (ZT) *pr, (pi ? (ZT) *pi : (ZT) 0)); \ } #define mxWrapGetArrayZDef(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ double* qr; \ double* qi; \ if (!a || mxGetClassID(a) != mxDOUBLE_CLASS) { \ *e = "Invalid array argument, mxDOUBLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ qr = mxGetPr(a); \ qi = mxGetPi(a); \ for (i = 0; i < arraylen; ++i) { \ ZT val_qr = *qr++; \ ZT val_qi = (qi ? (ZT) *qi++ : (ZT) 0); \ setz(p, val_qr, val_qi); \ ++p; \ } \ return array; \ } #define mxWrapCopyZDef(func, T, real, imag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ double* pr = mxGetPr(a); \ double* pi = mxGetPi(a); \ for (i = 0; i < n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ } #define mxWrapReturnZDef(func, T, real, imag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ double* pr; \ double* pi; \ if (!q) { \ return mxCreateDoubleMatrix(0,0, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateDoubleMatrix(m,n, mxCOMPLEX); \ pr = mxGetPr(a); \ pi = mxGetPi(a); \ for (i = 0; i < m*n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ return a; \ } \ } void* mxWrapGetP_single(const mxArray* a, const char* fmt, const char** e) { void* p = 0; #ifdef R2008OO mxArray* ap; #endif if (mxGetClassID(a) == mxSINGLE_CLASS && mxGetM(a)*mxGetN(a) == 1 && *((float*)mxGetData(a)) == 0) return p; if (mxIsChar(a)) { char pbuf[128]; mxGetString(a, pbuf, sizeof(pbuf)); sscanf(pbuf, fmt, &p); } #ifdef R2008OO else if (ap = mxGetProperty(a, 0, "mwptr")) { return mxWrapGetP(ap, fmt, e); } #endif if (p == 0) *e = "Invalid pointer"; return p; } mxArray* mxWrapCreateP_single(void* p, const char* fmt) { if (p == 0) { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *((float*)mxGetData(z)) = 0; return z; } else { char pbuf[128]; snprintf(pbuf, sizeof(pbuf), fmt, p); return mxCreateString(pbuf); } } mxArray* mxWrapStrncpy_single(const char* s) { if (s) { return mxCreateString(s); } else { mxArray* z = mxCreateNumericMatrix(1,1, mxSINGLE_CLASS, mxREAL); *((float*)mxGetData(z)) = 0; return z; } } float mxWrapGetScalar_single(const mxArray* a, const char** e) { if (!a || mxGetClassID(a) != mxSINGLE_CLASS || mxGetM(a)*mxGetN(a) != 1) { *e = "Invalid scalar argument"; return 0; } return *((float*)mxGetData(a)); } char* mxWrapGetString_single(const mxArray* a, const char** e) { char* s; mwSize slen; if (!a || (!mxIsChar(a) && mxGetM(a)*mxGetN(a) > 0)) { *e = "Invalid string argument, mxSINGLE_CLASS expected"; return NULL; } slen = mxGetM(a)*mxGetN(a) + 1; s = (char*) mxMalloc(slen); if (mxGetM(a)*mxGetN(a) == 0) *s = 0; else mxGetString(a, s, slen); return s; } #define mxWrapGetArrayDef_single(func, T) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* q; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ q = (float*) mxGetData(a); \ for (i = 0; i < arraylen; ++i) \ *p++ = (T) (*q++); \ return array; \ } #define mxWrapCopyDef_single(func, T) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* p = (float*) mxGetData(a); \ for (i = 0; i < n; ++i) \ *p++ = *q++; \ } #define mxWrapReturnDef_single(func, T) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ float* p; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxREAL); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxREAL);\ p = (float*) mxGetData(a); \ for (i = 0; i < m*n; ++i) \ *p++ = *q++; \ return a; \ } \ } #define mxWrapGetScalarZDef_single(func, T, ZT, setz) \ void func(T* z, const mxArray* a) \ { \ float* pr = (float*) mxGetData(a); \ float* pi = (float*) mxGetImagData(a); \ setz(z, (ZT) *pr, (pi ? (ZT) *pi : (ZT) 0)); \ } #define mxWrapGetArrayZDef_single(func, T, ZT, setz) \ T* func(const mxArray* a, const char** e) \ { \ T* array; \ mwSize arraylen; \ mwIndex i; \ T* p; \ float* qr; \ float* qi; \ if (!a || mxGetClassID(a) != mxSINGLE_CLASS) { \ *e = "Invalid array argument, mxSINGLE_CLASS expected"; \ return 0; \ } \ arraylen = mxGetM(a)*mxGetN(a); \ array = (T*) mxMalloc(mxGetM(a)*mxGetN(a) * sizeof(T)); \ p = array; \ qr = (float*) mxGetData(a); \ qi = (float*) mxGetImagData(a); \ for (i = 0; i < arraylen; ++i) { \ ZT val_qr = *qr++; \ ZT val_qi = (qi ? (ZT) *qi++ : (ZT) 0); \ setz(p, val_qr, val_qi); \ ++p; \ } \ return array; \ } #define mxWrapCopyZDef_single(func, T, real, imag) \ void func(mxArray* a, const T* q, mwSize n) \ { \ mwIndex i; \ float* pr = (float*) mxGetData(a); \ float* pi = (float*) mxGetImagData(a); \ for (i = 0; i < n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ } #define mxWrapReturnZDef_single(func, T, real, imag) \ mxArray* func(const T* q, mwSize m, mwSize n) \ { \ mwIndex i; \ float* pr; \ float* pi; \ if (!q) { \ return mxCreateNumericMatrix(0,0, mxSINGLE_CLASS, mxCOMPLEX); \ } else { \ mxArray* a = mxCreateNumericMatrix(m,n, mxSINGLE_CLASS, mxCOMPLEX);\ pr = (float*) mxGetData(a); \ pi = (float*) mxGetImagData(a); \ for (i = 0; i < m*n; ++i) { \ *pr++ = real(*q); \ *pi++ = imag(*q); \ ++q; \ } \ return a; \ } \ } #endif zgimbutas-mwrap-04cdb66/src/mwrap-typecheck.cc000066400000000000000000000266651522673526200215070ustar00rootroot00000000000000/* * mwrap-typecheck.cc * Typecheck MWrap AST. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include #include #include "mwrap-ast.h" /* -- Label input / output indices -- */ /* * To each argument, we assign an input label (index of the argument * in the prhs array) and an output label (the index in the plhs * array). The protocol is that we pass input and inout arguments in * first, and then tack on all the dimensioning arguments at the end. */ void label_dim_args(Expr* e, int& icount) { if (!e) return; e->input_label = icount++; label_dim_args(e->next, icount); } void label_dim_args(Var* v, int& icount) { if (!v) return; if (v->qual) label_dim_args(v->qual->args, icount); label_dim_args(v->next, icount); } void label_args(Var* v, int& icount, int& ocount) { if (!v) return; if (v->iospec == 'i' || v->iospec == 'b') v->input_label = icount++; if (v->iospec == 'o' || v->iospec == 'b') v->output_label = ocount++; label_args(v->next, icount, ocount); } void label_args(Func* f) { int icount = 0; int ocount = 0; if (f->thisv) icount = 1; label_args(f->ret, icount, ocount); label_args(f->args, icount, ocount); label_dim_args(f->ret, icount); label_dim_args(f->args, icount); } /* -- Fortran-ize arguments -- */ /* * For FORTRAN calls, we make the following type conversions and * checks on arguments: * * - All types of scalar arguments are converted to pointer-to-scalar. * - Object arguments are forbidden * - C string arguments generate a warning * * Only scalar return values are allowed from wrapped FORTRAN functions. */ int fortranize_args(Var* v, int line) { if (!v) return 0; int errcount = 0; if (v->tinfo == VT_obj || v->tinfo == VT_p_obj || v->tinfo == VT_r_obj) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Cannot pass object %s to FORTRAN\n", v->name); ++errcount; } else if (v->tinfo == VT_rarray) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Cannot pass pointer ref %s to FORTRAN\n", v->name); ++errcount; } else if (v->tinfo == VT_string) { fprintf(stderr, "Warning (%d): ", line); fprintf(stderr, "Danger passing C string %s to FORTRAN\n", v->name); } else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar) { v->tinfo = VT_p_scalar; } else if (v->tinfo == VT_cscalar || v->tinfo == VT_r_cscalar) { v->tinfo = VT_p_cscalar; } else if (v->tinfo == VT_zscalar || v->tinfo == VT_r_zscalar) { v->tinfo = VT_p_zscalar; } return errcount + fortranize_args(v->next, line); } int fortranize_ret(Var* v, int line) { if (!v) return 0; if (v->tinfo == VT_cscalar || v->tinfo == VT_zscalar) { fprintf(stderr, "Warning (%d): ", line); fprintf(stderr, "Danger returning complex from FORTRAN\n"); } else if (v->tinfo != VT_scalar) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Can only return scalars from FORTRAN\n"); return 1; } return 0; } int fortranize_args(Func* f, int line) { if (!f->fort) return 0; return (fortranize_args(f->args, line) + fortranize_ret(f->ret, line)); } /* -- Type info assignment and checking -- */ /* * Strictly speaking, we're doing semantic checking here -- there are a * few things we check for that don't qualify as type errors. The * assumption is that any input to the C code generator has passed the * type checker. */ int assign_scalar_tinfo(Var* v, int line, int tags, int tagp, int tagr, int taga, int tagar) { // Numeric types if (!v->qual) { v->tinfo = tags; } else if (v->qual->qual == '*') { v->tinfo = tagp; } else if (v->qual->qual == '&') { v->tinfo = tagr; } else if (v->qual->qual == 'a') { v->tinfo = taga; if (v->qual->args && v->qual->args->next && v->qual->args->next->next) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Array %s should be 1D or 2D\n", v->name); return 1; } } else if (v->qual->qual == 'r') { v->tinfo = tagar; if (tagar == VT_unk) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Array ref %s must be to a real array\n", v->name); return 1; } if (v->qual->args && v->qual->args->next && v->qual->args->next->next) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Array %s should be 1D or 2D\n", v->name); return 1; } } else { assert(0); } return 0; } int assign_tinfo(Var* v, int line) { if (is_scalar_type(v->basetype)) { return assign_scalar_tinfo(v, line, VT_scalar, VT_p_scalar, VT_r_scalar, VT_array, VT_rarray); } else if (is_cscalar_type(v->basetype)) { return assign_scalar_tinfo(v, line, VT_cscalar, VT_p_cscalar, VT_r_cscalar, VT_carray, VT_unk); } else if (is_zscalar_type(v->basetype)) { return assign_scalar_tinfo(v, line, VT_zscalar, VT_p_zscalar, VT_r_zscalar, VT_zarray, VT_unk); } else if (strcmp(v->basetype, "const") == 0) { if (v->qual) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Constant %s cannot have modifiers\n", v->name); return 1; } v->tinfo = VT_const; if (v->name[0] == '\'') { char* p_out = v->name; char* p_in = v->name; for (; *p_in; ++p_in) { if (*p_in != '\'') *p_out++ = *p_in; } *p_out++ = 0; } } else if (strcmp(v->basetype, "cstring") == 0) { // String type if (v->qual && v->qual->qual != 'a') { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "String type %s cannot have modifiers\n", v->name); return 1; } if (v->qual && v->qual->args && v->qual->args->next) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Strings are one dimensional\n"); return 1; } v->tinfo = VT_string; } else if (strcmp(v->basetype, "mxArray") == 0) { // MATLAB intrinsic type if (v->qual) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "mxArray %s cannot have modifiers\n", v->name); return 1; } v->tinfo = VT_mx; } else { // Object type if (!v->qual) v->tinfo = VT_obj; else if (v->qual->qual == '*') v->tinfo = VT_p_obj; else if (v->qual->qual == '&') v->tinfo = VT_r_obj; else if (v->qual->qual == 'a' || v->qual->qual == 'r') { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "%s cannot be an array of object %s\n", v->name, v->basetype); return 1; } else assert(0); } return 0; } int typecheck_return(Var* v, int line) { if (!v) return 0; int err = assign_tinfo(v, line); if ((v->tinfo == VT_array || v->tinfo == VT_carray || v->tinfo == VT_zarray) && !(v->qual && v->qual->args)) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Return array %s must have dims\n", v->name); ++err; } else if (v->tinfo == VT_const) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Cannot return constant\n"); ++err; } else if (v->tinfo == VT_rarray) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Ref to array %s looks just like array on return\n", v->name); ++err; } else if (v->tinfo == VT_string && v->qual) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Return string %s cannot have dims\n", v->name); ++err; } return err; } int typecheck_args(Var* v, int line) { if (!v) return 0; int err = assign_tinfo(v, line); if (v->devicespec == 'g' && v->tinfo != VT_array && v->tinfo != VT_carray && v->tinfo != VT_zarray) { if (v->iospec != 'o' && (v->tinfo == VT_obj || v->tinfo == VT_p_obj || v->tinfo == VT_r_obj || v->tinfo == VT_string)) { /* Object/string inputs compiled with the gpu qualifier as a no-op in 1.2; keep accepting them with a warning, and clear the qualifier so codegen really does ignore it. This relies on typecheck running before the .m stub is printed and before codegen reads devicespec (grammar action order). */ fprintf(stderr, "Warning (%d): ", line); fprintf(stderr, "gpu qualifier on non-array %s is ignored\n", v->name); v->devicespec = 'c'; } else { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "gpu variable %s must be a numeric array\n", v->name); ++err; } } if (v->iospec == 'i') return err + typecheck_args(v->next, line); if (isdigit(v->name[0])) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Number %s cannot be output\n", v->name); ++err; } if ((v->tinfo == VT_obj || v->tinfo == VT_p_obj || v->tinfo == VT_r_obj) && !is_mxarray_type(v->basetype)) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Object %s cannot be output\n", v->name); ++err; } else if ((v->tinfo == VT_array || v->tinfo == VT_carray || v->tinfo == VT_zarray || v->tinfo == VT_rarray) && v->iospec == 'o' && !(v->qual && v->qual->args)) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Output array %s must have dims\n", v->name); ++err; } else if (v->tinfo == VT_rarray && v->iospec != 'o') { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Array ref %s *must* be output\n", v->name); ++err; } else if (v->tinfo == VT_scalar) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Scalar %s cannot be output\n", v->name); ++err; } else if (v->tinfo == VT_const) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "Constant %s cannot be output\n", v->name); ++err; } else if (v->tinfo == VT_string && !(v->qual && v->qual->args)) { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "String %s cannot be output without size\n", v->name); ++err; } else if (v->tinfo == VT_mx && v->iospec == 'b') { fprintf(stderr, "Error (%d): ", line); fprintf(stderr, "mxArray %s cannot be used for inout\n", v->name); ++err; } return err + typecheck_args(v->next, line); } int typecheck(Func* f, int line) { label_args(f); return (typecheck_return(f->ret, line) + typecheck_args(f->args, line) + fortranize_args(f, line)); } zgimbutas-mwrap-04cdb66/src/mwrap-version.h000066400000000000000000000006231522673526200210410ustar00rootroot00000000000000/* * mwrap-version.h * Single source of truth for the mwrap version (C++ implementation). * * Keep in sync with python/mwrap_version.py; the parity suite * (testing/test_python.sh) fails on any mismatch because both * implementations stamp this version into their generated output. */ #ifndef MWRAP_VERSION_H #define MWRAP_VERSION_H #define MWRAP_VERSION "1.3.5" #endif /* MWRAP_VERSION_H */ zgimbutas-mwrap-04cdb66/src/mwrap.l000066400000000000000000000146131522673526200173660ustar00rootroot00000000000000%{ /* * mwrap.l * Lexer for MWrap. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include "mwrap.hh" #include #include extern int listing_flag; // Output filenames from @ commands? extern int mbatching_flag; // Do we want to output on @ commands? extern int linenum; // Lexer line number extern FILE* outfp; // MATLAB output file extern FILE* outcfp; // C output file static int done_at_switch; // Set when @ redirection is done extern char* mwrap_strdup(char* s); static int is_name_char(char c) { return (isalnum(c) || c == '_'); } static char* fname_scan_line(char* s) { static char namebuf[256]; /* FIXME */ int name_start, name_end, i, j; /* Name ends at last alphanum before args */ name_end = 0; while (s[name_end] && s[name_end] != '(') ++name_end; while (name_end > 0 && !is_name_char(s[name_end])) --name_end; /* Back up to the start of the name */ name_start = name_end; while (s[name_start] > 0 && is_name_char(s[name_start])) --name_start; /* Copy the name into the buf and add .m */ for (i = name_start+1, j = 0; i <= name_end; ++i, ++j) namebuf[j] = s[i]; strcpy(namebuf+j, ".m"); return namebuf; } #define MAX_INCLUDE_DEPTH 10 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_line[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; extern void set_include_name(const char* s); extern void get_include_name(); /* The lexer switches states when it sees a specially formatted comment * as the first non-blank mode in a line. In total, there are six states: * * INITIAL - start of line * TS - ordinary text line * AS - at line (redirection) * FS - @function line (function declaration + redirection) * SS - embedded C line * BS - block of embedded C * CS - C call line * COMMS - Comment line * * Transitions are as follows. * * "$[" : INITIAL -> BS * "$]" : BS -> INITIAL * "@" : INITIAL -> AS (batching mode only) * "@function" : INITIAL -> FS * "$" : INITIAL -> SS * "#" : INITIAL -> CS * non-blank : INITIAL -> TS * newline: (AS, SS, CS, TS) -> INITIAL */ %} %s CSTATE %s SSTATE %s BSTATE %s ASTATE %s FSTATE %s TSTATE %s COMMSTATE %x INCLSTATE %% "@function" { if (mbatching_flag && outfp) fclose(outfp); BEGIN FSTATE; return NON_C_LINE; } "@include" { BEGIN INCLSTATE; return NON_C_LINE; } "@" { if (mbatching_flag && outfp) fclose(outfp); done_at_switch = 0; BEGIN ASTATE; return NON_C_LINE; } "#" { BEGIN CSTATE; } \$\[[ \t\r]*\n { BEGIN BSTATE; ++linenum; return NON_C_LINE; } "$" { BEGIN SSTATE; return NON_C_LINE; } "//" { BEGIN COMMSTATE; return NON_C_LINE; } \n { if (outfp) fprintf(outfp, "%s", yytext); ++linenum; BEGIN 0; return NON_C_LINE; } [ \t] { if (outfp) fprintf(outfp, "%s", yytext); } . { if (outfp) fprintf(outfp, "%s", yytext); BEGIN TSTATE; return NON_C_LINE; } \n { ++linenum; BEGIN 0; } . ; <> { if (--include_stack_ptr < 0) { yyterminate(); } else { yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(include_stack[include_stack_ptr]); linenum = include_stack_line[include_stack_ptr]; get_include_name(); BEGIN INCLSTATE; } } [ \t\r]* ; [^ \t\r\n]+ { if (include_stack_ptr >= MAX_INCLUDE_DEPTH) { fprintf(stderr, "Error: Includes nested too deeply"); exit(-1); } set_include_name(yytext); include_stack_line[include_stack_ptr] = linenum; include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; yyin = fopen(yytext, "r"); if (!yyin) { fprintf(stderr, "Error: Could not read '%s'\n", yytext); exit(-1); } linenum = 1; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); BEGIN 0; } \n { ++linenum; BEGIN 0; } [^\n]+ { char* fname = fname_scan_line(yytext); if (mbatching_flag) { outfp = fopen(fname, "w+"); if (!outfp) { fprintf(stderr, "Error: Could not write %s\n", yytext); exit(-1); } } if (listing_flag) fprintf(stdout, "%s\n", fname); if (outfp) fprintf(outfp, "function%s\n", yytext); } \n { ++linenum; BEGIN 0; } [ \t\r]+ ; [^ \t\r\n]+ { if (mbatching_flag && !done_at_switch) { outfp = fopen(yytext, "w+"); if (!outfp) { fprintf(stderr, "Error: Could not write %s\n", yytext); exit(-1); } } if (listing_flag && !done_at_switch) fprintf(stdout, "%s\n", yytext); done_at_switch = 1; } \n { ++linenum; BEGIN 0; } \n { if (outfp) fprintf(outfp, "%s", yytext); ++ linenum; BEGIN 0; } . { if (outfp) fprintf(outfp, "%s", yytext); } \n { if (outcfp) fprintf(outcfp, "%s", yytext); ++linenum; BEGIN 0; } . { if (outcfp) fprintf(outcfp, "%s", yytext); } \$\][ \t\r]*\n { ++linenum; BEGIN 0; } \n { if (outcfp) fprintf(outcfp, "%s", yytext); ++linenum; } . { if (outcfp) fprintf(outcfp, "%s", yytext); } "new" { return NEW; } "FORTRAN" { return FORTRAN; } "input" { return INPUT; } "output" { return OUTPUT; } "inout" { return INOUT; } "class" { return CLASS; } "typedef" { return TYPEDEF; } "cpu" { return CPU; } "gpu" { return GPU; } ((::)?[_a-zA-Z][_a-zA-Z0-9]*)* { yylval.string = mwrap_strdup(yytext); return ID; } [0-9]+ { yylval.string = mwrap_strdup(yytext); return NUMBER; } \'[^'\n]*['\n] { yylval.string = mwrap_strdup(yytext); return STRING; } \/\/[^\n]* ; [ \t\r]+ ; \n { ++linenum; BEGIN 0; } . return yytext[0]; %% zgimbutas-mwrap-04cdb66/src/mwrap.y000066400000000000000000000317121522673526200174020ustar00rootroot00000000000000%{ /* * mwrap.y * Parser for mwrap. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include "mwrap-ast.h" #include "mwrap-version.h" extern "C" { int yylex(); int yywrap(); int yyerror(const char* s); } using std::string; bool mw_use_gpu = 0; // Use GPU? bool mw_generate_catch = false; // Catch C++ exceptions? bool mw_use_cpp_complex = false; // Use C++ complex types? bool mw_use_c99_complex = false; // Use C99 complex types? int mw_promote_int = 0; // Convert integer types to mwSize? int mw_use_int32_t = 0; // Use C99 int32_t? int mw_use_int64_t = 0; // Use C99 int64_t? int mw_use_uint32_t = 0; // Use C99 uint32_t? int mw_use_uint64_t = 0; // Use C99 uint64_t? int mw_use_ulong = 0; // Use unsigned long? int mw_use_uint = 0; // Use unsigned int? int mw_use_ushort = 0; // Use unsigned short? int mw_use_uchar = 0; // Use unsigned char? int listing_flag = 0; // Output filenames from @ commands? int mbatching_flag = 0; // Output on @ commands? int linenum = 0; // Lexer line number FILE* outfp = 0; // MATLAB output file FILE* outcfp = 0; // C output file static int type_errs = 0; // Number of typecheck errors static int syntax_errs = 0; // Number of syntax errors static int func_id = 0; // Assign stub numbers static Func* funcs = 0; // AST - linked list of functions static Func* lastfunc = 0; // Last link in funcs list static const char* mexfunc = "mexfunction"; // Name of mex function static string current_ifname; // Current input file name #define MAX_INCLUDE_DEPTH 10 static string include_stack_names[MAX_INCLUDE_DEPTH]; extern int include_stack_ptr; extern "C" void set_include_name(const char* s) { include_stack_names[include_stack_ptr] = current_ifname; current_ifname = s; } extern "C" void get_include_name() { current_ifname = include_stack_names[include_stack_ptr].c_str(); } inline void add_func(Func* func) { static std::map func_lookup; if (!funcs) { funcs = func; lastfunc = func; func_lookup[id_string(func)] = func; return; } Func*& func_ptr = func_lookup[id_string(func)]; if (func_ptr) { func_ptr->same_next = func; } else { lastfunc->next = func; lastfunc = func; } func_ptr = func; } %} %union { char* string; struct Func* func; struct Var* var; struct TypeQual* qual; struct Expr* expr; struct InheritsDecl* inherits; char c; } %token NON_C_LINE %token NEW TYPEDEF CLASS FORTRAN %token ID %token NUMBER STRING %token INPUT OUTPUT INOUT %token CPU GPU %type func funcall %type var basevar args argsrest %type iospec %type devicespec %type quals aqual %type arrayspec exprs exprrest expr %type inheritslist inheritsrest %error-verbose %% statements: statement statements | ; statement: basevar '=' funcall { $3->ret = $1; $3->id = ++func_id; type_errs += typecheck($3, linenum); if (outfp) print_matlab_call(outfp, $3, mexfunc); add_func($3); } | funcall { $1->id = ++func_id; type_errs += typecheck($1, linenum); if (outfp) print_matlab_call(outfp, $1, mexfunc); add_func($1); } | tdef | classdef | NON_C_LINE | error ';' { yyerrok; } ; tdef: TYPEDEF ID ID ';' { if (strcmp($2, "numeric") == 0) { add_scalar_type($3); } else if (strcmp($2, "dcomplex") == 0) { add_zscalar_type($3); } else if (strcmp($2, "fcomplex") == 0) { add_cscalar_type($3); } else if (strcmp($2, "mxArray") == 0) { add_mxarray_type($3); } else { fprintf(stderr, "Unrecognized typespace: %s\n", $2); ++type_errs; } delete[] $2; delete[] $3; } ; classdef: CLASS ID ':' inheritslist ';' { add_inherits($2, $4); delete[] $2; destroy($4); } inheritslist: ID inheritsrest { $$ = new InheritsDecl($1, $2); } ; inheritsrest: ',' ID inheritsrest { $$ = new InheritsDecl($2, $3); } | { $$ = NULL; } ; funcall: func '(' args ')' ';' { $$ = $1; $$->args = $3; } ; args: var argsrest { $$ = $1; $$->next = $2; } | { $$ = NULL; } ; argsrest: ',' var argsrest {$$ = $2; $$->next = $3; } | { $$ = NULL; } ; basevar: ID ID { $$ = new Var('c', 'o', promote_int($1), NULL, $2); } basevar: ID quals ID { $$ = new Var('c', 'o', promote_int($1), $2, $3); } basevar: ID ID aqual { $$ = new Var('c', 'o', promote_int($1), $3, $2); } var: devicespec iospec ID ID { $$ = new Var($1, $2, promote_int($3), NULL, $4); } var: devicespec iospec ID quals ID { $$ = new Var($1, $2, promote_int($3), $4, $5); } var: devicespec iospec ID ID aqual { $$ = new Var($1, $2, promote_int($3), $5, $4); } var: devicespec iospec ID NUMBER { $$ = new Var($1, $2, promote_int($3), NULL, $4); } var: devicespec iospec ID quals NUMBER { $$ = new Var($1, $2, promote_int($3), $4, $5); } var: devicespec iospec ID STRING { $$ = new Var($1, $2, promote_int($3), NULL, $4); } var: devicespec iospec ID quals STRING { $$ = new Var($1, $2, promote_int($3), $4, $5); } devicespec: CPU { $$ = 'c'; } | GPU { $$ = 'g'; } | { $$ = 'c'; } ; iospec: INPUT { $$ = 'i'; } | OUTPUT { $$ = 'o'; } | INOUT { $$ = 'b'; } | { $$ = 'i'; } ; quals: '*' { $$ = new TypeQual('*', NULL); } | '&' { $$ = new TypeQual('&', NULL); } | aqual { $$ = $1; } ; aqual: arrayspec { $$ = new TypeQual('a', $1); } | arrayspec '&' { $$ = new TypeQual('r', $1); } ; arrayspec: '[' exprs ']' { $$ = $2; } ; exprs: expr exprrest { $$ = $1; $$->next = $2; } | { $$ = NULL; } exprrest: ',' expr exprrest { $$ = $2; $$->next = $3; } | { $$ = NULL; } expr: ID { $$ = new Expr($1); } | NUMBER { $$ = new Expr($1); } func: ID '-' '>' ID '.' ID { $$ = new Func($1, $4, $6, current_ifname, linenum); } | ID { $$ = new Func(NULL, NULL, $1, current_ifname, linenum); } | FORTRAN ID { $$ = new Func(NULL, NULL, $2, current_ifname, linenum); $$->fort = true; } | NEW ID { $$ = new Func(NULL, $2, mwrap_strdup("new"), current_ifname, linenum); } ; %% #include #include extern FILE* yyin; int yywrap() { return 1; } int yyerror(const char* s) { fprintf(stderr, "Parse error (%s:%d): %s\n", current_ifname.c_str(), linenum, s); ++syntax_errs; return 0; } char* mwrap_strdup(const char* s) { char* result = new char[strlen(s)+1]; strcpy(result, s); return result; } const char* usage_string = "Usage: mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] infile1 ...\n" "Try 'mwrap --help' for more information.\n"; const char* help_string = "mwrap " MWRAP_VERSION " - MEX file generator for MATLAB and Octave\n" "\n" "Syntax:\n" " mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] [-mb] [-list]\n" " [-catch] [-i8] [-c99complex] [-cppcomplex] [-gpu] infile1 infile2 ...\n" "\n" " -mex outputmex -- specify the MATLAB mex function name\n" " -m output.m -- generate the MATLAB stub called output.m\n" " -c outputmex.c -- generate the C file outputmex.c\n" " -mb -- generate .m files specified with @ redirections\n" " -list -- list files specified with @ redirections\n" " -catch -- generate C++ exception handling code\n" " -i8 -- convert int, long, uint, ulong to int64_t, uint64_t\n" " -c99complex -- add support code for C99 complex types\n" " -cppcomplex -- add support code for C++ complex types\n" " -gpu -- add support code for MATLAB gpuArray\n" "\n"; int main(int argc, char** argv) { int j; int err_flag = 0; init_scalar_types(); if (argc == 1) { fprintf(stderr, "%s", help_string); return 0; } else { bool emitted_mex_init = false; const char* mfile = NULL; const char* cfile = NULL; for (j = 1; j < argc; ++j) { if (strcmp(argv[j], "--help") == 0) { fprintf(stderr, "%s", help_string); return 0; } if (strcmp(argv[j], "-m") == 0 && j+1 < argc) mfile = argv[++j]; else if (strcmp(argv[j], "-c") == 0 && j+1 < argc) cfile = argv[++j]; else if (strcmp(argv[j], "-mex") == 0 && j+1 < argc) mexfunc = argv[++j]; else if (strcmp(argv[j], "-mb") == 0) mbatching_flag = 1; else if (strcmp(argv[j], "-list") == 0) listing_flag = 1; else if (strcmp(argv[j], "-catch") == 0) mw_generate_catch = true; else if (strcmp(argv[j], "-i8") == 0) mw_promote_int = 4; else if (strcmp(argv[j], "-c99complex") == 0) mw_use_c99_complex = true; else if (strcmp(argv[j], "-cppcomplex") == 0) mw_use_cpp_complex = true; else if (strcmp(argv[j], "-gpu") == 0) mw_use_gpu = true; } if (mw_use_c99_complex || mw_use_cpp_complex) { add_zscalar_type("dcomplex"); add_cscalar_type("fcomplex"); } /* Pre-scan: count input files before opening any output files */ int yyin_count = 0; for (j = 1; j < argc; ++j) { if (strcmp(argv[j], "-m") == 0 || strcmp(argv[j], "-c") == 0 || strcmp(argv[j], "-mex") == 0) ++j; else if (strcmp(argv[j], "--help") == 0 || strcmp(argv[j], "-mb") == 0 || strcmp(argv[j], "-list") == 0 || strcmp(argv[j], "-catch") == 0 || strcmp(argv[j], "-i8") == 0 || strcmp(argv[j], "-c99complex") == 0 || strcmp(argv[j], "-cppcomplex") == 0 || strcmp(argv[j], "-gpu") == 0) ; else ++yyin_count; } if (yyin_count == 0) { fprintf(stderr, "%s", usage_string); return 1; } /* Now safe to open output files */ if (mfile) { outfp = fopen(mfile, "w+"); if (!outfp) { fprintf(stderr, "Could not write %s\n", mfile); return 1; } } if (cfile) { outcfp = fopen(cfile, "w+"); if (!outcfp) { fprintf(stderr, "Could not write %s\n", cfile); if (outfp) fclose(outfp); return 1; } } for (j = 1; j < argc; ++j) { if (strcmp(argv[j], "-m") == 0 || strcmp(argv[j], "-c") == 0 || strcmp(argv[j], "-mex") == 0) ++j; else if (strcmp(argv[j], "-mb") == 0 || strcmp(argv[j], "-list") == 0 || strcmp(argv[j], "-catch") == 0 || strcmp(argv[j], "-i8") == 0 || strcmp(argv[j], "-c99complex") == 0 || strcmp(argv[j], "-cppcomplex") == 0 || strcmp(argv[j], "-gpu") == 0) ; else { linenum = 1; type_errs = 0; syntax_errs = 0; include_stack_ptr = 0; yyin = fopen(argv[j], "r"); if (yyin) { current_ifname = argv[j]; if (outcfp && !emitted_mex_init) { print_mex_init(outcfp); emitted_mex_init = true; } /* Recovered and unrecovered syntax errors alike are counted by yyerror via syntax_errs; adding yyparse's return here would double-count the unrecovered ones. */ yyparse(); fclose(yyin); } else { fprintf(stderr, "Could not read %s\n", argv[j]); ++err_flag; } if (type_errs) fprintf(stderr, "%s: %d type errors detected\n", argv[j], type_errs); err_flag += type_errs + syntax_errs; } } } if (!err_flag && outcfp) print_mex_file(outcfp, funcs); destroy(funcs); destroy_inherits(); if (outfp) fclose(outfp); if (outcfp) fclose(outcfp); /* POSIX keeps only 8 bits of the exit status; returning the raw error count would report success at exact multiples of 256. */ return err_flag ? 1 : 0; } zgimbutas-mwrap-04cdb66/src/mwrap.y.in000066400000000000000000000317131522673526200200100ustar00rootroot00000000000000%{ /* * mwrap.y * Parser for mwrap. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include "mwrap-ast.h" #include "mwrap-version.h" extern "C" { int yylex(); int yywrap(); int yyerror(const char* s); } using std::string; bool mw_use_gpu = 0; // Use GPU? bool mw_generate_catch = false; // Catch C++ exceptions? bool mw_use_cpp_complex = false; // Use C++ complex types? bool mw_use_c99_complex = false; // Use C99 complex types? int mw_promote_int = 0; // Convert integer types to mwSize? int mw_use_int32_t = 0; // Use C99 int32_t? int mw_use_int64_t = 0; // Use C99 int64_t? int mw_use_uint32_t = 0; // Use C99 uint32_t? int mw_use_uint64_t = 0; // Use C99 uint64_t? int mw_use_ulong = 0; // Use unsigned long? int mw_use_uint = 0; // Use unsigned int? int mw_use_ushort = 0; // Use unsigned short? int mw_use_uchar = 0; // Use unsigned char? int listing_flag = 0; // Output filenames from @ commands? int mbatching_flag = 0; // Output on @ commands? int linenum = 0; // Lexer line number FILE* outfp = 0; // MATLAB output file FILE* outcfp = 0; // C output file static int type_errs = 0; // Number of typecheck errors static int syntax_errs = 0; // Number of syntax errors static int func_id = 0; // Assign stub numbers static Func* funcs = 0; // AST - linked list of functions static Func* lastfunc = 0; // Last link in funcs list static const char* mexfunc = "mexfunction"; // Name of mex function static string current_ifname; // Current input file name #define MAX_INCLUDE_DEPTH 10 static string include_stack_names[MAX_INCLUDE_DEPTH]; extern int include_stack_ptr; extern "C" void set_include_name(const char* s) { include_stack_names[include_stack_ptr] = current_ifname; current_ifname = s; } extern "C" void get_include_name() { current_ifname = include_stack_names[include_stack_ptr].c_str(); } inline void add_func(Func* func) { static std::map func_lookup; if (!funcs) { funcs = func; lastfunc = func; func_lookup[id_string(func)] = func; return; } Func*& func_ptr = func_lookup[id_string(func)]; if (func_ptr) { func_ptr->same_next = func; } else { lastfunc->next = func; lastfunc = func; } func_ptr = func; } %} %union { char* string; struct Func* func; struct Var* var; struct TypeQual* qual; struct Expr* expr; struct InheritsDecl* inherits; char c; } %token NON_C_LINE %token NEW TYPEDEF CLASS FORTRAN %token ID %token NUMBER STRING %token INPUT OUTPUT INOUT %token CPU GPU %type func funcall %type var basevar args argsrest %type iospec %type devicespec %type quals aqual %type arrayspec exprs exprrest expr %type inheritslist inheritsrest @ERROR_VERBOSE@ %% statements: statement statements | ; statement: basevar '=' funcall { $3->ret = $1; $3->id = ++func_id; type_errs += typecheck($3, linenum); if (outfp) print_matlab_call(outfp, $3, mexfunc); add_func($3); } | funcall { $1->id = ++func_id; type_errs += typecheck($1, linenum); if (outfp) print_matlab_call(outfp, $1, mexfunc); add_func($1); } | tdef | classdef | NON_C_LINE | error ';' { yyerrok; } ; tdef: TYPEDEF ID ID ';' { if (strcmp($2, "numeric") == 0) { add_scalar_type($3); } else if (strcmp($2, "dcomplex") == 0) { add_zscalar_type($3); } else if (strcmp($2, "fcomplex") == 0) { add_cscalar_type($3); } else if (strcmp($2, "mxArray") == 0) { add_mxarray_type($3); } else { fprintf(stderr, "Unrecognized typespace: %s\n", $2); ++type_errs; } delete[] $2; delete[] $3; } ; classdef: CLASS ID ':' inheritslist ';' { add_inherits($2, $4); delete[] $2; destroy($4); } inheritslist: ID inheritsrest { $$ = new InheritsDecl($1, $2); } ; inheritsrest: ',' ID inheritsrest { $$ = new InheritsDecl($2, $3); } | { $$ = NULL; } ; funcall: func '(' args ')' ';' { $$ = $1; $$->args = $3; } ; args: var argsrest { $$ = $1; $$->next = $2; } | { $$ = NULL; } ; argsrest: ',' var argsrest {$$ = $2; $$->next = $3; } | { $$ = NULL; } ; basevar: ID ID { $$ = new Var('c', 'o', promote_int($1), NULL, $2); } basevar: ID quals ID { $$ = new Var('c', 'o', promote_int($1), $2, $3); } basevar: ID ID aqual { $$ = new Var('c', 'o', promote_int($1), $3, $2); } var: devicespec iospec ID ID { $$ = new Var($1, $2, promote_int($3), NULL, $4); } var: devicespec iospec ID quals ID { $$ = new Var($1, $2, promote_int($3), $4, $5); } var: devicespec iospec ID ID aqual { $$ = new Var($1, $2, promote_int($3), $5, $4); } var: devicespec iospec ID NUMBER { $$ = new Var($1, $2, promote_int($3), NULL, $4); } var: devicespec iospec ID quals NUMBER { $$ = new Var($1, $2, promote_int($3), $4, $5); } var: devicespec iospec ID STRING { $$ = new Var($1, $2, promote_int($3), NULL, $4); } var: devicespec iospec ID quals STRING { $$ = new Var($1, $2, promote_int($3), $4, $5); } devicespec: CPU { $$ = 'c'; } | GPU { $$ = 'g'; } | { $$ = 'c'; } ; iospec: INPUT { $$ = 'i'; } | OUTPUT { $$ = 'o'; } | INOUT { $$ = 'b'; } | { $$ = 'i'; } ; quals: '*' { $$ = new TypeQual('*', NULL); } | '&' { $$ = new TypeQual('&', NULL); } | aqual { $$ = $1; } ; aqual: arrayspec { $$ = new TypeQual('a', $1); } | arrayspec '&' { $$ = new TypeQual('r', $1); } ; arrayspec: '[' exprs ']' { $$ = $2; } ; exprs: expr exprrest { $$ = $1; $$->next = $2; } | { $$ = NULL; } exprrest: ',' expr exprrest { $$ = $2; $$->next = $3; } | { $$ = NULL; } expr: ID { $$ = new Expr($1); } | NUMBER { $$ = new Expr($1); } func: ID '-' '>' ID '.' ID { $$ = new Func($1, $4, $6, current_ifname, linenum); } | ID { $$ = new Func(NULL, NULL, $1, current_ifname, linenum); } | FORTRAN ID { $$ = new Func(NULL, NULL, $2, current_ifname, linenum); $$->fort = true; } | NEW ID { $$ = new Func(NULL, $2, mwrap_strdup("new"), current_ifname, linenum); } ; %% #include #include extern FILE* yyin; int yywrap() { return 1; } int yyerror(const char* s) { fprintf(stderr, "Parse error (%s:%d): %s\n", current_ifname.c_str(), linenum, s); ++syntax_errs; return 0; } char* mwrap_strdup(const char* s) { char* result = new char[strlen(s)+1]; strcpy(result, s); return result; } const char* usage_string = "Usage: mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] infile1 ...\n" "Try 'mwrap --help' for more information.\n"; const char* help_string = "mwrap " MWRAP_VERSION " - MEX file generator for MATLAB and Octave\n" "\n" "Syntax:\n" " mwrap [-mex outputmex] [-m output.m] [-c outputmex.c] [-mb] [-list]\n" " [-catch] [-i8] [-c99complex] [-cppcomplex] [-gpu] infile1 infile2 ...\n" "\n" " -mex outputmex -- specify the MATLAB mex function name\n" " -m output.m -- generate the MATLAB stub called output.m\n" " -c outputmex.c -- generate the C file outputmex.c\n" " -mb -- generate .m files specified with @ redirections\n" " -list -- list files specified with @ redirections\n" " -catch -- generate C++ exception handling code\n" " -i8 -- convert int, long, uint, ulong to int64_t, uint64_t\n" " -c99complex -- add support code for C99 complex types\n" " -cppcomplex -- add support code for C++ complex types\n" " -gpu -- add support code for MATLAB gpuArray\n" "\n"; int main(int argc, char** argv) { int j; int err_flag = 0; init_scalar_types(); if (argc == 1) { fprintf(stderr, "%s", help_string); return 0; } else { bool emitted_mex_init = false; const char* mfile = NULL; const char* cfile = NULL; for (j = 1; j < argc; ++j) { if (strcmp(argv[j], "--help") == 0) { fprintf(stderr, "%s", help_string); return 0; } if (strcmp(argv[j], "-m") == 0 && j+1 < argc) mfile = argv[++j]; else if (strcmp(argv[j], "-c") == 0 && j+1 < argc) cfile = argv[++j]; else if (strcmp(argv[j], "-mex") == 0 && j+1 < argc) mexfunc = argv[++j]; else if (strcmp(argv[j], "-mb") == 0) mbatching_flag = 1; else if (strcmp(argv[j], "-list") == 0) listing_flag = 1; else if (strcmp(argv[j], "-catch") == 0) mw_generate_catch = true; else if (strcmp(argv[j], "-i8") == 0) mw_promote_int = 4; else if (strcmp(argv[j], "-c99complex") == 0) mw_use_c99_complex = true; else if (strcmp(argv[j], "-cppcomplex") == 0) mw_use_cpp_complex = true; else if (strcmp(argv[j], "-gpu") == 0) mw_use_gpu = true; } if (mw_use_c99_complex || mw_use_cpp_complex) { add_zscalar_type("dcomplex"); add_cscalar_type("fcomplex"); } /* Pre-scan: count input files before opening any output files */ int yyin_count = 0; for (j = 1; j < argc; ++j) { if (strcmp(argv[j], "-m") == 0 || strcmp(argv[j], "-c") == 0 || strcmp(argv[j], "-mex") == 0) ++j; else if (strcmp(argv[j], "--help") == 0 || strcmp(argv[j], "-mb") == 0 || strcmp(argv[j], "-list") == 0 || strcmp(argv[j], "-catch") == 0 || strcmp(argv[j], "-i8") == 0 || strcmp(argv[j], "-c99complex") == 0 || strcmp(argv[j], "-cppcomplex") == 0 || strcmp(argv[j], "-gpu") == 0) ; else ++yyin_count; } if (yyin_count == 0) { fprintf(stderr, "%s", usage_string); return 1; } /* Now safe to open output files */ if (mfile) { outfp = fopen(mfile, "w+"); if (!outfp) { fprintf(stderr, "Could not write %s\n", mfile); return 1; } } if (cfile) { outcfp = fopen(cfile, "w+"); if (!outcfp) { fprintf(stderr, "Could not write %s\n", cfile); if (outfp) fclose(outfp); return 1; } } for (j = 1; j < argc; ++j) { if (strcmp(argv[j], "-m") == 0 || strcmp(argv[j], "-c") == 0 || strcmp(argv[j], "-mex") == 0) ++j; else if (strcmp(argv[j], "-mb") == 0 || strcmp(argv[j], "-list") == 0 || strcmp(argv[j], "-catch") == 0 || strcmp(argv[j], "-i8") == 0 || strcmp(argv[j], "-c99complex") == 0 || strcmp(argv[j], "-cppcomplex") == 0 || strcmp(argv[j], "-gpu") == 0) ; else { linenum = 1; type_errs = 0; syntax_errs = 0; include_stack_ptr = 0; yyin = fopen(argv[j], "r"); if (yyin) { current_ifname = argv[j]; if (outcfp && !emitted_mex_init) { print_mex_init(outcfp); emitted_mex_init = true; } /* Recovered and unrecovered syntax errors alike are counted by yyerror via syntax_errs; adding yyparse's return here would double-count the unrecovered ones. */ yyparse(); fclose(yyin); } else { fprintf(stderr, "Could not read %s\n", argv[j]); ++err_flag; } if (type_errs) fprintf(stderr, "%s: %d type errors detected\n", argv[j], type_errs); err_flag += type_errs + syntax_errs; } } } if (!err_flag && outcfp) print_mex_file(outcfp, funcs); destroy(funcs); destroy_inherits(); if (outfp) fclose(outfp); if (outcfp) fclose(outcfp); /* POSIX keeps only 8 bits of the exit status; returning the raw error count would report success at exact multiples of 256. */ return err_flag ? 1 : 0; } zgimbutas-mwrap-04cdb66/src/stringify.c000066400000000000000000000017311522673526200202420ustar00rootroot00000000000000/* * stringify.c * Turns input from stdin into a C string definition written on * stdout. Adds quotes, newline characters, and backslashes as * needed. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include void stringify(const char* name) { char line[512]; char* p; printf("/*\n" " * Auto-generated by stringify\n" " */\n\n"); printf("const char* %s =", name); while (fgets(line, sizeof(line), stdin)) { printf("\n \""); for (p = line; *p; ++p) { if (*p == '"' || *p == '\\') putc('\\', stdout); if (*p != '\n' && *p != '\r') putc(*p, stdout); } printf("\\n\""); } printf(";\n\n"); } int main(int argc, char** argv) { if (argc != 2) { fprintf(stderr, "String name required\n"); exit(-1); } stringify(argv[1]); return 0; } zgimbutas-mwrap-04cdb66/testing/000077500000000000000000000000001522673526200167445ustar00rootroot00000000000000zgimbutas-mwrap-04cdb66/testing/CMakeLists.txt000066400000000000000000000232001522673526200215010ustar00rootroot00000000000000set(_mwrap_log_test_driver "${CMAKE_CURRENT_BINARY_DIR}/mwrap_log_test.cmake") set(_mwrap_log_test_script [[ if(NOT DEFINED MWRAP_EXECUTABLE) message(FATAL_ERROR "MWRAP_EXECUTABLE is required") endif() foreach(var OUTPUT_FILE REFERENCE_FILE) if(NOT DEFINED ${var}) message(FATAL_ERROR "${var} is required") endif() endforeach() if(NOT DEFINED TEST_NAME) set(TEST_NAME "mwrap_test") endif() set(_args) if(DEFINED ARGC AND ARGC GREATER 0) math(EXPR _last_index "${ARGC} - 1") foreach(_arg_index RANGE 0 ${_last_index}) set(_var_name ARG${_arg_index}) if(NOT DEFINED ${_var_name}) message(FATAL_ERROR "${TEST_NAME}: missing argument ${_var_name}") endif() list(APPEND _args "${${_var_name}}") endforeach() endif() set(_working_directory "${CMAKE_CURRENT_LIST_DIR}") if(DEFINED WORKING_DIRECTORY) set(_working_directory "${WORKING_DIRECTORY}") endif() file(MAKE_DIRECTORY "${_working_directory}") set(stdout_file "${OUTPUT_FILE}.stdout") execute_process( COMMAND ${MWRAP_EXECUTABLE} ${_args} RESULT_VARIABLE run_result OUTPUT_FILE "${stdout_file}" ERROR_FILE "${OUTPUT_FILE}" WORKING_DIRECTORY "${_working_directory}" ) set(expect_nonzero FALSE) if(DEFINED EXPECT_NONZERO AND EXPECT_NONZERO) set(expect_nonzero TRUE) endif() if(expect_nonzero AND run_result EQUAL 0) message(FATAL_ERROR "${TEST_NAME}: expected failure but command succeeded") endif() if(NOT expect_nonzero AND NOT run_result EQUAL 0) message(FATAL_ERROR "${TEST_NAME}: command failed with exit code ${run_result}") endif() if(NOT EXISTS "${REFERENCE_FILE}") message(FATAL_ERROR "${TEST_NAME}: reference file '${REFERENCE_FILE}' not found") endif() file(READ "${OUTPUT_FILE}" output_contents) string(REPLACE "end of file" "$end" normalized_output "${output_contents}") if(NOT normalized_output STREQUAL output_contents) file(WRITE "${OUTPUT_FILE}" "${normalized_output}") endif() execute_process( COMMAND ${CMAKE_COMMAND} -E compare_files "${OUTPUT_FILE}" "${REFERENCE_FILE}" RESULT_VARIABLE diff_result ) if(NOT diff_result EQUAL 0) file(READ "${OUTPUT_FILE}" normalized_output) message(FATAL_ERROR "${TEST_NAME}: output does not match reference.\n--- Actual stderr ---\n${normalized_output}") endif() ]]) file(WRITE "${_mwrap_log_test_driver}" "${_mwrap_log_test_script}") function(_mwrap_add_log_test name) set(options EXPECT_NONZERO) set(oneValueArgs REFERENCE) set(multiValueArgs ARGS) cmake_parse_arguments(_MWRAP_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT _MWRAP_TEST_REFERENCE) message(FATAL_ERROR "_mwrap_add_log_test requires REFERENCE for test '${name}'") endif() set(log_file "${CMAKE_CURRENT_BINARY_DIR}/${name}.log") list(LENGTH _MWRAP_TEST_ARGS arg_count) set(arg_definitions -DARGC=${arg_count}) if(arg_count GREATER 0) math(EXPR arg_max "${arg_count} - 1") foreach(arg_index RANGE 0 ${arg_max}) list(GET _MWRAP_TEST_ARGS ${arg_index} arg_value) list(APPEND arg_definitions -DARG${arg_index}=${arg_value}) endforeach() endif() add_test( NAME ${name} COMMAND ${CMAKE_COMMAND} -DMWRAP_EXECUTABLE=$ -DOUTPUT_FILE=${log_file} -DREFERENCE_FILE=${_MWRAP_TEST_REFERENCE} -DEXPECT_NONZERO=${_MWRAP_TEST_EXPECT_NONZERO} -DTEST_NAME=${name} -DWORKING_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR} ${arg_definitions} -P ${_mwrap_log_test_driver} ) set_tests_properties(${name} PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endfunction() _mwrap_add_log_test( mwrap.test_syntax EXPECT_NONZERO REFERENCE ${CMAKE_CURRENT_SOURCE_DIR}/test_syntax.ref ARGS -cppcomplex test_syntax.mw ) _mwrap_add_log_test( mwrap.test_typecheck EXPECT_NONZERO REFERENCE ${CMAKE_CURRENT_SOURCE_DIR}/test_typecheck.ref ARGS -cppcomplex test_typecheck.mw ) if(MWRAP_COMPILE_MEX AND MWRAP_MEX_BACKENDS) include(${CMAKE_SOURCE_DIR}/cmake/MwrapAddMex.cmake) set(_mwrap_testing_source_dir "${CMAKE_CURRENT_SOURCE_DIR}") set(_mwrap_testing_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/octave") file(MAKE_DIRECTORY "${_mwrap_testing_binary_dir}") configure_file( "${_mwrap_testing_source_dir}/test_include2.mw" "${_mwrap_testing_binary_dir}/test_include2.mw" COPYONLY ) set(_mwrap_testing_generators) mwrap_add_mex(test_transfers WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_transfersmex CC_FILENAME test_transfersmex.cc M_FILENAME test_transfers.m MW_FILES "${_mwrap_testing_source_dir}/test_transfers.mw" ) list(APPEND _mwrap_testing_generators test_transfers) mwrap_add_mex(test_cpp_complex WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_cpp_complexmex CC_FILENAME test_cpp_complexmex.cc M_FILENAME test_cpp_complex.m MW_FILES "${_mwrap_testing_source_dir}/test_cpp_complex.mw" MWRAP_FLAGS -cppcomplex ) list(APPEND _mwrap_testing_generators test_cpp_complex) mwrap_add_mex(test_c99_complex WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_c99_complexmex CC_FILENAME test_c99_complexmex.c M_FILENAME test_c99_complex.m MW_FILES "${_mwrap_testing_source_dir}/test_c99_complex.mw" MWRAP_FLAGS -c99complex ) list(APPEND _mwrap_testing_generators test_c99_complex) mwrap_add_mex(test_catch WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_catchmex CC_FILENAME test_catchmex.cc M_FILENAME test_catch.m MW_FILES "${_mwrap_testing_source_dir}/test_catch.mw" MWRAP_FLAGS -catch ) list(APPEND _mwrap_testing_generators test_catch) mwrap_add_mex(test_fortran1 WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_fortran1mex CC_FILENAME test_fortran1mex.cc M_FILENAME test_fortran1.m MW_FILES "${_mwrap_testing_source_dir}/test_fortran1.mw" ) list(APPEND _mwrap_testing_generators test_fortran1) mwrap_add_mex(test_fortran2 WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_fortran2mex CC_FILENAME test_fortran2mex.c M_FILENAME test_fortran2.m MW_FILES "${_mwrap_testing_source_dir}/test_fortran2.mw" ) list(APPEND _mwrap_testing_generators test_fortran2) mwrap_add_mex(test_include WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_includemex CC_FILENAME test_includemex.cc M_FILENAME test_include.m MW_FILES "${_mwrap_testing_source_dir}/test_include.mw" ) list(APPEND _mwrap_testing_generators test_include) mwrap_add_mex(test_single_cpp WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_singlemex CC_FILENAME test_singlemex.cc MW_FILES "${_mwrap_testing_source_dir}/test_single.mw" MWRAP_FLAGS -cppcomplex -mb ) list(APPEND _mwrap_testing_generators test_single_cpp) mwrap_add_mex(test_char_cpp WORK_DIR "${_mwrap_testing_binary_dir}" MEX_NAME test_charmex CC_FILENAME test_charmex.cc MW_FILES "${_mwrap_testing_source_dir}/test_char.mw" MWRAP_FLAGS -cppcomplex -mb ) list(APPEND _mwrap_testing_generators test_char_cpp) set(_mwrap_testing_mex_targets) foreach(_mwrap_generator IN LISTS _mwrap_testing_generators) _mwrap_compile_mex(${_mwrap_generator} OUTPUT_VAR _mwrap_generator_mex_targets) if(_mwrap_generator_mex_targets) list(APPEND _mwrap_testing_mex_targets ${_mwrap_generator_mex_targets}) endif() endforeach() if(_mwrap_testing_mex_targets) list(REMOVE_DUPLICATES _mwrap_testing_mex_targets) endif() set(_mwrap_redirect_output "${_mwrap_testing_binary_dir}/test_redirect.m") add_custom_command( OUTPUT "${_mwrap_redirect_output}" COMMAND ${CMAKE_COMMAND} -E make_directory "${_mwrap_testing_binary_dir}" COMMAND $ -mb "${_mwrap_testing_source_dir}/test_redirect.mw" DEPENDS mwrap "${_mwrap_testing_source_dir}/test_redirect.mw" WORKING_DIRECTORY "${_mwrap_testing_binary_dir}" COMMENT "Generating test_redirect.m with mwrap" VERBATIM ) add_custom_target(mwrap_testing_redirect DEPENDS "${_mwrap_redirect_output}") set_property(TARGET mwrap_testing_redirect PROPERTY FOLDER "tests") set(_mwrap_testing_artifacts ${_mwrap_testing_mex_targets} mwrap_testing_redirect) list(REMOVE_DUPLICATES _mwrap_testing_artifacts) if(_mwrap_testing_artifacts) add_custom_target(mwrap_testing_mex DEPENDS ${_mwrap_testing_artifacts}) set_property(TARGET mwrap_testing_mex PROPERTY FOLDER "tests") endif() if("OCTAVE" IN_LIST MWRAP_MEX_BACKENDS) if(MWRAP_OCTAVE_EXECUTABLE) string(REPLACE "'" "''" _mwrap_octave_binary_dir_escaped "${_mwrap_testing_binary_dir}") string(REPLACE "'" "''" _mwrap_octave_source_dir_escaped "${_mwrap_testing_source_dir}") set(_mwrap_octave_eval "addpath('${_mwrap_octave_binary_dir_escaped}'); addpath('${_mwrap_octave_source_dir_escaped}'); test_all; exit;") add_test(NAME mwrap.octave.test_all COMMAND ${MWRAP_OCTAVE_EXECUTABLE} --quiet --no-init-file --eval "${_mwrap_octave_eval}") set_tests_properties(mwrap.octave.test_all PROPERTIES WORKING_DIRECTORY "${_mwrap_testing_binary_dir}") else() message(WARNING "Octave backend enabled but no Octave interpreter was found; skipping Octave runtime tests.") endif() endif() endif() if(CMAKE_CONFIGURATION_TYPES) add_custom_target(mwrap_tests COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $ WORKING_DIRECTORY ${PROJECT_BINARY_DIR} ) else() add_custom_target(mwrap_tests COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure WORKING_DIRECTORY ${PROJECT_BINARY_DIR} ) endif() add_dependencies(mwrap_tests mwrap) if(TARGET mwrap_testing_mex) add_dependencies(mwrap_tests mwrap_testing_mex) endif() set_property(TARGET mwrap_tests PROPERTY FOLDER "tests") zgimbutas-mwrap-04cdb66/testing/Makefile000066400000000000000000000100121522673526200203760ustar00rootroot00000000000000include ../make.inc MWRAP = ../mwrap all: test_transfers test_cpp_complex $(TESTC99COMPLEX) test_syntax \ test_typecheck test_catch test_fortran1 test_fortran2 \ test_redirect test_include test_single_cpp test_char_cpp # run the tests... octave-cli --no-init-file --quiet test_all.m test_transfers: $(MWRAP) -mex test_transfersmex \ -c test_transfersmex.cc \ -m test_transfers.m test_transfers.mw $(MEX) test_transfersmex.cc test_cpp_complex: $(MWRAP) -cppcomplex \ -mex test_cpp_complexmex \ -c test_cpp_complexmex.cc \ -m test_cpp_complex.m test_cpp_complex.mw $(MEX) test_cpp_complexmex.cc test_c99_complex: $(MWRAP) -c99complex \ -mex test_c99_complexmex \ -c test_c99_complexmex.c \ -m test_c99_complex.m test_c99_complex.mw $(MEX) test_c99_complexmex.c # The following two are designed to create mwrap errors, whose stderr is # compared against reference files. The minus sign only ignores mwrap's # (intentional) nonzero exit status; the diff still gates the test. # Bison >= 3.6 says "end of file" where older versions said "$end", so the # log is normalized before comparison (as in CMake and test_python.sh). test_syntax: - $(MWRAP) -cppcomplex test_syntax.mw 2> test_syntax.log sed 's/end of file/$$end/g' test_syntax.log | diff - test_syntax.ref test_typecheck: - $(MWRAP) -cppcomplex test_typecheck.mw 2> test_typecheck.log diff test_typecheck.log test_typecheck.ref test_catch: $(MWRAP) -mex test_catchmex -catch \ -c test_catchmex.cc \ -m test_catch.m \ test_catch.mw $(MEX) test_catchmex.cc test_fortran1: $(MWRAP) -mex test_fortran1mex \ -c test_fortran1mex.cc \ -m test_fortran1.m \ test_fortran1.mw $(MEX) test_fortran1mex.cc test_fortran2: $(MWRAP) -mex test_fortran2mex \ -c test_fortran2mex.c \ -m test_fortran2.m \ test_fortran2.mw $(MEX) test_fortran2mex.c test_redirect: $(MWRAP) -mb test_redirect.mw test_include: $(MWRAP) -mex test_includemex\ -c test_includemex.cc \ -m test_include.m test_include.mw $(MEX) test_includemex.cc # these two are tested by test_char.m ... test_char_cpp: $(MWRAP) -cppcomplex -mex test_charmex \ -c test_charmex.cc \ -mb test_char.mw $(MEX) test_charmex.cc test_char_c99: $(MWRAP) -mex test_charmex \ -c test_charmex.c \ -mb test_char.mw $(MEX) test_charmex.c # these last two are tested by test_single.m ... test_single_cpp: $(MWRAP) -cppcomplex -mex test_singlemex \ -c test_singlemex.cc \ -mb test_single.mw $(MEX) test_singlemex.cc test_single_c99: $(MWRAP) -c99complex -mex test_singlemex \ -c test_singlemex.c \ -mb test_single.mw $(MEX) test_singlemex.c test_gpu: $(MWRAP) -gpu -list -cppcomplex -mb -mex test_gpu -c test_gpu.cu test_gpu.mw test_gpu_complex: $(MWRAP) -gpu -list -cppcomplex -mb -mex test_gpu_complex -c test_gpu_complex.cu test_gpu_complex.mw test_gpu_int32: $(MWRAP) -gpu -list -cppcomplex -mb -mex test_gpu_int32 -c test_gpu_int32.cu test_gpu_int32.mw # GPU tests: requires CUDA toolkit and GPU device # Run mwrap targets first (test_gpu, test_gpu_complex, test_gpu_int32), # then run this target from MATLAB/Octave with GPU support. test_all_gpu: test_gpu test_gpu_complex test_gpu_int32 matlab -batch "test_all_gpu" test_cpu: $(MWRAP) -list -cppcomplex -mb -mex test_cpu -c test_cpu.cc test_cpu.mw test_cpu_int32: $(MWRAP) -list -cppcomplex -mb -mex test_cpu_int32 -c test_cpu_int32.cc test_cpu_int32.mw clean: rm -f *~ *.mex* *.o* test_typecheck.log test_syntax.log rm -f test_fortran1.m test_fortran2.m test_transfers.m test_catch.m rm -f test_fortran1mex.cc test_fortran2mex.c rm -f test_cpp_complex.m test_c99_complex.m rm -f test_transfersmex.cc test_catchmex.cc test_fortranmex.cc rm -f test_cpp_complexmex.cc test_c99_complexmex.c rm -f test_redirect.m test_redirect1.m rm -f test_singlemex.c test_singlemex.cc rm -f add.m addf.m addz.m addc.m rm -f arradd.m arraddf.m arraddz.m arraddc.m rm -f test_include.m test_includemex.cc rm -f test_charmex.c test_charmex.cc rm -f addchar.m arraddchar.m rm -f test_cpu.cc timestwo_cpu.m rm -f test_cpu_int32.cc timestwo_cpu_int32.m zgimbutas-mwrap-04cdb66/testing/run_matlab_tests.m000066400000000000000000000077221522673526200225000ustar00rootroot00000000000000function run_matlab_tests(build_dir, source_dir) % run_matlab_tests Run all MATLAB tests for mwrap. % % run_matlab_tests(BUILD_DIR, SOURCE_DIR) runs the example and unit tests % using MEX binaries from BUILD_DIR and test scripts from SOURCE_DIR. % % This script is designed for use with matlab-actions/run-command in CI. % It exits with a non-zero status on failure. if nargin < 2 error('Usage: run_matlab_tests(build_dir, source_dir)'); end n_pass = 0; n_fail = 0; failures = {}; %% -- Example tests ---------------------------------------------------------- example_tests = { % {test_name, mex_dir, script_dirs, script_name, needs_cd} {'eventq_plain', fullfile(build_dir, 'example', 'eventq_plain'), ... {fullfile(source_dir, 'example', 'eventq')}, 'testq_plain', false} {'eventq_handle', fullfile(build_dir, 'example', 'eventq_handle'), ... {fullfile(source_dir, 'example', 'eventq')}, 'testq_handle', false} {'eventq_class', fullfile(build_dir, 'example', 'eventq_class'), ... {fullfile(source_dir, 'example', 'eventq')}, 'testq_class', false} {'eventq2', fullfile(build_dir, 'example', 'eventq2'), ... {fullfile(source_dir, 'example', 'eventq2')}, 'testq2', false} {'zlib', fullfile(build_dir, 'example', 'zlib'), ... {fullfile(source_dir, 'example', 'zlib')}, 'testgz', true} {'fem_simple', fullfile(build_dir, 'example', 'fem_interface'), ... {fullfile(source_dir, 'example', 'fem')}, 'test_simple', true} {'fem_patch', fullfile(build_dir, 'example', 'fem_interface'), ... {fullfile(source_dir, 'example', 'fem')}, 'test_patch', true} {'fem_assembler', fullfile(build_dir, 'example', 'fem_interface'), ... {fullfile(source_dir, 'example', 'fem')}, 'test_assembler', true} }; for i = 1:numel(example_tests) t = example_tests{i}; test_name = t{1}; mex_dir = t{2}; script_dirs = t{3}; script_name = t{4}; needs_cd = t{5}; fprintf('\n=== Running example test: %s ===\n', test_name); saved_path = path; saved_dir = pwd; try addpath(mex_dir); for j = 1:numel(script_dirs) addpath(script_dirs{j}); end if needs_cd cd(mex_dir); end feval(script_name); fprintf('PASS: %s\n', test_name); n_pass = n_pass + 1; catch ME fprintf('FAIL: %s -- %s\n', test_name, ME.message); n_fail = n_fail + 1; failures{end+1} = test_name; %#ok end cd(saved_dir); path(saved_path); end %% -- Unit tests (testing/) -------------------------------------------------- testing_mex_dir = fullfile(build_dir, 'testing'); testing_gen_dir = fullfile(build_dir, 'testing', 'octave'); testing_src_dir = fullfile(source_dir, 'testing'); unit_tests = { 'test_transfers' 'test_cpp_complex' 'test_c99_complex' 'test_catch' 'test_fortran1' 'test_fortran2' 'test_redirect' 'test_include' 'test_single' 'test_char' }; for i = 1:numel(unit_tests) test_name = unit_tests{i}; fprintf('\n=== Running unit test: %s ===\n', test_name); saved_path = path; saved_dir = pwd; try addpath(testing_mex_dir); addpath(testing_gen_dir); addpath(testing_src_dir); feval(test_name); fprintf('PASS: %s\n', test_name); n_pass = n_pass + 1; catch ME fprintf('FAIL: %s -- %s\n', test_name, ME.message); n_fail = n_fail + 1; failures{end+1} = test_name; %#ok end cd(saved_dir); path(saved_path); end %% -- Summary ----------------------------------------------------------------- fprintf('\n============================\n'); fprintf('Results: %d passed, %d failed\n', n_pass, n_fail); if n_fail > 0 fprintf('Failures:\n'); for i = 1:numel(failures) fprintf(' - %s\n', failures{i}); end error('mwrap:testFailure', '%d test(s) failed.', n_fail); end fprintf('All tests passed.\n'); end zgimbutas-mwrap-04cdb66/testing/test_all.m000066400000000000000000000002671522673526200207360ustar00rootroot00000000000000test_transfers; test_cpp_complex; if exist('test_c99_complex.m'), test_c99_complex; end test_catch; test_fortran1; test_fortran2; test_redirect; test_include; test_single; test_char; zgimbutas-mwrap-04cdb66/testing/test_all_gpu.m000066400000000000000000000003611522673526200216040ustar00rootroot00000000000000% test_all_gpu - Run all GPU tests. % Requires: CUDA toolkit, mexcuda, GPU device. % Run mwrap first for each test (see individual test files for commands). test_gpu; test_gpu_complex; test_gpu_int32; fprintf('\nAll GPU tests PASSED.\n'); zgimbutas-mwrap-04cdb66/testing/test_c99_complex.mw000066400000000000000000000020021522673526200224750ustar00rootroot00000000000000function test_c99_complex; $ #include $ $ _Complex double zsum(_Complex double* zarray, int n) { $ int i; $ _Complex double sum = 0; $ for (i = 0; i < n; ++i) sum += zarray[i]; $ return sum; $ } zarray = rand(10,1) + 1i*rand(10,1); n = length(zarray); # dcomplex result = zsum(dcomplex[] zarray, int n); tassert(abs(result-sum(zarray)) < 1e-10*norm(zarray), 'C++ complex support'); # dcomplex cresult = conj(dcomplex result); tassert(conj(result) == cresult, 'C++ complex support (2)'); % Real (non-complex) scalar into a dcomplex argument must be accepted rscalar = 3.25; # dcomplex rresult = conj(dcomplex rscalar); tassert(rresult == 3.25, 'real scalar into dcomplex argument'); % Empty array into a dcomplex argument must raise an error, not crash empty_arg = []; caught = false; try # dcomplex eresult = conj(dcomplex empty_arg); catch caught = true; end tassert(caught, 'empty array into dcomplex argument'); function tassert(pred, msg) if ~pred, fprintf('Failure: %s\n', msg); end zgimbutas-mwrap-04cdb66/testing/test_catch.mw000066400000000000000000000003021522673526200214250ustar00rootroot00000000000000function test_catch $ void toss() $ { $ throw("Cookies"); $ } try # toss(); disp('Failed to properly catch exception'); catch fprintf('Correctly caught message: %s\n', lasterr); end zgimbutas-mwrap-04cdb66/testing/test_char.m000066400000000000000000000020631522673526200210770ustar00rootroot00000000000000function test_char % pass-fail test of the char- args and arrays. % must do either make test_char_cpp or make test_char_c99 first. % based on test_single.m, Barnett & Gimbutas 7/5/20-7/20/20. tol = 2e-16; tols = 1e-7; %format long g % for debug %fprintf('scalar char routines...\n') % ------------------------------------- x = 1/3; ce = x+x; xs = single(x); try c = addchar(xs,xs); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid scalar argument, mxCHAR_CLASS expected'))) end a = '1'; b = '2'; ce = a+b; c = addchar(a,b); assert(norm(c-ce) $ using std::complex; $ using std::conj; $ $ complex zsum(complex* zarray, int n) { $ complex sum(0); $ for (int i = 0; i < n; ++i) sum += zarray[i]; $ return sum; $ } zarray = rand(10,1) + 1i*rand(10,1); n = length(zarray); # dcomplex result = zsum(dcomplex[] zarray, int n); tassert(abs(result-sum(zarray)) < 1e-10*norm(zarray), 'C++ complex support'); # dcomplex cresult = conj(dcomplex result); tassert(conj(result) == cresult, 'C++ complex support (2)'); % Real (non-complex) scalar into a dcomplex argument must be accepted rscalar = 3.25; # dcomplex rresult = conj(dcomplex rscalar); tassert(rresult == 3.25, 'real scalar into dcomplex argument'); % Empty array into a dcomplex argument must raise an error, not crash empty_arg = []; caught = false; try # dcomplex eresult = conj(dcomplex empty_arg); catch caught = true; end tassert(caught, 'empty array into dcomplex argument'); function tassert(pred, msg) if ~pred, fprintf('Failure: %s\n', msg); end zgimbutas-mwrap-04cdb66/testing/test_cpu.mw000066400000000000000000000004651522673526200211440ustar00rootroot00000000000000$void TimesTwo_cpu(double *A, $ double *B, $ int const N) ${ $ int i; $ for(i = 0; i>>(A, B, N); $} @function result = timestwo(a) n = numel(a) # TimesTwo(gpu double[] a, gpu output double[n] result, int n); end zgimbutas-mwrap-04cdb66/testing/test_gpu_complex.m000066400000000000000000000007441522673526200225100ustar00rootroot00000000000000function test_gpu_complex % Test GPU complex double array passing via gpuArray. % Requires: CUDA toolkit, mexcuda, GPU device. % Run mwrap first: % ../mwrap -gpu -list -cppcomplex -mb -mex test_gpu_complex -c test_gpu_complex.cu test_gpu_complex.mw mexcuda test_gpu_complex.cu a = complex((1:7)', (7:-1:1)'); agpu = gpuArray(a); bgpu = timestwo_complex(agpu); b = gather(bgpu); assert(norm(b - 2*a) < 1e-14, 'GPU complex timestwo failed'); fprintf('test_gpu_complex: PASSED\n'); zgimbutas-mwrap-04cdb66/testing/test_gpu_complex.mw000066400000000000000000000015141522673526200226730ustar00rootroot00000000000000$void __global__ TimesTwoKernel(cuDoubleComplex *A, $ cuDoubleComplex *B, $ int const N) ${ $ /* Calculate the global linear index, assuming a 1-d grid. */ $ int const i = blockDim.x * blockIdx.x + threadIdx.x; $ if (i < N) { $ /* B[i] = make_cuDoubleComplex(2.0,0.0) * A[i]; */ $ B[i] = cuCmul(make_cuDoubleComplex(2.0,0.0),A[i]); $ } $} $void TimesTwo(cuDoubleComplex *A, $ cuDoubleComplex *B, $ int const N) ${ $ int const threadsPerBlock = 256; $ int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock; $ TimesTwoKernel<<>>(A, B, N); $} @function result = timestwo_complex(a) n = numel(a) # TimesTwo(gpu dcomplex[] a, gpu output dcomplex[n] result, int n); end zgimbutas-mwrap-04cdb66/testing/test_gpu_int32.m000066400000000000000000000007171522673526200220000ustar00rootroot00000000000000function test_gpu_int32 % Test GPU int32 array passing via gpuArray. % Requires: CUDA toolkit, mexcuda, GPU device. % Run mwrap first: % ../mwrap -gpu -list -cppcomplex -mb -mex test_gpu_int32 -c test_gpu_int32.cu test_gpu_int32.mw mexcuda test_gpu_int32.cu a = int32((1:7)'); agpu = gpuArray(a); bgpu = timestwo_gpu_int32(agpu); b = gather(bgpu); assert(norm(double(b) - 2*double(a)) == 0, 'GPU int32 timestwo failed'); fprintf('test_gpu_int32: PASSED\n'); zgimbutas-mwrap-04cdb66/testing/test_gpu_int32.mw000066400000000000000000000013211522673526200221570ustar00rootroot00000000000000$void __global__ TimesTwoKernel(int32_t *A, $ int32_t *B, $ int const N) ${ $ /* Calculate the global linear index, assuming a 1-d grid. */ $ int const i = blockDim.x * blockIdx.x + threadIdx.x; $ if (i < N) { $ B[i] = 2.0 * A[i]; $ } $} $void TimesTwo(int32_t *A, $ int32_t *B, $ int const N) ${ $ int const threadsPerBlock = 256; $ int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock; $ TimesTwoKernel<<>>(A, B, N); $} @function result = timestwo_gpu_int32(a) n = numel(a) # TimesTwo(gpu int32_t[] a, gpu output int32_t[n] result, int n); end zgimbutas-mwrap-04cdb66/testing/test_include.mw000066400000000000000000000003421522673526200217720ustar00rootroot00000000000000function test_include() @include test_include2.mw tassert(j == 4, 'Include test'); % ================================================================ function tassert(pred, msg) if ~pred, fprintf('Failure: %s\n', msg); end zgimbutas-mwrap-04cdb66/testing/test_include2.mw000066400000000000000000000000701522673526200220520ustar00rootroot00000000000000$int add2(int i) { return i+2; } # int j = add2(int 2); zgimbutas-mwrap-04cdb66/testing/test_include3.mw000066400000000000000000000000701522673526200220530ustar00rootroot00000000000000$int add3(int i) { return i+3; } # int k = add3(int 3); zgimbutas-mwrap-04cdb66/testing/test_lexer_edge.mw000066400000000000000000000014161522673526200224550ustar00rootroot00000000000000% Lexer edge cases pinned for C++/Python parity (test_python.sh only; % the generated C is compared byte-for-byte but never compiled). function test_lexer_edge() % The very first parsed call is duplicated immediately (pins add_func's % first-occurrence registration) and again further down (pins the full % same_next chain in "Also at" comments and the profiler report). # double s = dupfirst(double a); # double s = dupfirst(double a); $[ /* block C section closed by an indented terminator */ $] $[ /* one-line $[ is not a block start; this goes through the $ rule */ $] @include test_include2.mw test_include3.mw % Post-include declaration: pins file/line provenance after an @include # double[n] y = scalevec(double[m] x, int n, int m); # double s = dupfirst(double a); zgimbutas-mwrap-04cdb66/testing/test_python.sh000077500000000000000000000127751522673526200216770ustar00rootroot00000000000000#!/usr/bin/env bash # # test_python.sh — test the Python mwrap port against reference files # and against C++ mwrap output. # # Usage: # bash testing/test_python.sh # # Example: # bash testing/test_python.sh build/mwrap python/mwrap set -euo pipefail if [ $# -ne 2 ]; then echo "Usage: $0 " exit 1 fi MWRAP_CPP="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" MWRAP_PY="$(cd "$(dirname "$2")" && pwd)/$(basename "$2")" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" TMPDIR_BASE="$(mktemp -d)" trap 'rm -rf "$TMPDIR_BASE"' EXIT PASS=0 FAIL=0 ERRORS="" pass() { PASS=$((PASS + 1)) echo " PASS: $1" } fail() { FAIL=$((FAIL + 1)) ERRORS="${ERRORS} FAIL: $1\n" echo " FAIL: $1" } # ---------------------------------------------------------------- # Group A: Reference-file tests # Run from the testing directory with relative .mw paths so that # error messages use basenames (matching the .ref files). # ---------------------------------------------------------------- echo "=== Group A: Reference-file tests ===" run_ref_test() { local name="$1" local mw_basename="$2" local ref_file="$3" shift 3 local flags=("$@") local tmpout="$TMPDIR_BASE/${name}.stderr" # Run Python mwrap from SCRIPT_DIR with relative .mw path; expect nonzero exit if (cd "$SCRIPT_DIR" && "$MWRAP_PY" "${flags[@]}" "$mw_basename" 2>"$tmpout") ; then fail "$name (expected nonzero exit, got 0)" return fi # Normalize "end of file" -> "$end" to match Bison 3.x convention sed -i.bak 's/end of file/$end/g' "$tmpout" if diff -u "$ref_file" "$tmpout" >/dev/null 2>&1; then pass "$name" else fail "$name (stderr differs from reference)" diff -u "$ref_file" "$tmpout" || true fi } run_ref_test test_syntax \ test_syntax.mw \ "$SCRIPT_DIR/test_syntax.ref" \ -cppcomplex run_ref_test test_typecheck \ test_typecheck.mw \ "$SCRIPT_DIR/test_typecheck.ref" \ -cppcomplex # ---------------------------------------------------------------- # Group B: Output equivalence tests (Python vs C++) # ---------------------------------------------------------------- echo "" echo "=== Group B: Output equivalence tests ===" run_equiv_test() { local name="$1" local mw_file="$2" local cc_ext="$3" # .cc or .c local gen_m="$4" # "yes" or "no" shift 4 local flags=() if [ $# -gt 0 ]; then flags=("$@") fi local cpp_dir="$TMPDIR_BASE/cpp_${name}" local py_dir="$TMPDIR_BASE/py_${name}" mkdir -p "$cpp_dir" "$py_dir" local mex_name="${name}mex" local cc_file="${mex_name}${cc_ext}" # Copy included .mw files if present (for @include) for inc in test_include2.mw test_include3.mw; do if [ -f "$SCRIPT_DIR/$inc" ]; then cp "$SCRIPT_DIR/$inc" "$cpp_dir/" cp "$SCRIPT_DIR/$inc" "$py_dir/" fi done local cpp_args=(-mex "$mex_name" -c "$cc_file") local py_args=(-mex "$mex_name" -c "$cc_file") if [ "$gen_m" = "yes" ]; then cpp_args+=(-m "${name}.m") py_args+=(-m "${name}.m") else cpp_args+=(-mb) py_args+=(-mb) fi if [ ${#flags[@]} -gt 0 ]; then cpp_args+=("${flags[@]}") py_args+=("${flags[@]}") fi cpp_args+=("$mw_file") py_args+=("$mw_file") # Run C++ mwrap if ! (cd "$cpp_dir" && "$MWRAP_CPP" "${cpp_args[@]}" 2>/dev/null); then fail "$name (C++ mwrap failed)" return fi # Run Python mwrap if ! (cd "$py_dir" && "$MWRAP_PY" "${py_args[@]}" 2>/dev/null); then fail "$name (Python mwrap failed)" return fi # Compare generated C/C++ file if diff -u "$cpp_dir/$cc_file" "$py_dir/$cc_file" >/dev/null 2>&1; then pass "$name ($cc_file)" else fail "$name ($cc_file differs)" diff -u "$cpp_dir/$cc_file" "$py_dir/$cc_file" | head -40 || true fi # Compare generated .m file (if applicable) if [ "$gen_m" = "yes" ]; then if diff -u "$cpp_dir/${name}.m" "$py_dir/${name}.m" >/dev/null 2>&1; then pass "$name (${name}.m)" else fail "$name (${name}.m differs)" diff -u "$cpp_dir/${name}.m" "$py_dir/${name}.m" | head -40 || true fi fi } run_equiv_test test_transfers \ "$SCRIPT_DIR/test_transfers.mw" .cc yes run_equiv_test test_cpp_complex \ "$SCRIPT_DIR/test_cpp_complex.mw" .cc yes \ -cppcomplex run_equiv_test test_c99_complex \ "$SCRIPT_DIR/test_c99_complex.mw" .c yes \ -c99complex run_equiv_test test_catch \ "$SCRIPT_DIR/test_catch.mw" .cc yes \ -catch run_equiv_test test_fortran1 \ "$SCRIPT_DIR/test_fortran1.mw" .cc yes run_equiv_test test_fortran2 \ "$SCRIPT_DIR/test_fortran2.mw" .c yes run_equiv_test test_include \ "$SCRIPT_DIR/test_include.mw" .cc yes run_equiv_test test_single \ "$SCRIPT_DIR/test_single.mw" .cc no \ -cppcomplex run_equiv_test test_char \ "$SCRIPT_DIR/test_char.mw" .cc no \ -cppcomplex run_equiv_test test_lexer_edge \ "$SCRIPT_DIR/test_lexer_edge.mw" .cc yes run_equiv_test test_i8 \ "$SCRIPT_DIR/test_transfers.mw" .cc yes \ -i8 # ---------------------------------------------------------------- # Summary # ---------------------------------------------------------------- echo "" echo "=== Summary ===" echo " Passed: $PASS" echo " Failed: $FAIL" if [ $FAIL -ne 0 ]; then echo "" echo "Failures:" echo -e "$ERRORS" exit 1 fi exit 0 zgimbutas-mwrap-04cdb66/testing/test_redirect.mw000066400000000000000000000002111522673526200221430ustar00rootroot00000000000000@function test_redirect if test_redirect1(42) ~= 42, fprint('Failure: Redirection failed?'); end @function x = test_redirect1(y) x = y; zgimbutas-mwrap-04cdb66/testing/test_single.m000066400000000000000000000060711522673526200214460ustar00rootroot00000000000000function test_single % pass-fail test of the single- and double-precision args and arrays. % must do either make test_single_cpp or make test_single_c99 first. % Barnett & Gimbutas. 7/20/20 tol = 2e-16; tols = 1e-7; %format long g % for debug %fprintf('scalar real routines...\n') % ------------------------------------- x = 1/3; ce = x+x; xf = single(x); c = add(x,x); assert(abs(c-ce)tol) % test it's not doing double-prec! assert(strcmp(class(c),'single')) %fprintf('\narray real routines...\n') % ------------------------------------ x = x*ones(3,1); xf = xf*ones(3,1); ce=x+x; c = arradd(x,x); assert(norm(c-ce)double, as mwrap 0.33.3 designed for! try c = arradd(xf,xf); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid array argument, mxDOUBLE_CLASS expected'))); end try c = arraddf(x,x); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid array argument, mxSINGLE_CLASS expected'))); end c = arraddf(xf,xf); % input as designed, should give single assert(norm(double(c)-ce)tol) % test it's not doing double-prec! assert(strcmp(class(c),'single')) %fprintf('\nscalar complex routines...\n') % ------------------------------- z = (1+2i)/3; zf = single(z); ce = z+z; c = addz(z,z); assert(abs(c-ce)double, as mwrap 0.33.3 designed for! try c = addz(zf,zf); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid scalar argument, mxDOUBLE_CLASS expected'))); end try c = addc(z,z); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid scalar argument, mxSINGLE_CLASS expected'))); end c = addc(zf,zf); % input as designed, should give single assert(abs(double(c)-ce)tol) % test it's not doing double-prec! assert(strcmp(class(c),'single')) % fprintf('\narray complex routines...\n') % -------------------------------- z = z*ones(3,1); zf = zf*ones(3,1); ce = z+z; c = arraddz(z,z); % input as designed, double assert(norm(c-ce)double, as mwrap 0.33.3 designed for! try c = arraddz(zf,zf); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid array argument, mxDOUBLE_CLASS expected'))); end try c = arraddc(z,z); % should error catch ME assert(~isempty(strfind(ME.message,'Invalid array argument, mxSINGLE_CLASS expected'))); end c = arraddc(zf,zf); % input as designed, should give single assert(norm(double(c)-ce)tol) % test it's not doing double-prec! assert(strcmp(class(c),'single')) zgimbutas-mwrap-04cdb66/testing/test_single.mw000066400000000000000000000034751522673526200216420ustar00rootroot00000000000000% test double and single precision, scalars and arrays, in mwrap. % Barnett & Gimbutas 7/5/20-7/20/20. % make as in Makefile, % then pass-fail test in octave/matlab with: % test_single.m % the old non-pass-fail is here: % test_single_humanreadable.m % REAL====================================================== % scalar real......... $ void add(double a, double b, double *c) { *c = a + b; } @function c=add(a,b) # add(double a, double b, output double[1]c); $ void addf(float a, float b, float *c) { *c = a + b; } @function c=addf(a,b) # addf(float a, float b, output float[1]c); % array real........ $ void arradd(double *a, double *b, double *c, int n) $ { for (int i=0;idouble, as mwrap 0.33.3 designed for! try c = arradd(xf,xf) % should error catch ME ME.message disp('good') end try c = arraddf(x,x), class(c) % should error catch ME ME.message disp('good') end c = arraddf(xf,xf) % input as designed, should give single fprintf('\nscalar complex routines...\n') z = (1+2i)/3; zf = single(z); c = addz(z,z), class(c) try c = addz(zf,zf) % should error catch ME ME.message disp('good') end try c = addc(z,z), class(c) % should error catch ME ME.message disp('good') end c = addc(zf,zf) % input as designed, should give single fprintf('\narray complex routines...\n') z = z*ones(3,1); zf = zf*ones(3,1); try c = arraddz(z,z), class(c) % input as designed, double catch ME ME.message disp('****** bad') end try c = arraddz(zf,zf) % should error catch ME ME.message disp('good') end try c = arraddc(z,z), class(c) % should error catch ME ME.message disp('good') end try c = arraddc(zf,zf) % input as designed, should give single catch ME ME.message disp('****** bad') end zgimbutas-mwrap-04cdb66/testing/test_syntax.mw000066400000000000000000000003071522673526200216760ustar00rootroot00000000000000 function badmojo # double z = sumpair(double x) // Missing semicolon should result in error // Another line just to double check lines # double z = sumpair(double x); # double z = sumpair(double x) zgimbutas-mwrap-04cdb66/testing/test_syntax.ref000066400000000000000000000002401522673526200220230ustar00rootroot00000000000000Parse error (test_syntax.mw:4): syntax error, unexpected NON_C_LINE, expecting ';' Parse error (test_syntax.mw:8): syntax error, unexpected $end, expecting ';' zgimbutas-mwrap-04cdb66/testing/test_transfers.mw000066400000000000000000000362071522673526200223670ustar00rootroot00000000000000function test_transfers test_mult_inherit; test_scopes; test_literals; test_types; test_complex; test_nulls; test_method; test_returns; test_inputs; test_outputs; test_inouts; test_mx; test_const; test_struct; % ================================================================ $[ #include #include struct Pair { Pair(double x, double y) { xy[0] = x; xy[1] = y; } double xy[2]; double x() { return xy[0]; } double y() { return xy[1]; } }; struct BadPair { double x; }; struct DerivedPair : public Pair { DerivedPair() : Pair(7,11) {} }; #include typedef std::complex cmplx; #define real_cmplx(z) (z).real() #define imag_cmplx(z) (z).imag() #define setz_cmplx(zp, r, i) *zp = cmplx(r,i) typedef unsigned char uchar; typedef unsigned long ulong; $] % ================================================================ function test_mult_inherit $[ class Parent1 { public: Parent1(int data) : data1_(data) {} virtual ~Parent1() {} virtual int data1() { return data1_; } protected: int data1_; }; class Parent2 { public: Parent2(int data) : data2_(data) {} virtual ~Parent2() {} virtual int data2() { return data2_; } protected: int data2_; }; class Child : public Parent1, public Parent2 { public: Child() : Parent1(1), Parent2(2) {} virtual int data1() { return data1_ + 1; } virtual int data2() { return data2_ + 1; } int datas() { return data1_ + data2_; } }; $] # class Child : Parent1, Parent2; # Child* c = new Child(); # int d1 = c->Parent1.data1(); # int d2 = c->Parent2.data2(); # int dd = c->Child.datas(); tassert(d1 == 2, 'Multiple inheritance handling (1)'); tassert(d2 == 3, 'Multiple inheritance handling (2)'); tassert(dd == 3, 'Multiple inheritance handling (3)'); % ================================================================ function test_scopes; $ class OuterClass { $ public: $ static int static_method() { return 123; } $ }; # int x = OuterClass::static_method(); tassert(x == 123, 'Access to static class method'); % ================================================================ function test_literals; $ int literal_plus1(int x) { return x+1; } # int y = literal_plus1(int 7); # int l = strlen(cstring 'Test'); tassert(y == 8, 'Integer literals'); tassert(l == 4, 'String literals'); % ================================================================ function test_types; $ typedef unsigned char byte; $ void takes_double(double& x) {} $ void takes_float(float& x) {} $ void takes_long(long& x) {} $ void takes_int(int& x) {} $ void takes_char(char& x) {} $ void takes_ulong(unsigned long& x) {} $ void takes_uint(unsigned int& x) {} $ void takes_uchar(unsigned char& x) {} $ void takes_bool(bool& x) {} $ void takes_size_t(size_t& x) {} x = 0; xs = single(x); xc=char(42); # typedef numeric byte; # takes_double(double& x); # takes_float(float& xs); # takes_long(long& x); # takes_int(int& x); # takes_char(char& xc); # takes_ulong(ulong& x); # takes_uint(uint& x); # takes_uchar(uchar& x); # takes_uchar(byte& x); # takes_bool(bool& x); # takes_size_t(size_t& x); # class DerivedPair : Pair; # DerivedPair* dp = new DerivedPair(); # double x = dp->Pair.x(); tassert(x == 7, 'Type casting'); % ================================================================ function test_complex; $ cmplx zsum(cmplx* zarray, int n) { $ cmplx sum(0); $ for (int i = 0; i < n; ++i) sum += zarray[i]; $ return sum; $ } zarray = rand(10,1) + 1i*rand(10,1); n = length(zarray); # typedef dcomplex cmplx; # cmplx result = zsum(cmplx[] zarray, int n); tassert(abs(result-sum(zarray)) < 1e-10*norm(zarray), 'Complex support'); % ================================================================ function test_nulls; $ Pair* null_pair() { return NULL; } # Pair* p = null_pair(); tassert(p == 0, 'Null pointer return'); $ int is_null(Pair* p) { return !p; } # int flag = is_null(Pair* p); tassert(flag, 'Null pointer input'); $ char* null_string() { return NULL; } # cstring s = null_string(); tassert(s == 0, 'Null string return'); # char* c = null_string(); tassert(isempty(c), 'Null scalar pointer return'); nil = []; $ int is_null(double* data) { return !data; } # int flag = is_null(double[] nil); tassert(flag, 'Null array input'); # char[1] ca = null_string(); tassert(isempty(ca), 'Null array return'); $ void test_null_obj(Pair& p) { } try # test_null_obj(Pair p); tassert(0, 'Null argument dereference 1'); end try # test_null_obj(Pair& p); tassert(0, 'Null argument dereference 1'); end try # double x = p->Pair.x(); tassert(0, 'Invalid this test'); end # BadPair* bp = new BadPair(); try $ void test_bad_pair(Pair* p) { } # test_bad_pair(Pair* bp); tassert(0, 'Invalid pointer test'); end # delete(BadPair* bp); % ================================================================ function test_method; x = 1; y = 2; # Pair* p = new Pair(double x, double y); # double xx = p->Pair.x(); # double yy = p->Pair.y(); # delete(Pair* p); tassert(xx == 1, 'Method call'); tassert(yy == 2, 'Method call'); % ================================================================ function test_returns; $ Pair test_return_obj() { return Pair(1.5, 2.5); } # Pair p1 = test_return_obj(); tassert(sscanf(p1, 'Pair:%x') > 0, 'Return object'); $ double* test_return_array(Pair& p) { return p.xy; } # double[2] xy = test_return_array(Pair& p1); tassert(norm(xy-[1.5; 2.5]) == 0, 'Return array'); $ double* test_return_array2(Pair& p) { return p.xy; } # double xy[2] = test_return_array2(Pair& p1); tassert(norm(xy-[1.5; 2.5]) == 0, 'Return array'); $ double test_return_scalar(double* xy) { return xy[0] + xy[1]; } # double sum = test_return_scalar(double[] xy); tassert(sum == 4, 'Return scalar'); xy_z = [1+5i, 7+11i]; $ cmplx test_return_zscalar(cmplx* xy) { return xy[0] + xy[1]; } # cmplx sum1 = test_return_zscalar(cmplx[] xy); # cmplx sum2 = test_return_zscalar(cmplx[] xy_z); tassert(sum1 == 4, 'Return zscalar (reals)'); tassert(sum2 == 8+16i, 'Return zscalar (complexes)'); $ const char* test_return_string() { return "Hello, world!"; } # cstring s = test_return_string(); tassert(strcmp(s, 'Hello, world!'), 'Return string'); $ Pair* test_return_p_obj() { return new Pair(3, 5); } # Pair* p2 = test_return_p_obj(); # double[2] xy = test_return_array(Pair& p2); tassert(norm(xy - [3;5]) == 0, 'Return obj*'); a = 7; b = 11; $ int* test_return_p_scalar(int* a, int* b) { return (*a > *b) ? a : b; } # int* z1 = test_return_p_scalar(int* a, int* b); tassert(z1 == 11, 'Return scalar*'); a_z = 7 + 10i; b_z = 11 + 15i; $ cmplx* test_return_p_zscalar(cmplx* a, cmplx* b) { $ return (a->real() > b->real()) ? a : b; $ } # cmplx* z1 = test_return_p_zscalar(cmplx* a, cmplx* b); # cmplx* z2 = test_return_p_zscalar(cmplx* a_z, cmplx* b_z); tassert(z1 == 11, 'Return zscalar*'); tassert(z2 == 11 + 15i, 'Return zscalar*'); $ Pair& test_return_r_obj(Pair& p) { return p; } # Pair& p2c = test_return_r_obj(Pair& p2); tassert(strcmp(p2, p2c), 'Return obj&'); $ int& test_return_r_scalar(int& a, int& b) { return (a > b) ? a : b; } # int& z2 = test_return_r_scalar(int& a, int& b); tassert(z2 == 11, 'Return scalar&'); $ cmplx& test_return_r_zscalar(cmplx& a, cmplx& b) { $ return (a.real() > b.real()) ? a : b; $ } # cmplx& z2 = test_return_r_zscalar(cmplx& a, cmplx& b); # cmplx& z3 = test_return_r_zscalar(cmplx& a_z, cmplx& b_z); tassert(z2 == 11, 'Return zscalar&'); tassert(z3 == 11 + 15i, 'Return zscalar&'); # delete(Pair* p1); # delete(Pair* p2); % ================================================================ function test_inputs x = 101; y = 202; # Pair* p = new Pair(double x, double y); $ double test_input_obj(Pair p) { return p.xy[0] + p.xy[1]; } # double sum = test_input_obj(input Pair p); tassert(sum == 303, 'Input obj'); xy = [11, 22]; $ double test_input_array(double* xy) { return xy[0] + xy[1]; } # double sum = test_input_array(double[2] xy); tassert(sum == 33, 'Input array'); $ double test_input_array2(double* xy) { return xy[0] + xy[1]; } # double sum = test_input_array2(double xy[2]); tassert(sum == 33, 'Input array'); xy_z = [11 + 5i, 22 + 6i]; $ cmplx test_input_zarray(cmplx* xy) { return xy[0] + xy[1]; } # cmplx sum = test_input_zarray(cmplx[2] xy); # cmplx sum2 = test_input_zarray(cmplx[2] xy_z); tassert(sum == 33, 'Input zarray'); tassert(sum2 == 33 + 11i, 'Input zarray'); $ int test_input_scalar(int x) { return x+1; } # int xp1 = test_input_scalar(int x); tassert(xp1 == 102, 'Input scalar'); x_z = 101 + 99i; $ cmplx test_input_zscalar(cmplx x) { return x+1.0; } # cmplx xp1 = test_input_zscalar(cmplx x); # cmplx xp1z = test_input_zscalar(cmplx x_z); tassert(xp1 == 102, 'Input zscalar'); tassert(xp1z == 102 + 99i, 'Input zscalar'); msg = 'Hello, world!'; $ int test_input_string(char* s) { return strlen(s); } # int msglen = test_input_string(cstring msg); tassert(msglen == length(msg), 'Input string'); $ double test_input_p_obj(Pair* p) { return p->xy[0] + p->xy[1]; } # double sum2 = test_input_p_obj(Pair* p); tassert(sum2 == 303, 'Input obj*'); $ int test_input_p_scalar(int* x) { return *x+1; } # int xp1b = test_input_p_scalar(int* x); tassert(xp1b == 102, 'Input scalar*'); $ cmplx test_input_p_zscalar(cmplx* x) { return *x+1.0; } # cmplx xp1b = test_input_p_zscalar(cmplx* x); # cmplx xp1c = test_input_p_zscalar(cmplx* x_z); tassert(xp1b == 102, 'Input zscalar*'); tassert(xp1c == 102 + 99i, 'Input zscalar*'); $ double test_input_r_obj(Pair& p) { return p.xy[0] + p.xy[1]; } # double sum3 = test_input_r_obj(Pair& p); tassert(sum3 == 303, 'Input obj&'); $ int test_input_r_scalar(int& x) { return x+1; } # int xp1c = test_input_r_scalar(input int& x); tassert(xp1c == 102, 'Input scalar&'); $ cmplx test_input_r_zscalar(cmplx& x) { return x+1.0; } # cmplx xp1c = test_input_r_zscalar(input cmplx& x); # cmplx xp1d = test_input_r_zscalar(input cmplx& x_z); tassert(xp1c == 102, 'Input scalar&'); tassert(xp1d == 102 + 99i, 'Input scalar&'); # delete(input Pair* p); % ================================================================ function test_outputs $ void test_output_array(double* xy) { xy[0] = 1; xy[1] = 2; } # test_output_array(output double[2] xy); tassert(norm(xy-[1;2]) == 0, 'Output array'); $ void test_output_rarray(const double*& xy) { $ static double result[2] = {7, 11}; $ xy = result; $ } # test_output_rarray(output double[2]& xyr); tassert(norm(xyr-[7;11]) == 0, 'Output rarray'); $ void test_output_rarray2(const double*& xy) { xy = NULL; } # test_output_rarray2(output double[2]& xyr2); tassert(isempty(xyr2), 'Output rarray'); $ void test_output_zarray(cmplx* xy) { xy[0] = 1; xy[1] = 2; } $ void test_output_zarray2(cmplx* xy) { xy[0] = cmplx(1,3); xy[1] = 2; } # test_output_zarray(output cmplx[2] xy); # test_output_zarray2(output cmplx[2] xy_z); tassert(norm(xy-[1;2]) == 0, 'Output array'); tassert(norm(xy_z-[1+3i;2]) == 0, 'Output array'); fmt = '= %d'; i = 101; # snprintf(output cstring[128] buf, input int 128, input cstring fmt, input int i); tassert(strcmp('= 101', buf), 'Output string'); $ void test_output_p_scalar(int* i) { *i = 202; } # test_output_p_scalar(output int* i2); tassert(i2 == 202, 'Output scalar*'); $ void test_output_p_zscalar(cmplx* z) { *z = cmplx(202,303); } # test_output_p_zscalar(output cmplx* z2); tassert(z2 == 202+303i, 'Output zscalar*'); $ void test_output_r_scalar(int& i) { i = 303; } # test_output_r_scalar(output int& i3); tassert(i3 == 303, 'Output scalar&'); $ void test_output_r_zscalar(cmplx& z) { z = cmplx(303,404); } # test_output_r_zscalar(output cmplx& z3); tassert(z3 == 303+404i, 'Output zscalar&'); % ================================================================ function test_inouts xy = [1, 2]; $ void test_inout_array(double* xy) { xy[0] += 1; xy[1] += 1; } # test_inout_array(inout double[] xy); tassert(norm(xy - [2,3]) == 0, 'Inout array'); s1 = 'foo'; s2 = 'bar'; # strcat(inout cstring[128] s1, input cstring s2); tassert(strcmp(s1, 'foobar'), 'Inout string'); i1 = 101; $ void test_inout_p_scalar(int* i) { *i += 202; } # test_inout_p_scalar(inout int* i1); tassert(i1 == 303, 'Inout scalar*'); i2 = 101; $ void test_inout_r_scalar(int& i) { i += 303; } # test_inout_r_scalar(inout int& i2); tassert(i2 == 404, 'Inout scalar&'); % ================================================================ function test_mx $ #include in1 = 42; $ double test_mx_input(const mxArray* x) { return *mxGetPr(x); } # double out1 = test_mx_input(input mxArray in1); tassert(out1 == 42, 'Input mx'); $ void test_mx_output(mxArray** x) $ { $ *x = mxCreateString("foobar"); $ } # test_mx_output(output mxArray out2); tassert(strcmp(out2, 'foobar'), 'Output mx'); $ mxArray* test_mx_return() $ { $ mxArray* m = mxCreateDoubleMatrix(1,1, mxREAL); $ *mxGetPr(m) = 42; $ return m; $ } # mxArray out3 = test_mx_return(); tassert(out3 == 42, 'Return mx'); % ================================================================ function test_const $ const int TEST_CONST = 42; $ int identity(int i) { return i; } # int result = identity(const TEST_CONST); tassert(result == 42, 'Constant transfer'); # int result2 = identity(const 'TEST_CONST'); tassert(result2 == 42, 'Constant transfer'); % ================================================================ function test_struct $[ struct my_struct_t { double x; double y; }; int my_struct_allocs = 0; int get_my_struct_allocs() { return my_struct_allocs; } my_struct_t* mxWrapGet_my_struct_t(const mxArray* a, const char** e) { // Note -- there really ought to be an error check here ++my_struct_allocs; my_struct_t* o = new my_struct_t; o->x = mxGetPr(a)[0]; o->y = mxGetPr(a)[1]; return o; } mxArray* mxWrapSet_my_struct_t(my_struct_t* o) { mxArray* a = mxCreateDoubleMatrix(2,1,mxREAL); mxGetPr(a)[0] = o->x; mxGetPr(a)[1] = o->y; return a; } my_struct_t* mxWrapAlloc_my_struct_t() { ++my_struct_allocs; return new my_struct_t; } void mxWrapFree_my_struct_t(my_struct_t* o) { --my_struct_allocs; delete o; } void unpack_struct(my_struct_t& o, double* xy) { xy[0] = o.x; xy[1] = o.y; } void pack_struct(my_struct_t& o, double* xy) { o.x = xy[0]; o.y = xy[1]; } void swap_struct(my_struct_t& o) { double tmp = o.x; o.x = o.y; o.y = tmp; } my_struct_t& rightmost(my_struct_t& p1, my_struct_t& p2) { return (p1.x >= p2.x) ? p1 : p2; } my_struct_t* add1(my_struct_t& o) { o.x += 1; o.y += 1; return &o; } $] # typedef mxArray my_struct_t; xy1 = [1, 2]; # unpack_struct(my_struct_t& xy1, output double xy2[2]); tassert(norm(xy2-[1;2]) == 0, 'Structure conversion on input'); # pack_struct(output my_struct_t xy3, double[] xy1); tassert(norm(xy3-[1;2]) == 0, 'Structure conversion on output'); xy4 = [3; 4]; # swap_struct(inout my_struct_t xy4); tassert(norm(xy4-[4; 3]) == 0, 'Structure on inout'); # my_struct_t& result = rightmost(my_struct_t& xy1, my_struct_t& xy4); tassert(norm(result-[4;3]) == 0, 'Structure on reference return'); # my_struct_t* xy5 = add1(my_struct_t& xy4); tassert(norm(xy5-[5;4]) == 0, 'Structure on pointer return'); # int alloc_count = get_my_struct_allocs(); tassert(alloc_count == 0, 'Balanced allocations in structure management'); % ================================================================ function tassert(pred, msg) if ~pred, fprintf('Failure: %s\n', msg); end zgimbutas-mwrap-04cdb66/testing/test_typecheck.mw000066400000000000000000000014131522673526200223260ustar00rootroot00000000000000 function badmojo # double z = sumpair(inout Pair p); # double z = sumpair(output Pair p); # dble(inout int y); # dble(output int y); # strcat(inout cstring s1, cstring s2); # strcat(output cstring s1, cstring s2); # double z = sumpair(inout Pair& p); # double z = sumpair(inout Pair* p); # double z = sumpair(output Pair& p); # double z = sumpair(output Pair* p); # get34(output int[] z); # get34(output fcomplex[] z); # get34(output dcomplex[] z); # get(output int[1,2,3] z); # scale(gpu double x); # scale(gpu output double* y); # scale2(gpu cstring s); # scale3(inout double[]& x); # typedef bozo byte; %# An okay line -- should be ignored, since it doesn't start with # # // Another okay line -- comments should be ignored // Another okay line -- comments should be ignored zgimbutas-mwrap-04cdb66/testing/test_typecheck.ref000066400000000000000000000015031522673526200224570ustar00rootroot00000000000000Error (3): Object p cannot be output Error (4): Object p cannot be output Error (5): Scalar y cannot be output Error (6): Scalar y cannot be output Error (7): String s1 cannot be output without size Error (8): String s1 cannot be output without size Error (9): Object p cannot be output Error (10): Object p cannot be output Error (11): Object p cannot be output Error (12): Object p cannot be output Error (13): Output array z must have dims Error (14): Output array z must have dims Error (15): Output array z must have dims Error (16): Array z should be 1D or 2D Error (17): gpu variable x must be a numeric array Error (18): gpu variable y must be a numeric array Warning (19): gpu qualifier on non-array s is ignored Error (20): Array ref x *must* be output Unrecognized typespace: bozo test_typecheck.mw: 18 type errors detected