[
  {
    "path": ".JuliaFormatter.toml",
    "content": "whitespace_typedefs = false\nwhitespace_ops_in_indices = false\nwhitespace_in_kwargs = false\nremove_extra_newlines = true\n\nformat_docstrings = true\nannotate_untyped_fields_with_any = true\njoin_lines_based_on_source = true\n\nalign_assignment = true\nalign_pair_arrow = true\nalign_struct_field = true\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/CI.yml",
    "content": "name: CI\r\n\r\non:\r\n  push:\r\n    branches:\r\n      - master\r\n      - release-*\r\n    tags: '*'\r\n  pull_request:\r\n\r\njobs:\r\n  test:\r\n    name: julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}\r\n    runs-on: ${{ matrix.os }}\r\n    strategy:\r\n      fail-fast: false\r\n      matrix:\r\n        version:\r\n          - '1.3'\r\n          - '1'\r\n          - 'nightly'\r\n        os:\r\n          - ubuntu-latest\r\n        arch:\r\n          - x64\r\n    steps:\r\n      - uses: actions/checkout@v6\r\n      - uses: julia-actions/setup-julia@latest\r\n        with:\r\n          version: ${{ matrix.version }}\r\n          arch: ${{ matrix.arch }}\r\n      - uses: julia-actions/julia-buildpkg@latest\r\n      - uses: julia-actions/julia-runtest@latest\r\n"
  },
  {
    "path": ".github/workflows/Format.yml",
    "content": "name: Format suggestions\non:\n  pull_request:\n    # this argument is not required if you don't use the `suggestion-label` input\n    types: [opened, reopened, synchronize, labeled, unlabeled]\njobs:\n  code-style:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: julia-actions/julia-format@v4\n        with:\n          version: \"1\" # Set `version` to '1.0.54' if you need to use JuliaFormatter.jl v1.0.54 (default: '1')\n          suggestion-label: \"format-suggest\" # leave this unset or empty to show suggestions for all PRs\n"
  },
  {
    "path": ".github/workflows/RegisterAction.yml",
    "content": "name: RegisterAction\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: Version to register or component to bump\n        required: true\njobs:\n  register:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: julia-actions/RegisterAction@latest\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/TagBot.yml",
    "content": "name: TagBot\non:\n  issue_comment:\n    types:\n      - created\n  workflow_dispatch:\n    inputs:\n      lookback:\n        default: 3\npermissions:\n  actions: read\n  checks: read\n  contents: write\n  deployments: read\n  issues: read\n  discussions: read\n  packages: read\n  pages: read\n  pull-requests: read\n  repository-projects: read\n  security-events: read\n  statuses: read\njobs:\n  TagBot:\n    if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: JuliaRegistries/TagBot@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          # Edit the following line to reflect the actual name of the GitHub Secret containing your private key\n          ssh: ${{ secrets.DOCUMENTER_KEY }}\n          # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }}\n"
  },
  {
    "path": ".gitignore",
    "content": "deps/build.log\ndeps/deps.jl\n*.mat\nManifest.toml\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2013 Dahua Lin\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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.\n"
  },
  {
    "path": "Project.toml",
    "content": "name = \"MATLAB\"\nuuid = \"10e44e05-a98a-55b3-a45b-ba969058deb6\"\nlicense = \"MIT\"\nrepo = \"https://github.com/JuliaInterop/MATLAB.jl.git\"\nversion = \"0.9.0\"\n\n[deps]\nLibdl = \"8f399da3-3557-5675-b5ff-fb832c97cbdb\"\nSparseArrays = \"2f01184e-e22b-5df5-ae63-d93ebab69eaf\"\n\n[compat]\njulia = \"1\"\n\n[extras]\nTest = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\n\n[targets]\ntest = [\"Test\"]\n"
  },
  {
    "path": "README.md",
    "content": "## MATLAB\n\n| ❗ If you get a 'Segmentation fault' error when calling MATLAB with version R2022 or newer, try using an [older version](https://github.com/JuliaInterop/MATLAB.jl#changing-matlab-version) of MATLAB. ❗ |\n|:----:|\n\nThe `MATLAB.jl` package provides an interface for using [MATLAB®](http://www.mathworks.com/products/matlab/) from [Julia](http://julialang.org) using the MATLAB C api.  In other words, this package allows users to call MATLAB functions within Julia, thus making it easy to interoperate with MATLAB from the Julia language.\n\nYou cannot use `MATLAB.jl` without having purchased and installed a copy of MATLAB® from [MathWorks](http://www.mathworks.com/). This package is available free of charge and in no way replaces or alters any functionality of MathWorks's MATLAB product.\n\n## Overview\n\nThis package is composed of two aspects:\n\n* Creating and manipulating mxArrays (the data structure that MATLAB used to represent arrays and other kinds of data)\n\n* Communicating with MATLAB engine sessions\n\n**Warning**:\n\n* MATLAB string arrays are not supported, and will throw an error exception. This also applies if they are nested within a MATLAB struct. This is a limitation of the MATLAB C api. The MATLAB function `convertContainedStringsToChars` may be used to facilitate conversion to a compatible format for use with `MATLAB.jl`.\n\n* Threading is also not supported within Julia when using the MATLAB.jl library.\n\n\n## Installation\n\n**Important**: The procedure to setup this package consists of the following steps.\n\n### Windows\n\n1. For Matlab R2020a onwards, you should be able to go directly to step 2. If you encounter issues, run `matlab -batch \"comserver('register')\"` in the command prompt. For earlier versions of Matlab, start a command prompt as an administrator and enter `matlab /regserver`. Alternatively, you can do the same using the MATLAB GUI. To do this, open MATLAB with administrative privileges and run the following command in the MATLAB command window: `!matlab -regserver`. Close MATLAB and restart Julia.\n\n2. From Julia run: `Pkg.add(\"MATLAB\")`\n\n\n### Linux\n\n1. Make sure ``matlab`` is in executable path.\n\n2. Make sure ``csh`` is installed. (Note: MATLAB for Linux relies on ``csh`` to open an engine session.)\n\n\tTo install ``csh`` in Debian/Ubuntu/Linux Mint, you may type in the following command in terminal:\n\n\t```bash\n\tsudo apt-get install csh\n\t```\n\n3. From Julia run: `Pkg.add(\"MATLAB\")`\n\n\nIf you experience problems when starting the MATLAB engine with versions R2022 or R2023, try to [update](https://se.mathworks.com/help/matlab/matlab_env/check-for-software-updates.html) your MATLAB release.\n\n\n### Mac OS X\n\n1. Ensure that MATLAB is installed in `/Applications` (for example, if you are using MATLAB R2012b, you may add the following command to `.profile`:  `export MATLAB_HOME=/Applications/MATLAB_R2012b.app`).\n\n2. From Julia run: `Pkg.add(\"MATLAB\")`\n\n\n## Changing MATLAB version\n\nBy default, `MATLAB.jl` is built using the MATLAB installation with the greatest version number. To specify that a specific MATLAB installation should be used, set the environment variable `MATLAB_ROOT`:\n```julia\njulia> ENV[\"MATLAB_ROOT\"] = \"/usr/local/MATLAB/R2021b\" # example on a Linux machine\n```\n```julia\njulia> ENV[\"MATLAB_ROOT\"] = raw\"C:\\Program Files\\MATLAB\\R2021b\" # example on a Windows machine\n```\nReplace the path string with the location of the MATLAB folder on your machine. You need to set the path to the `R20XX` folder, not the `matlab` binary.\n\nIf you had the package `MATLAB.jl` already installed and built before changing the environment variable, you will need to rebuild it to apply the change:\n```julia\njulia> using Pkg; Pkg.build(\"MATLAB\")\n```\n\n### Notes for Windows Users\n\nIf you are using Windows, MATLAB needs to register its COM interface to properly work with MATLAB.jl. This should happen automatically during installation, but if you encounter issues, you can manually re-register MATLAB (with the version you want to use). To do this\n\n1. Open a MATLAB window with administrative privileges\n2. Run the following command in the MATLAB command window: `!matlab -regserver`\n3. Close MATLAB and restart Julia\n\n> [!IMPORTANT]\n> The version of MATLAB that is registered must be same that `MATLAB.jl` uses.\n\n\n\n## Usage\n\n### MxArray class\n\nAn instance of ``MxArray`` encapsulates a MATLAB variable. This package provides a series of functions to manipulate such instances.\n\n#### Create MATLAB variables in Julia\n\nOne can use the function ``mxarray`` to create MATLAB variables (of type ``MxArray``), as follows\n\n```julia\nmxarray(Float64, n)   # creates an n-by-1 MATLAB zero array of double valued type\nmxarray(Int32, m, n)  # creates an m-by-n MATLAB zero array of int32 valued type\nmxarray(Bool, m, n)   # creates a MATLAB logical array of size m-by-n\n\nmxarray(Float64, (n1, n2, n3))  # creates a MATLAB array of size n1-by-n2-by-n3\n\nmxcellarray(m, n)        # creates a MATLAB cell array\nmxstruct(\"a\", \"b\", \"c\")  # creates a MATLAB struct with given fields\n```\n\nYou may also convert a Julia variable to MATLAB variable\n\n```julia\na = rand(m, n)\n\nx = mxarray(a)     # converts a to a MATLAB array\nx = mxarray(1.2)   # converts a scalar 1.2 to a MATLAB variable\n\na = sprand(m, n, 0.1)\nx = mxarray(a)     # converts a sparse matrix to a MATLAB sparse matrix\n\nx = mxarray(\"abc\") # converts a string to a MATLAB char array\n\nx = mxarray([\"a\", 1, 2.3])  # converts a Julia array to a MATLAB cell array\n\nx = mxarray(Dict(\"a\"=>1, \"b\"=>\"string\", \"c\"=>[1,2,3])) # converts a Julia dictionary to a MATLAB struct\n```\n\nThe function ``mxarray`` can also convert a compound type to a Julia struct:\n```julia\nstruct S\n    x::Float64\n    y::Vector{Int32}\n    z::Bool\nend\n\ns = S(1.2, Int32[1, 2], false)\n\nx = mxarray(s)   # creates a MATLAB struct with three fields: x, y, z\nxc = mxarray([s, s])  # creates a MATLAB cell array, each cell is a struct.\nxs = mxstructarray([s, s])  # creates a MATLAB array of structs\n```\n\n**Note:** For safety, the conversation between MATLAB and Julia variables uses deep copy.\n\nWhen you finish using a MATLAB variable, you may call ``delete`` to free the memory. But this is optional, it will be deleted when reclaimed by the garbage collector.\n\n```julia\ndelete(x)\n```\n\n*Note:* if you put a MATLAB variable ``x`` to MATLAB engine session, then the MATLAB engine will take over the management of its life cylce, and you don't have to delete it explicitly.\n\n\n#### Access MATLAB variables\n\nYou may access attributes and data of a MATLAB variable through the functions provided by this package.\n\n```julia\n# suppose x is of type MxArray\nnrows(x)    # returns number of rows in x\nncols(x)    # returns number of columns in x\nnelems(x)   # returns number of elements in x\nndims(x)    # returns number of dimensions in x\nsize(x)     # returns the size of x as a tuple\nsize(x, d)  # returns the size of x along a specific dimension\n\neltype(x)   # returns element type of x (in Julia Type)\nelsize(x)   # return number of bytes per element\n\ndata_ptr(x)   # returns pointer to data (in Ptr{T}), where T is eltype(x)\n\n# suppose s is a MATLAB struct\nmxnfields(s)\t# returns the number of fields in struct s\n\n```\n\nYou may also make tests on a MATLAB variable.\n\n```julia\nis_double(x)   # returns whether x is a double array\nis_sparse(x)   # returns whether x is sparse\nis_complex(x)  # returns whether x is complex\nis_cell(x)     # returns whether x is a cell array\nis_struct(x)   # returns whether x is a struct\nis_empty(x)    # returns whether x is empty\n\n...            # there are many more there\n```\n\n#### Convert MATLAB variables to Julia\n\n```julia\na = jarray(x)   # converts x to a Julia array\na = jvector(x)  # converts x to a Julia vector (1D array) when x is a vector\na = jscalar(x)  # converts x to a Julia scalar\na = jmatrix(x)  # converts x to a Julia matrix\na = jstring(x)  # converts x to a Julia string\na = jdict(x)    # converts a MATLAB struct to a Julia dictionary (using fieldnames as keys)\n\na = jvalue(x)  # converts x to a Julia value in default manner\n```\n\n### Read/Write MAT Files\n\nThis package provides functions to manipulate MATLAB's mat files:\n\n```julia\nmf = MatFile(filename, mode)    # opens a MAT file using a specific mode, and returns a handle\nmf = MatFile(filename)          # opens a MAT file for reading, equivalent to MatFile(filename, \"r\")\nclose(mf)                       # closes a MAT file.\n\nget_mvariable(mf, name)   # gets a variable and returns an mxArray\nget_variable(mf, name)    # gets a variable, but converts it to a Julia value using `jvalue`\n\nput_variable(mf, name, v)   # puts a variable v to the MAT file\n                            # v can be either an MxArray instance or normal variable\n                            # If v is not an MxArray, it will be converted using `mxarray`\n\nput_variables(mf; name1=v1, name2=v2, ...)  # put multiple variables using keyword arguments\n\nvariable_names(mf)   # get a vector of all variable names in a MAT file\n```\n\nThere are also convenient functions that can get/put all variables in one call:\n\n```julia\nread_matfile(filename)    # returns a dictionary that maps each variable name\n                          # to an MxArray instance\n\nwrite_matfile(filename; name1=v1, name2=v2, ...)  # writes all variables given in the\n                                                  # keyword argument list to a MAT file\n```\nBoth ``read_matfile`` and ``write_matfile`` will close the MAT file handle before returning.\n\n**Examples:**\n\n```julia\nstruct S\n    x::Float64\n    y::Bool\n    z::Vector{Float64}\nend\n\nwrite_matfile(\"test.mat\";\n    a = Int32[1 2 3; 4 5 6],\n    b = [1.2, 3.4, 5.6, 7.8],\n    c = [[0.0, 1.0], [1.0, 2.0], [1.0, 2.0, 3.0]],\n    d = Dict(\"name\"=>\"MATLAB\", \"score\"=>100.0),\n    s = \"abcde\",\n    ss = [S(1.0, true, [1., 2.]), S(2.0, false, [3., 4.])] )\n```\n\nThis example will create a MAT file called ``test.mat``, which contains six MATLAB variables:\n\n* ``a``: a 2-by-3 int32 array\n* ``b``: a 4-by-1 double array\n* ``c``: a 3-by-1 cell array, each cell contains a double vector\n* ``d``: a struct with two fields: name and score\n* ``s``: a string (i.e. char array)\n* ``ss``: an array of structs with two elements, and three fields: x, y, and z.\n\n\n### Use MATLAB Engine\n\n#### Basic Use\n\nTo evaluate expressions in MATLAB, one may open a MATLAB engine session and communicate with it. There are three ways to call MATLAB from Julia:\n\n- The `mat\"\"` custom string literal allows you to write MATLAB syntax inside Julia and use Julia variables directly from MATLAB via interpolation\n- The `eval_string` evaluate a string containing MATLAB expressions (typically used with the helper macros `@mget` and `@mput`\n- The `mxcall` function calls a given MATLAB function and returns the result\n\nIn general, the `mat\"\"` custom string literal is the preferred method to interact with the MATLAB engine.\n\n*Note:* There can be multiple (reasonable) ways to convert a MATLAB variable to Julia array. For example, MATLAB represents a scalar using a 1-by-1 matrix. Here we have two choices in terms of converting such a matrix back to Julia: (1) convert to a scalar number, or (2) convert to a matrix of size 1-by-1.\n\n##### The `mat\"\"` custom string literal\n\nText inside the `mat\"\"` custom string literal is in MATLAB syntax. Variables from Julia can be \"interpolated\" into MATLAB code by prefixing them with a dollar sign as you would interpolate them into an ordinary string.\n\n```julia\nusing MATLAB\n\nx = range(-10.0, stop=10.0, length=500)\nmat\"plot($x, sin($x))\"  # evaluate a MATLAB function\n\ny = range(2.0, stop=3.0, length=500)\nmat\"\"\"\n    $u = $x + $y\n\t$v = $x - $y\n\"\"\"\n@show u v               # u and v are accessible from Julia\n```\n\nAs with ordinary string literals, you can also interpolate whole Julia expressions, e.g. `mat\"$(x[1]) = $(x[2]) + $(binomial(5, 2))\"`.\n\n##### `eval_string`\n\nYou may also use the `eval_string` function to evaluate MATLAB code as follows\n ```julia\neval_string(\"a = sum([1,2,3])\")\n```\n\nThe `eval_string` function also takes an optional argument that specifies which MATLAB session to evaluate the code in, e.g.\n```julia\njulia> s = MSession();\njulia> eval_string(s, \"a = sum([1,2,3])\")\na =\n     6\n```\n\n##### `mxcall`\n\nYou may also directly call a MATLAB function on Julia variables using `mxcall`:\n\n```julia\nx = -10.0:0.1:10.0\ny = -10.0:0.1:10.0\nxx, yy = mxcall(:meshgrid, 2, x, y)\n```\n*Note:* Since MATLAB functions behavior depends on the number of outputs, you have to specify the number of output arguments in ``mxcall`` as the second argument.\n\n``mxcall`` puts the input arguments to the MATLAB workspace (using mangled names), evaluates the function call in MATLAB, and retrieves the variable from the MATLAB session. This function is mainly provided for convenience. However, you should keep in mind that it may incur considerable overhead due to the communication between MATLAB and Julia domain.\n\n##### `@mget` and `@mput`\n\nThe macro `@mget` can be used to extract the value of a MATLAB variable into Julia\n```julia\njulia> mat\"a = 6\"\njulia> @mget a\n6.0\n```\n\nThe macro `@mput` can be used to translate a Julia variable into MATLAB\n```julia\njulia> x = [1,2,3]\njulia> @mput x\njulia> eval_string(\"y = sum(x)\")\njulia> @mget y\n6.0\njulia> @show y\na = 63.0\n```\n\n#### Calling custom MATLAB function\n\nIf the MATLAB function is not in the current directory, we need to first add it to the MATLAB path before calling through Julia:\n\n```julia\nmat\"addpath('/path/to/folder')\"\nval = mat\"myfunction($arg1, $arg2)\"\n```\nFor example, if there is a MATLAB file located at `/path/to/folder` with contents:\n\n```matlab\nfunction [r,u] = test(x, y)\n\tr = x + y;\n\tu = x - y;\nend\n```\n\nWe can call this function as follows in Julia:\n\n```julia\nusing MATLAB\n\nx = range(-10.0, stop=10.0, length=500)\ny = range(2.0, stop=3.0, length=500)\n\nmat\"addpath('/path/to/folder')\"\n\nr, u = mxcall(:test,2,x,y)\n```\n\n\n\n#### Viewing the MATLAB Session (Windows only)\n\nTo open an interactive window for the MATLAB session, use the command `show_msession()` and to hide the window, use `hide_msession()`. *Warning: manually closing this window will result in an error or result in a segfault; it is advised that you only use the `hide_msession()` command to hide the interactive window.*\n\nNote that this feature only works on Windows.\n\n```julia\n# default\nshow_msession() # open the default MATLAB session interactive window\nget_msession_visiblity() # get the session's visibility state\nhide_msession() # hide the default MATLAB session interactive window\n\n# similarly\ns = MSession()\nshow_msession(s)\nget_msession_visiblity(a)\nhide_msession(s)\n```\n\n\n#### Advanced use of MATLAB Engines\n\nThis package provides a series of functions for users to control the communication with MATLAB sessions.\n\nHere is an example:\n\n```julia\ns1 = MSession()    # creates a MATLAB session\ns2 = MSession(0)   # creates a MATLAB session without recording output\n\nx = rand(3, 4)\nput_variable(s1, :x, x)  # put x to session s1\n\ny = rand(2, 3)\nput_variable(s2, :y, y)  # put y to session s2\n\neval_string(s1, \"r = sin(x)\")  # evaluate sin(x) in session s1\neval_string(s2, \"r = sin(y)\")  # evaluate sin(y) in session s2\n\nr1_mx = get_mvariable(s1, :r)  # get r from s1\nr2_mx = get_mvariable(s2, :r)  # get r from s2\n\nr1 = jarray(r1_mx)\nr2 = jarray(r2_mx)\n\n# ... do other stuff on r1 and r2\n\nclose(s1)  # close session s1\nclose(s2)  # close session s2\n```\n"
  },
  {
    "path": "deps/build.jl",
    "content": "import Libdl\n\nconst depsfile = joinpath(@__DIR__, \"deps.jl\")\n\nfunction find_matlab_root()\n    # Determine MATLAB library path and provide facilities to load libraries with this path\n    matlab_root = get(ENV, \"MATLAB_ROOT\",\n        get(ENV, \"MATLAB_HOME\", nothing))\n    if isnothing(matlab_root)\n        matlab_exe = Sys.which(\"matlab\")\n        if !isnothing(matlab_exe)\n            matlab_exe = islink(matlab_exe) ? readlink(matlab_exe) : matlab_exe # guard against /usr/local \n            matlab_root = dirname(dirname(matlab_exe))\n        else\n            if Sys.isapple()\n                default_dir = \"/Applications\"\n                if isdir(default_dir)\n                    dirs = readdir(default_dir)\n                    filter!(app -> occursin(r\"^MATLAB_R[0-9]+[ab]\\.app$\", app), dirs)\n                    if !isempty(dirs)\n                        matlab_root = joinpath(default_dir, maximum(dirs))\n                    end\n                end\n            elseif Sys.iswindows()\n                default_dir =\n                    Sys.WORD_SIZE == 32 ? \"C:\\\\Program Files (x86)\\\\MATLAB\" :\n                    \"C:\\\\Program Files\\\\MATLAB\"\n                if isdir(default_dir)\n                    dirs = readdir(default_dir)\n                    filter!(dir -> occursin(r\"^R[0-9]+[ab]$\", dir), dirs)\n                    if !isempty(dirs)\n                        matlab_root = joinpath(default_dir, maximum(dirs))\n                    end\n                end\n            elseif Sys.islinux()\n                default_dir = \"/usr/local/MATLAB\"\n                if isdir(default_dir)\n                    dirs = readdir(default_dir)\n                    filter!(dir -> occursin(r\"^R[0-9]+[ab]$\", dir), dirs)\n                    if !isempty(dirs)\n                        matlab_root = joinpath(default_dir, maximum(dirs))\n                    end\n                end\n            end\n        end\n    end\n    !isnothing(matlab_root) && isdir(matlab_root) &&\n        @info(\"Detected MATLAB root folder at \\\"$matlab_root\\\"\")\n    return matlab_root\nend\n\nfunction find_matlab_libpath(matlab_root)\n    # get path to MATLAB libraries\n    matlab_libdir = if Sys.islinux()\n        Sys.WORD_SIZE == 32 ? \"glnx86\" : \"glnxa64\"\n    elseif Sys.isapple()\n        archchar = Sys.ARCH == :aarch64 ? \"a\" : \"i\"\n        Sys.WORD_SIZE == 32 ? \"maci\" : \"mac\" * archchar * \"64\"\n    elseif Sys.iswindows()\n        Sys.WORD_SIZE == 32 ? \"win32\" : \"win64\"\n    end\n    matlab_libpath = joinpath(matlab_root, \"bin\", matlab_libdir)\n    isdir(matlab_libpath) && @info(\"Detected MATLAB library path at \\\"$matlab_libpath\\\"\")\n    return matlab_libpath\nend\n\nfunction find_matlab_cmd(matlab_root)\n    if Sys.iswindows()\n        matlab_cmd = joinpath(\n            matlab_root,\n            \"bin\",\n            (Sys.WORD_SIZE == 32 ? \"win32\" : \"win64\"),\n            \"matlab.exe\",\n        )\n        isfile(matlab_cmd) && @info(\"Detected MATLAB executable at \\\"$matlab_cmd\\\"\")\n    else\n        matlab_exe = joinpath(matlab_root, \"bin\", \"matlab\")\n        isfile(matlab_exe) && @info(\"Detected MATLAB executable at \\\"$matlab_exe\\\"\")\n        matlab_cmd = \"exec $(Base.shell_escape(matlab_exe))\"\n    end\n    return matlab_cmd\nend\n\nmatlab_root = find_matlab_root()\n\nif !isnothing(matlab_root)\n    matlab_libpath = find_matlab_libpath(matlab_root)\n    matlab_cmd = find_matlab_cmd(matlab_root)\n    libmx_size = filesize(Libdl.dlpath(joinpath(matlab_libpath, \"libmx\")))\n    open(depsfile, \"w\") do io\n        println(io,\n            \"\"\"\n            # This file is automatically generated, do not edit.\n\n            function check_deps()\n                if libmx_size != filesize(Libdl.dlpath(joinpath(matlab_libpath, \"libmx\")))\n                    error(\"MATLAB library has changed, re-run Pkg.build(\\\\\\\"MATLAB\\\\\\\")\")\n                end\n            end\n            \"\"\",\n        )\n        println(io, \"const matlab_libpath = \\\"$(escape_string(matlab_libpath))\\\"\")\n        println(io, \"const matlab_cmd = \\\"$(escape_string(matlab_cmd))\\\"\")\n        println(io, \"const libmx_size = $libmx_size\")\n    end\nelseif get(ENV, \"JULIA_REGISTRYCI_AUTOMERGE\", nothing) == \"true\" ||\n       get(ENV, \"CI\", nothing) == \"true\"\n    # We need to be able to install and load this package without error for\n    # Julia's registry AutoMerge to work, so we just use dummy values.\n    # Similarly we want to also be able to install and load this package for CI.\n    matlab_libpath = \"\"\n    matlab_cmd = \"\"\n    libmx_size = 0\n\n    open(depsfile, \"w\") do io\n        println(io,\n            \"\"\"\n            # This file is automatically generated, do not edit.\n\n            check_deps() = nothing\n            \"\"\",\n        )\n        println(io, \"const matlab_libpath = \\\"$(escape_string(matlab_libpath))\\\"\")\n        println(io, \"const matlab_cmd = \\\"$(escape_string(matlab_cmd))\\\"\")\n        println(io, \"const libmx_size = $libmx_size\")\n    end\nelse\n    error(\n        \"MATLAB cannot be found. Set the \\\"MATLAB_ROOT\\\" environment variable to the MATLAB root directory and re-run Pkg.build(\\\"MATLAB\\\").\",\n    )\nend\n"
  },
  {
    "path": "src/MATLAB.jl",
    "content": "module MATLAB\n\nusing Libdl\nusing SparseArrays\n\nimport Base: eltype, close, size, copy, ndims, unsafe_convert\n\n# mxarray\nexport MxArray,\n    mxClassID, mxComplexity,\n    mxclassid, data_ptr,\n    classid, nrows, ncols, nelems, elsize\n\nexport is_double, is_single,\n    is_int8, is_uint8, is_int16, is_uint16,\n    is_int32, is_uint32, is_int64, is_uint64,\n    is_numeric, is_complex, is_sparse, is_empty,\n    is_logical, is_char, is_struct, is_cell\n\nexport mxarray, mxsparse, delete,\n    mxcellarray, get_cell, set_cell,\n    mxstruct, mxstructarray, mxnfields, get_fieldname, get_field, set_field,\n    jvalue, jarray, jscalar, jvector, jmatrix, jsparse, jstring, jdict\n\n# engine & matfile\nexport MSession, MatFile,\n    get_default_msession, restart_default_msession, close_default_msession,\n    eval_string, get_mvariable, get_variable, put_variable, put_variables,\n    variable_names, read_matfile, write_matfile,\n    mxcall,\n    @mput, @mget, @mat_str\n\nif Sys.iswindows()\n    export show_msession, hide_msession, get_msession_visiblity\nend\n\nconst depsfile = joinpath(dirname(@__DIR__), \"deps\", \"deps.jl\")\nif isfile(depsfile)\n    include(depsfile)\nelse\n    error(\n        \"MATLAB is not properly installed. Please run Pkg.build(\\\"MATLAB\\\") and restart Julia.\",\n    )\nend\n\ninclude(\"exceptions.jl\")\ninclude(\"init.jl\") # initialize Refs\ninclude(\"mxarray.jl\")\ninclude(\"matfile.jl\")\ninclude(\"engine.jl\")\ninclude(\"matstr.jl\")\n\nif Sys.iswindows()\n    # workaround \"primary message table for module 77\" error\n    # creates a dummy Engine session and keeps it open so the libraries used by all other\n    # Engine clients are not loaded and unloaded repeatedly\n    # see: https://www.mathworks.com/matlabcentral/answers/305877-what-is-the-primary-message-table-for-module-77\n\n    # initialization is delayed until first call to MSession\n    const persistent_msession_ref = Ref{MSession}()\n    const persistent_msession_assigned = Ref(false)\n\n    function assign_persistent_msession()\n        if persistent_msession_assigned[] == false\n            persistent_msession_assigned[] = true\n            persistent_msession_ref[] = MSession(0)\n        end\n    end\nend\n\n# helper library access function\nengfunc(fun::Symbol) = Libdl.dlsym(libeng[], fun)\nmxfunc(fun::Symbol)  = Libdl.dlsym(libmx[], fun)\nmatfunc(fun::Symbol) = Libdl.dlsym(libmat[], fun)\n\nfunction __init__()\n    check_deps()\n\n    if libmx_size > 0 # non-zero size library path\n\n        # load libraries\n        # workaround for https://github.com/JuliaInterop/MATLAB.jl/issues/200\n        if Sys.iswindows()\n            ENV[\"PATH\"] = string(matlab_libpath, \";\", ENV[\"PATH\"])\n        elseif Sys.islinux()\n            ENV[\"PATH\"] = string(matlab_libpath, \":\", ENV[\"PATH\"])\n        end\n        libmx[]  = Libdl.dlopen(joinpath(matlab_libpath, \"libmx\"), Libdl.RTLD_GLOBAL)\n        libmat[] = Libdl.dlopen(joinpath(matlab_libpath, \"libmat\"), Libdl.RTLD_GLOBAL)\n        libeng[] = Libdl.dlopen(joinpath(matlab_libpath, \"libeng\"), Libdl.RTLD_GLOBAL)\n\n        # engine functions\n\n        eng_open[]          = engfunc(:engOpen)\n        eng_close[]         = engfunc(:engClose)\n        eng_set_visible[]   = engfunc(:engSetVisible)\n        eng_get_visible[]   = engfunc(:engGetVisible)\n        eng_output_buffer[] = engfunc(:engOutputBuffer)\n        eng_eval_string[]   = engfunc(:engEvalString)\n        eng_put_variable[]  = engfunc(:engPutVariable)\n        eng_get_variable[]  = engfunc(:engGetVariable)\n\n        # mxarray functions\n\n        mx_destroy_array[]   = mxfunc(:mxDestroyArray)\n        mx_duplicate_array[] = mxfunc(:mxDuplicateArray)\n\n        # load functions to access mxarray\n\n        mx_free[] = mxfunc(:mxFree)\n\n        mx_get_classid[]  = mxfunc(:mxGetClassID)\n        mx_get_m[]        = mxfunc(:mxGetM)\n        mx_get_n[]        = mxfunc(:mxGetN)\n        mx_get_nelems[]   = mxfunc(:mxGetNumberOfElements)\n        mx_get_ndims[]    = mxfunc(:mxGetNumberOfDimensions_730)\n        mx_get_elemsize[] = mxfunc(:mxGetElementSize)\n        mx_get_data[]     = mxfunc(:mxGetData)\n        mx_get_dims[]     = mxfunc(:mxGetDimensions_730)\n        mx_get_nfields[]  = mxfunc(:mxGetNumberOfFields)\n        mx_get_pr[]       = mxfunc(:mxGetPr)\n        mx_get_pi[]       = mxfunc(:mxGetPi)\n        mx_get_ir[]       = mxfunc(:mxGetIr_730)\n        mx_get_jc[]       = mxfunc(:mxGetJc_730)\n\n        mx_is_double[] = mxfunc(:mxIsDouble)\n        mx_is_single[] = mxfunc(:mxIsSingle)\n        mx_is_int64[]  = mxfunc(:mxIsInt64)\n        mx_is_uint64[] = mxfunc(:mxIsUint64)\n        mx_is_int32[]  = mxfunc(:mxIsInt32)\n        mx_is_uint32[] = mxfunc(:mxIsUint32)\n        mx_is_int16[]  = mxfunc(:mxIsInt16)\n        mx_is_uint16[] = mxfunc(:mxIsUint16)\n        mx_is_int8[]   = mxfunc(:mxIsInt8)\n        mx_is_uint8[]  = mxfunc(:mxIsUint8)\n        mx_is_char[]   = mxfunc(:mxIsChar)\n\n        mx_is_numeric[] = mxfunc(:mxIsNumeric)\n        mx_is_logical[] = mxfunc(:mxIsLogical)\n        mx_is_complex[] = mxfunc(:mxIsComplex)\n        mx_is_sparse[]  = mxfunc(:mxIsSparse)\n        mx_is_empty[]   = mxfunc(:mxIsEmpty)\n        mx_is_struct[]  = mxfunc(:mxIsStruct)\n        mx_is_cell[]    = mxfunc(:mxIsCell)\n\n        # load functions to create & delete MATLAB array\n\n        mx_create_numeric_matrix[] = mxfunc(:mxCreateNumericMatrix_730)\n        mx_create_numeric_array[]  = mxfunc(:mxCreateNumericArray_730)\n\n        mx_create_double_scalar[]  = mxfunc(:mxCreateDoubleScalar)\n        mx_create_logical_scalar[] = mxfunc(:mxCreateLogicalScalar)\n\n        mx_create_sparse[]         = mxfunc(:mxCreateSparse_730)\n        mx_create_sparse_logical[] = mxfunc(:mxCreateSparseLogicalMatrix_730)\n\n        mx_create_string[]     = mxfunc(:mxCreateString)\n        mx_create_char_array[] = mxfunc(:mxCreateCharArray_730)\n\n        mx_create_cell_array[] = mxfunc(:mxCreateCellArray_730)\n\n        mx_create_struct_matrix[] = mxfunc(:mxCreateStructMatrix_730)\n        mx_create_struct_array[]  = mxfunc(:mxCreateStructArray_730)\n\n        mx_get_cell[] = mxfunc(:mxGetCell_730)\n        mx_set_cell[] = mxfunc(:mxSetCell_730)\n\n        mx_get_field[]       = mxfunc(:mxGetField_730)\n        mx_set_field[]       = mxfunc(:mxSetField_730)\n        mx_get_field_bynum[] = mxfunc(:mxGetFieldByNumber_730)\n        mx_get_fieldname[]   = mxfunc(:mxGetFieldNameByNumber)\n\n        mx_get_string[] = mxfunc(:mxGetString_730)\n\n        # load I/O mat functions\n\n        mat_open[]         = matfunc(:matOpen)\n        mat_close[]        = matfunc(:matClose)\n        mat_get_variable[] = matfunc(:matGetVariable)\n        mat_put_variable[] = matfunc(:matPutVariable)\n        mat_get_dir[]      = matfunc(:matGetDir)\n    end\nend\n\n###########################################################\n#\n#   deprecations\n#\n###########################################################\n\nend\n"
  },
  {
    "path": "src/engine.jl",
    "content": "# operation on MATLAB engine sessions\n\n###########################################################\n#\n#   Session open & close\n#\n###########################################################\nconst default_startflag = \"-nodisplay -nosplash -nodesktop\" # no additional flags\nconst default_matlabcmd = matlab_cmd * \" -nodisplay -nosplash -nodesktop\"\n# pass matlab flags directly or as a Vector of flags, i.e. \"-a\" or [\"-a\", \"-b\", \"-c\"]\nstartcmd(flag::AbstractString=default_startflag) =\n    isempty(flag) ? default_matlabcmd : default_matlabcmd * \" \" * flag\nstartcmd(flags::AbstractVector{<:AbstractString}) =\n    isempty(flags) ? default_matlabcmd : default_matlabcmd * \" \" * join(flags, \" \")\n\n# 64 K buffer should be sufficient to store the output text in most cases\nconst default_output_buffer_size = 64 * 1024\n\nconst windows_regserver_warning = \"\"\"\nFailed to start MATLAB engine. If you have/had multiple versions of MATLAB installed, this can happen if you \ntried to start a different version of MATLAB in Julia compared to which MATLAB server is registered in Windows.\n\nSteps to resolve this:\n\n1. Register a specific MATLAB version manually as a server, open a MATLAB window as a user with administrator privileges. \nIn MATLAB, enter the command `!matlab -regserver`. Then close the MATLAB window. More details:\nhttps://de.mathworks.com/help/matlab/matlab_external/registering-matlab-software-as-a-com-server.html\n\n2. Ensure that the MATLAB.jl package is using the same MATLAB version that was registered in step 1. See the instructions on GitHub\non how to change the version that MATLAB.jl uses:\nhttps://github.com/JuliaInterop/MATLAB.jl?tab=readme-ov-file#changing-matlab-version\n\"\"\"\n\nmutable struct MSession\n    ptr::Ptr{Cvoid}\n    buffer::Vector{UInt8}\n    bufptr::Ptr{UInt8}\n    check_exceptions::Bool\n\n    function MSession(\n        bufsize::Integer=default_output_buffer_size;\n        flags=default_startflag,\n        check_exceptions::Bool=true,\n    )\n        if Sys.iswindows()\n            assign_persistent_msession()\n        end\n        ep = ccall(eng_open[], Ptr{Cvoid}, (Ptr{UInt8},), startcmd(flags))\n        if ep == C_NULL\n            @warn(\n                \"Confirm MATLAB is installed and discoverable.\",\n                matlab_libpath,\n                maxlog = 1\n            )\n            if Sys.iswindows()\n                @warn(windows_regserver_warning, maxlog = 1)\n            elseif Sys.islinux()\n                @warn(\n                    \"Ensure `csh` is installed; this may require running `sudo apt-get install csh`.\",\n                    maxlog = 1\n                )\n            end\n            throw(MEngineError(\"failed to open MATLAB engine session\"))\n        end\n        if Sys.iswindows()\n            # hide the MATLAB command window on Windows and change to current directory\n            ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), ep, 0)\n            ccall(eng_eval_string[], Cint, (Ptr{Cvoid}, Ptr{UInt8}),\n                ep, \"try cd('$(escape_string(pwd()))'); end\")\n        end\n        buf = Vector{UInt8}(undef, bufsize)\n        if bufsize > 0\n            bufptr = pointer(buf)\n            ccall(eng_output_buffer[], Cint, (Ptr{Cvoid}, Ptr{UInt8}, Cint),\n                ep, bufptr, bufsize)\n        else\n            bufptr = convert(Ptr{UInt8}, C_NULL)\n        end\n\n        self = new(ep, buf, bufptr, check_exceptions)\n        finalizer(release, self)\n        return self\n    end\nend\n\nfunction unsafe_convert(::Type{Ptr{Cvoid}}, m::MSession)\n    ptr = m.ptr\n    ptr == C_NULL && throw(UndefRefError())\n    return ptr\nend\n\nfunction release(session::MSession)\n    ptr = session.ptr\n    if ptr != C_NULL\n        ccall(eng_close[], Cint, (Ptr{Cvoid},), ptr)\n    end\n    session.ptr = C_NULL\n    return nothing\nend\n\nfunction close(session::MSession)\n    # close a MATLAB Engine session\n    ret = ccall(eng_close[], Cint, (Ptr{Cvoid},), session)\n    ret != 0 && throw(MEngineError(\"failed to close MATLAB engine session (err = $ret)\"))\n    session.ptr = C_NULL\n    return nothing\nend\n\nhas_exception_check_enabled(session::MSession=get_default_msession()) =\n    session.check_exceptions\ndisable_exception_check!(session::MSession=get_default_msession()) =\n    (session.check_exceptions = false; nothing)\nenable_exception_check!(session::MSession=get_default_msession()) =\n    (session.check_exceptions = true; nothing)\n\n# default session\n\nconst default_msession_ref = Ref{MSession}()\n\n# this function will start an MSession if default_msession_ref is undefined or if the\n# MSession has been closed so that the engine ptr is void\nfunction get_default_msession()\n    if !isassigned(default_msession_ref) || default_msession_ref[].ptr == C_NULL\n        default_msession_ref[] = MSession()\n    end\n    return default_msession_ref[]\nend\n\nfunction restart_default_msession(bufsize::Integer=default_output_buffer_size)\n    close_default_msession()\n    default_msession_ref[] = MSession(bufsize)\n    return nothing\nend\n\nfunction close_default_msession()\n    if isassigned(default_msession_ref) && default_msession_ref[].ptr !== C_NULL\n        close(default_msession_ref[])\n    end\n    return nothing\nend\n\nif Sys.iswindows()\n    function show_msession(m::MSession=get_default_msession())\n        ret = ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), m, 1)\n        ret != 0 && throw(MEngineError(\"failed to show MATLAB engine session (err = $ret)\"))\n        return nothing\n    end\n\n    function hide_msession(m::MSession=get_default_msession())\n        ret = ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), m, 0)\n        ret != 0 && throw(MEngineError(\"failed to hide MATLAB engine session (err = $ret)\"))\n        return nothing\n    end\n\n    function get_msession_visiblity(m::MSession=get_default_msession())\n        vis = Ref{Cint}(true)\n        ccall(eng_get_visible[], Int, (Ptr{Cvoid}, Ptr{Cint}), m, vis)\n        return vis[] == 1 ? true : false\n    end\nend\n\n###########################################################\n#\n#   communication with MATLAB session\n#\n###########################################################\n\nfunction _eval_string(session::MSession, stmt::String)\n    # evaluate a MATLAB statement in a given MATLAB session\n    ret = ccall(eng_eval_string[], Cint, (Ptr{Cvoid}, Ptr{UInt8}), session, stmt)\n    ret != 0 && throw(MEngineError(\"invalid engine session (err = $ret)\"))\n\n    bufptr = session.bufptr\n    if bufptr != C_NULL\n        bs = unsafe_string(bufptr)\n        if ~isempty(bs)\n            print(bs)\n        end\n    end\n    return nothing\nend\n\nfunction eval_string(session::MSession, stmt::String)\n    _eval_string(session, stmt)\n    if session.check_exceptions\n        check_and_clear_last_exception(session)\n    end\nend\n\neval_string(stmt::String) = eval_string(get_default_msession(), stmt)\n\nfunction put_variable(session::MSession, name::Symbol, v::MxArray)\n    # put a variable into a MATLAB engine session\n    ret = ccall(\n        eng_put_variable[],\n        Cint,\n        (Ptr{Cvoid}, Ptr{UInt8}, Ptr{Cvoid}),\n        session,\n        string(name),\n        v,\n    )\n    ret != 0 && throw(\n        MEngineError(\"failed to put variable $(name) into MATLAB session (err = $ret)\"),\n    )\n    return nothing\nend\n\nput_variable(session::MSession, name::Symbol, v) = put_variable(session, name, mxarray(v))\n\nput_variable(name::Symbol, v) = put_variable(get_default_msession(), name, v)\n\nfunction get_mvariable(session::MSession, name::Symbol)\n    pv = ccall(\n        eng_get_variable[],\n        Ptr{Cvoid},\n        (Ptr{Cvoid}, Ptr{UInt8}),\n        session,\n        string(name),\n    )\n    pv == C_NULL &&\n        throw(MEngineError(\"failed to get variable $(name) from MATLAB session\"))\n    return MxArray(pv)\nend\n\nget_mvariable(name::Symbol) = get_mvariable(get_default_msession(), name)\n\nget_variable(name::Symbol) = jvalue(get_mvariable(name))\nget_variable(name::Symbol, kind) = jvalue(get_mvariable(name), kind)\n\n\"\"\"\n    check_and_clear_last_exception(session::MSession)\n\nChecks if an exception has been thrown in the MATLAB session by checking the `MException.last` variable.\nIf it is not empty, it throws a `MatlabException` with the message and identifier of the last exception.\nIn any case, it clears the `MException.last` variable.\n\"\"\"\nfunction check_and_clear_last_exception(session::MSession)\n    exception_check_code = \"\"\"\n    matlab_exception_jl_message = MException.last.message; \n    matlab_exception_jl_identifier = MException.last.identifier; \n    MException.last('reset');\n    \"\"\"\n    _eval_string(session, exception_check_code)\n    message = jvalue(get_mvariable(session, :matlab_exception_jl_message))\n    identifier = jvalue(get_mvariable(session, :matlab_exception_jl_identifier))\n\n    if !isempty(identifier)\n        throw(MatlabException(identifier, message))\n    end\n\n    _eval_string(\n        session,\n        \"clear matlab_exception_jl_message matlab_exception_jl_identifier;\",\n    )\nend\n\n###########################################################\n#\n#   macro to simplify syntax\n#\n###########################################################\n\nfunction _mput_multi(vs::Symbol...)\n    nv = length(vs)\n    if nv == 1\n        v = vs[1]\n        :(MATLAB.put_variable($(Meta.quot(v)), $(v)))\n    else\n        stmts = Vector{Expr}(undef, nv)\n        for i = 1:nv\n            v = vs[i]\n            stmts[i] = :(MATLAB.put_variable($(Meta.quot(v)), $(v)))\n        end\n        Expr(:block, stmts...)\n    end\nend\n\nmacro mput(vs...)\n    esc(_mput_multi(vs...))\nend\n\nfunction make_getvar_statement(v::Symbol)\n    :($(v) = MATLAB.get_variable($(Meta.quot(v))))\nend\n\nfunction make_getvar_statement(ex::Expr)\n    if !(ex.head == :(::))\n        error(\"Invalid expression for @mget.\")\n    end\n    v::Symbol = ex.args[1]\n    k::Symbol = ex.args[2]\n\n    :($(v) = MATLAB.get_variable($(Meta.quot(v)), $(k)))\nend\n\nfunction _mget_multi(vs::Union{Symbol,Expr}...)\n    nv = length(vs)\n    if nv == 1\n        make_getvar_statement(vs[1])\n    else\n        stmts = Vector{Expr}(undef, nv)\n        for i = 1:nv\n            stmts[i] = make_getvar_statement(vs[i])\n        end\n        Expr(:block, stmts...)\n    end\nend\n\nmacro mget(vs...)\n    esc(_mget_multi(vs...))\nend\n\n###########################################################\n#\n#   mxcall\n#\n###########################################################\n\n# MATLAB does not allow underscore as prefix of a variable name\n_gen_marg_name(mfun::Symbol, prefix::String, i::Int) = \"jx_$(mfun)_arg_$(prefix)_$(i)\"\n\nfunction mxcall(session::MSession, mfun::Symbol, nout::Integer, in_args...)\n    nin = length(in_args)\n\n    # generate temporary variable names\n\n    in_arg_names = Vector{String}(undef, nin)\n    out_arg_names = Vector{String}(undef, nout)\n\n    for i = 1:nin\n        in_arg_names[i] = _gen_marg_name(mfun, \"in\", i)\n    end\n\n    for i = 1:nout\n        out_arg_names[i] = _gen_marg_name(mfun, \"out\", i)\n    end\n\n    # generate MATLAB statement\n\n    buf = IOBuffer()\n    if nout > 0\n        if nout > 1\n            print(buf, \"[\")\n        end\n        join(buf, out_arg_names, \", \")\n        if nout > 1\n            print(buf, \"]\")\n        end\n        print(buf, \" = \")\n    end\n\n    print(buf, string(mfun))\n    print(buf, \"(\")\n    if nin > 0\n        join(buf, in_arg_names, \", \")\n    end\n    print(buf, \");\")\n\n    stmt = String(take!(buf))\n\n    # put variables to MATLAB\n\n    for i = 1:nin\n        put_variable(session, Symbol(in_arg_names[i]), in_args[i])\n    end\n\n    # execute MATLAB statement\n\n    eval_string(session, stmt)\n\n    # get results from MATLAB\n\n    ret = if nout == 1\n        jvalue(get_mvariable(session, Symbol(out_arg_names[1])))\n    elseif nout >= 2\n        results = Vector{Any}(undef, nout)\n        for i = 1:nout\n            results[i] = jvalue(get_mvariable(session, Symbol(out_arg_names[i])))\n        end\n        tuple(results...)\n    else\n        nothing\n    end\n\n    # clear temporaries from MATLAB workspace\n\n    for i = 1:nin\n        eval_string(session, string(\"clear \", in_arg_names[i], \";\"))\n    end\n\n    for i = 1:nout\n        eval_string(session, string(\"clear \", out_arg_names[i], \";\"))\n    end\n\n    return ret\nend\n\nmxcall(mfun::Symbol, nout::Integer, in_args...) =\n    mxcall(get_default_msession(), mfun, nout, in_args...)\n"
  },
  {
    "path": "src/exceptions.jl",
    "content": "struct MEngineError <: Exception\n    message::String\nend\n\n\"\"\"\n    MEngineError(message::String)\n\nException thrown by MATLAB, e.g. due to syntax errors in the code\npassed to `eval_string` or `mat\"...\"`.\n\"\"\"\nstruct MatlabException <: Exception\n    identifier::String\n    message::String\nend\n"
  },
  {
    "path": "src/init.jl",
    "content": "# libraries\n\nconst libeng = Ref{Ptr{Cvoid}}()\nconst libmx  = Ref{Ptr{Cvoid}}()\nconst libmat = Ref{Ptr{Cvoid}}()\n\n# matlab engine functions\n\nconst eng_open          = Ref{Ptr{Cvoid}}()\nconst eng_close         = Ref{Ptr{Cvoid}}()\nconst eng_set_visible   = Ref{Ptr{Cvoid}}()\nconst eng_get_visible   = Ref{Ptr{Cvoid}}()\nconst eng_output_buffer = Ref{Ptr{Cvoid}}()\nconst eng_eval_string   = Ref{Ptr{Cvoid}}()\nconst eng_put_variable  = Ref{Ptr{Cvoid}}()\nconst eng_get_variable  = Ref{Ptr{Cvoid}}()\n\n# mxarray functions\n\nconst mx_destroy_array   = Ref{Ptr{Cvoid}}()\nconst mx_duplicate_array = Ref{Ptr{Cvoid}}()\n\n# functions to access mxarray\n\nconst mx_free = Ref{Ptr{Cvoid}}()\n\nconst mx_get_classid  = Ref{Ptr{Cvoid}}()\nconst mx_get_m        = Ref{Ptr{Cvoid}}()\nconst mx_get_n        = Ref{Ptr{Cvoid}}()\nconst mx_get_nelems   = Ref{Ptr{Cvoid}}()\nconst mx_get_ndims    = Ref{Ptr{Cvoid}}()\nconst mx_get_elemsize = Ref{Ptr{Cvoid}}()\nconst mx_get_data     = Ref{Ptr{Cvoid}}()\nconst mx_get_dims     = Ref{Ptr{Cvoid}}()\nconst mx_get_nfields  = Ref{Ptr{Cvoid}}()\nconst mx_get_pr       = Ref{Ptr{Cvoid}}()\nconst mx_get_pi       = Ref{Ptr{Cvoid}}()\nconst mx_get_ir       = Ref{Ptr{Cvoid}}()\nconst mx_get_jc       = Ref{Ptr{Cvoid}}()\n\nconst mx_is_double = Ref{Ptr{Cvoid}}()\nconst mx_is_single = Ref{Ptr{Cvoid}}()\nconst mx_is_int64  = Ref{Ptr{Cvoid}}()\nconst mx_is_uint64 = Ref{Ptr{Cvoid}}()\nconst mx_is_int32  = Ref{Ptr{Cvoid}}()\nconst mx_is_uint32 = Ref{Ptr{Cvoid}}()\nconst mx_is_int16  = Ref{Ptr{Cvoid}}()\nconst mx_is_uint16 = Ref{Ptr{Cvoid}}()\nconst mx_is_int8   = Ref{Ptr{Cvoid}}()\nconst mx_is_uint8  = Ref{Ptr{Cvoid}}()\nconst mx_is_char   = Ref{Ptr{Cvoid}}()\n\nconst mx_is_numeric = Ref{Ptr{Cvoid}}()\nconst mx_is_logical = Ref{Ptr{Cvoid}}()\nconst mx_is_complex = Ref{Ptr{Cvoid}}()\nconst mx_is_sparse  = Ref{Ptr{Cvoid}}()\nconst mx_is_empty   = Ref{Ptr{Cvoid}}()\nconst mx_is_struct  = Ref{Ptr{Cvoid}}()\nconst mx_is_cell    = Ref{Ptr{Cvoid}}()\n\n# functions to create & delete MATLAB arrays\n\nconst mx_create_numeric_matrix = Ref{Ptr{Cvoid}}()\nconst mx_create_numeric_array  = Ref{Ptr{Cvoid}}()\n\nconst mx_create_double_scalar  = Ref{Ptr{Cvoid}}()\nconst mx_create_logical_scalar = Ref{Ptr{Cvoid}}()\n\nconst mx_create_sparse         = Ref{Ptr{Cvoid}}()\nconst mx_create_sparse_logical = Ref{Ptr{Cvoid}}()\n\nconst mx_create_string     = Ref{Ptr{Cvoid}}()\nconst mx_create_char_array = Ref{Ptr{Cvoid}}()\n\nconst mx_create_cell_array = Ref{Ptr{Cvoid}}()\n\nconst mx_create_struct_matrix = Ref{Ptr{Cvoid}}()\nconst mx_create_struct_array  = Ref{Ptr{Cvoid}}()\n\nconst mx_get_cell = Ref{Ptr{Cvoid}}()\nconst mx_set_cell = Ref{Ptr{Cvoid}}()\n\nconst mx_get_field       = Ref{Ptr{Cvoid}}()\nconst mx_set_field       = Ref{Ptr{Cvoid}}()\nconst mx_get_field_bynum = Ref{Ptr{Cvoid}}()\nconst mx_get_fieldname   = Ref{Ptr{Cvoid}}()\n\nconst mx_get_string = Ref{Ptr{Cvoid}}()\n\n# load I/O mat functions\n\nconst mat_open         = Ref{Ptr{Cvoid}}()\nconst mat_close        = Ref{Ptr{Cvoid}}()\nconst mat_get_variable = Ref{Ptr{Cvoid}}()\nconst mat_put_variable = Ref{Ptr{Cvoid}}()\nconst mat_get_dir      = Ref{Ptr{Cvoid}}()\n"
  },
  {
    "path": "src/matfile.jl",
    "content": "# mat file open & close\n\nmutable struct MatFile\n    ptr::Ptr{Cvoid}\n    filename::String\n\n    function MatFile(filename::String, mode::String)\n        p = ccall(mat_open[], Ptr{Cvoid}, (Ptr{Cchar}, Ptr{Cchar}), filename, mode)\n        self = new(p, filename)\n        finalizer(release, self)\n        return self\n    end\nend\nMatFile(filename::String) = MatFile(filename, \"r\")\n\nfunction unsafe_convert(::Type{Ptr{Cvoid}}, f::MatFile)\n    ptr = f.ptr\n    ptr == C_NULL && throw(UndefRefError())\n    return ptr\nend\n\nfunction release(f::MatFile)\n    ptr = f.ptr\n    if ptr != C_NULL\n        ccall(mat_close[], Cint, (Ptr{Cvoid},), ptr)\n    end\n    f.ptr = C_NULL\n    return nothing\nend\n\nfunction close(f::MatFile)\n    ret = ccall(mat_close[], Cint, (Ptr{Cvoid},), f)\n    ret != 0 && throw(MEngineError(\"failed to close file (err = $ret)\"))\n    f.ptr = C_NULL\n    return nothing\nend\n\n# get & put variables\n\nfunction get_mvariable(f::MatFile, name::String)\n    pm = ccall(mat_get_variable[], Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cchar}), f, name)\n    pm == C_NULL && error(\"Attempt to get variable $(name) failed.\")\n    MxArray(pm)\nend\n\nget_mvariable(f::MatFile, name::Symbol) = get_mvariable(f, string(name))\n\nget_variable(f::MatFile, name::String) = jvalue(get_mvariable(f, name))\nget_variable(f::MatFile, name::Symbol) = jvalue(get_mvariable(f, name))\n\nfunction put_variable(f::MatFile, name::String, v::MxArray)\n    ret = ccall(mat_put_variable[], Cint, (Ptr{Cvoid}, Ptr{Cchar}, Ptr{Cvoid}), f, name, v)\n    ret != 0 && error(\"Attempt to put variable $(name) failed.\")\n    return nothing\nend\n\nput_variable(f::MatFile, name::Symbol, v::MxArray) = put_variable(f, string(name), v)\n\nput_variable(f::MatFile, name::String, v) = put_variable(f, name, mxarray(v))\nput_variable(f::MatFile, name::Symbol, v) = put_variable(f, name, mxarray(v))\n\n# operation over entire file\n\nfunction put_variables(f::MatFile; kwargs...)\n    for (name, val) in kwargs\n        put_variable(f, name, val)\n    end\nend\n\nfunction write_matfile(filename::String; kwargs...)\n    mf = MatFile(filename, \"w\")\n    put_variables(mf; kwargs...)\n    close(mf)\nend\n\nfunction variable_names(f::MatFile)\n    # get a list of all variable names\n    _n = Ref{Cint}()\n    _a = ccall(mat_get_dir[], Ptr{Ptr{Cchar}}, (Ptr{Cvoid}, Ref{Cint}), f, _n)\n\n    n = Int(_n[])\n    a = unsafe_wrap(Array, _a, (n,))\n\n    names = String[unsafe_string(s) for s in a]\n    ccall(mx_free[], Cvoid, (Ptr{Cvoid},), _a)\n    return names\nend\n\nfunction read_matfile(f::MatFile)\n    # return a dictionary of all variables\n    names = variable_names(f)\n    r = Dict{String,MxArray}()\n    sizehint!(r, length(names))\n    for nam in names\n        r[nam] = get_mvariable(f, nam)\n    end\n    return r\nend\n\nfunction read_matfile(filename::String)\n    f = MatFile(filename, \"r\")\n    r = read_matfile(f)\n    close(f)\n    return r\nend\n"
  },
  {
    "path": "src/matstr.jl",
    "content": "# Syntax for mat\"\" string interpolation\n\n# A really basic parser intended only to handle checking whether\n# a variable is on the left or right hand side of an expression\nmutable struct DumbParserState\n    paren_depth::Int\n    in_string::Bool\nend\nDumbParserState() = DumbParserState(0, false)\n\n# Returns true if an = is encountered and updates pstate\nfunction dumb_parse!(pstate::DumbParserState, str::String)\n    paren_depth = pstate.paren_depth\n    in_string = pstate.in_string\n    x = '\\0'\n    s = firstindex(str)\n    while s <= ncodeunits(str)\n        lastx = x\n        x = str[s]\n        s = nextind(str, s)\n        if in_string\n            if x == '\\''\n                if (s <= ncodeunits(str)) && (str[s] == '\\'')\n                    x = str[s]\n                    s = nextind(str, s)\n                else\n                    in_string = false\n                end\n            end\n        else\n            if x == '('\n                paren_depth += 1\n            elseif x == ')'\n                paren_depth -= 1\n            elseif x == '\\'' && lastx in \",( \\t\\0;\"\n                in_string = true\n            elseif x == '=' && !(lastx in \"<>~\")\n                if (s <= ncodeunits(str)) && (str[s] == '=')\n                    x = str[s]\n                    s = nextind(str, s)\n                else\n                    return true\n                end\n            elseif x == '%'\n                break\n            end\n        end\n    end\n    pstate.paren_depth = paren_depth\n    pstate.in_string = in_string\n    return false\nend\n\n# Check if a given variable is assigned, used, or both. Returns the#\n# assignment and use status\nfunction check_assignment(interp, i)\n    # Go back to the last newline\n    before = String[]\n    for j = (i-1):-1:1\n        if isa(interp[j], String)\n            sp = split(interp[j], \"\\n\")\n            pushfirst!(before, sp[end])\n            for k = (length(sp)-1):-1:1\n                match(r\"\\.\\.\\.[ \\t]*\\r?$\", sp[k]) === nothing && @goto done_before\n                pushfirst!(before, sp[k])\n            end\n        end\n    end\n    @label done_before\n\n    # Check if this reference is inside parens at the start, or on the rhs of an assignment\n    pstate = DumbParserState()\n    (dumb_parse!(pstate, join(before)) || pstate.paren_depth > 1) && return (false, true)\n\n    # Go until the next newline or comment\n    after = String[]\n    both_sides = false\n    for j = (i+1):length(interp)\n        if isa(interp[j], String)\n            sp = split(interp[j], \"\\n\")\n            push!(after, sp[1])\n            for k = 2:length(sp)\n                match(r\"\\.\\.\\.[ \\t]*\\r?$\", sp[k-1]) === nothing && @goto done_after\n                push!(after, sp[k])\n            end\n        elseif interp[j] == interp[i]\n            both_sides = true\n        end\n    end\n    @label done_after\n\n    assigned = dumb_parse!(pstate, join(after))\n    used =\n        !assigned || both_sides ||\n        (i < length(interp) && match(r\"^[ \\t]*\\(\", interp[i+1]) != nothing)\n    return (assigned, used)\nend\n\nfunction do_mat_str(ex)\n    # Hack to do interpolation\n    interp = Meta.parse(string(\"\\\"\\\"\\\"\", replace(ex, \"\\\"\\\"\\\"\" => \"\\\\\\\"\\\"\\\"\"), \"\\\"\\\"\\\"\"))\n    if isa(interp, String)\n        interp = [interp]\n    elseif interp.head == :string\n        interp = interp.args\n    elseif interp.head == :macrocall\n        interp = interp.args[2:end]\n    else\n        throw(ArgumentError(\"unexpected input\"))\n    end\n\n    # Handle interpolated variables\n    putblock = Expr(:block)\n    getblock = Expr(:block)\n    usedvars = Set{Symbol}()\n    assignedvars = Set{Symbol}()\n    varmap = Dict{Symbol,Symbol}()\n    for i = 1:length(interp)\n        if !isa(interp[i], String)\n            # Don't put the same symbol to MATLAB twice\n            if haskey(varmap, interp[i])\n                var = varmap[interp[i]]\n            else\n                var = Symbol(\"matlab_jl_\", i)\n                if isa(interp[i], Symbol)\n                    varmap[interp[i]] = var\n                end\n            end\n\n            # Try to determine if variable is being used in an assignment\n            (assigned, used) = check_assignment(interp, i)\n\n            if used && !(var in usedvars)\n                push!(usedvars, var)\n                (var in assignedvars) || push!(\n                    putblock.args,\n                    :(put_variable($(Meta.quot(var)), $(esc(interp[i])))),\n                )\n            end\n            if assigned && !(var in assignedvars)\n                push!(assignedvars, var)\n                if isa(interp[i], Expr) && (interp[i].head == :ref)\n                    # Assignment to a sliced variable, e.g., x[1:3], must use broadcasting in v0.7+\n                    push!(\n                        getblock.args,\n                        Expr(:(.=), esc(interp[i]), :(get_variable($(Meta.quot(var))))),\n                    )\n                else\n                    push!(\n                        getblock.args,\n                        Expr(:(=), esc(interp[i]), :(get_variable($(Meta.quot(var))))),\n                    )\n                end\n            end\n\n            interp[i] = var\n        end\n    end\n\n    # Clear `ans` and set `matlab_jl_has_ans` before we run the code\n    pushfirst!(interp, \"clear ans;\\nmatlab_jl_has_ans = 0;\\n\")\n\n    # Add a semicolon to the end of the last statement to suppress output\n    isa(interp[end], String) && (interp[end] = rstrip(interp[end]))\n    push!(interp, \";\")\n\n    # Figure out if `ans` exists in code to avoid an error if it doesn't\n    push!(interp, \"\\nmatlab_jl_has_ans = exist('ans', 'var');\")\n\n    quote\n        $(putblock)\n        eval_string($(join(interp)))\n        $(getblock)\n        $(\n            if !isempty(usedvars) || !isempty(assignedvars)\n                # Clear variables we created\n                :(eval_string(\n                    $(string(\"clear \", join(union(usedvars, assignedvars), \" \"), \";\")),\n                ))\n            end\n        )\n        if get_variable(:matlab_jl_has_ans) != 0\n            # Return ans if it was set\n            get_variable(:ans)\n        end\n    end\nend\n\nmacro mat_str(ex)\n    do_mat_str(ex)\nend\n"
  },
  {
    "path": "src/mxarray.jl",
    "content": "# functions to deal with MATLAB arrays\n\nmutable struct MxArray\n    ptr::Ptr{Cvoid}\n    own::Bool\n\n    function MxArray(p::Ptr{Cvoid}, own::Bool)\n        p == C_NULL && error(\"NULL pointer for MxArray.\")\n        self = new(p, own)\n        if own\n            finalizer(release, self)\n        end\n        return self\n    end\nend\nMxArray(p::Ptr{Cvoid}) = MxArray(p, true)\n\nmxarray(mx::MxArray) = mx\n\nfunction release(mx::MxArray)\n    if mx.own && mx.ptr != C_NULL\n        ccall(mx_destroy_array[], Cvoid, (Ptr{Cvoid},), mx.ptr)\n    end\n    mx.ptr = C_NULL\n    return nothing\nend\n\n# delete & copy\n\nfunction delete(mx::MxArray)\n    if mx.own\n        ccall(mx_destroy_array[], Cvoid, (Ptr{Cvoid},), mx)\n    end\n    mx.ptr = C_NULL\n    return nothing\nend\n\nfunction copy(mx::MxArray)\n    pm = ccall(mx_duplicate_array[], Ptr{Cvoid}, (Ptr{Cvoid},), mx)\n    return MxArray(pm)\nend\n\nfunction unsafe_convert(::Type{Ptr{Cvoid}}, mx::MxArray)\n    ptr = mx.ptr\n    ptr == C_NULL && throw(UndefRefError())\n    return ptr\nend\n# functions to create mxArray from Julia values/arrays\n\nconst MxRealNum =\n    Union{Float64,Float32,Int32,UInt32,Int64,UInt64,Int16,UInt16,Int8,UInt8,Bool}\nconst MxComplexNum = Union{ComplexF32,ComplexF64}\nconst MxNum = Union{MxRealNum,MxComplexNum}\n\n###########################################################\n#\n#  MATLAB types\n#\n###########################################################\n\nconst mwSize = UInt\nconst mwIndex = Int\n\n@enum mxClassID::Cint begin\n    mxUNKNOWN_CLASS\n    mxCELL_CLASS\n    mxSTRUCT_CLASS\n    mxLOGICAL_CLASS\n    mxCHAR_CLASS\n    mxVOID_CLASS\n    mxDOUBLE_CLASS\n    mxSINGLE_CLASS\n    mxINT8_CLASS\n    mxUINT8_CLASS\n    mxINT16_CLASS\n    mxUINT16_CLASS\n    mxINT32_CLASS\n    mxUINT32_CLASS\n    mxINT64_CLASS\n    mxUINT64_CLASS\n    mxFUNCTION_CLASS\n    mxOPAQUE_CLASS\n    mxOBJECT_CLASS\nend\n\n@enum mxComplexity::Cint begin\n    mxREAL\n    mxCOMPLEX\nend\n\nmxclassid(::Type{Bool})                            = mxLOGICAL_CLASS\nmxclassid(::Union{Type{Float64},Type{ComplexF64}}) = mxDOUBLE_CLASS\nmxclassid(::Union{Type{Float32},Type{ComplexF32}}) = mxSINGLE_CLASS\nmxclassid(::Type{Int8})                            = mxINT8_CLASS\nmxclassid(::Type{UInt8})                           = mxUINT8_CLASS\nmxclassid(::Type{Int16})                           = mxINT16_CLASS\nmxclassid(::Type{UInt16})                          = mxUINT16_CLASS\nmxclassid(::Type{Int32})                           = mxINT32_CLASS\nmxclassid(::Type{UInt32})                          = mxUINT32_CLASS\nmxclassid(::Type{Int64})                           = mxINT64_CLASS\nmxclassid(::Type{UInt64})                          = mxUINT64_CLASS\n\nmxcomplexflag(::Type{T}) where {T<:MxRealNum}    = mxREAL\nmxcomplexflag(::Type{T}) where {T<:MxComplexNum} = mxCOMPLEX\n\nconst classid_type_map = Dict{mxClassID,Type}(\n    mxLOGICAL_CLASS => Bool,\n    mxCHAR_CLASS    => Char,\n    mxDOUBLE_CLASS  => Float64,\n    mxSINGLE_CLASS  => Float32,\n    mxINT8_CLASS    => Int8,\n    mxUINT8_CLASS   => UInt8,\n    mxINT16_CLASS   => Int16,\n    mxUINT16_CLASS  => UInt16,\n    mxINT32_CLASS   => Int32,\n    mxUINT32_CLASS  => UInt32,\n    mxINT64_CLASS   => Int64,\n    mxUINT64_CLASS  => UInt64,\n)\n\nfunction mxclassid_to_type(cid::mxClassID)\n    ty = get(classid_type_map, cid, nothing)\n    ty === nothing && throw(ArgumentError(\"The input class id is not a primitive type id.\"))\n    return ty\nend\n\n###########################################################\n#\n#  Functions to access mxArray\n#\n#  Part of the functions (e.g. mxGetNumberOfDimensions)\n#  are actually a macro replacement of an internal\n#  function name as (xxxx_730)\n#\n###########################################################\n\nmacro mxget_attr(fun, ret, mx)\n    :(ccall($(esc(fun)), $(esc(ret)), (Ptr{Cvoid},), $(esc(mx))))\nend\n\nclassid(mx::MxArray) = @mxget_attr(mx_get_classid[], mxClassID, mx)\nnrows(mx::MxArray)   = convert(Int, @mxget_attr(mx_get_m[], UInt, mx))\nncols(mx::MxArray)   = convert(Int, @mxget_attr(mx_get_n[], UInt, mx))\nnelems(mx::MxArray)  = convert(Int, @mxget_attr(mx_get_nelems[], UInt, mx))\nndims(mx::MxArray)   = convert(Int, @mxget_attr(mx_get_ndims[], mwSize, mx))\n\neltype(mx::MxArray) = mxclassid_to_type(classid(mx))\nelsize(mx::MxArray) = convert(Int, @mxget_attr(mx_get_elemsize[], UInt, mx))\ndata_ptr(mx::MxArray) = convert(Ptr{eltype(mx)}, @mxget_attr(mx_get_data[], Ptr{Cvoid}, mx))\nreal_ptr(mx::MxArray) = convert(Ptr{eltype(mx)}, @mxget_attr(mx_get_pr[], Ptr{Cvoid}, mx))\nimag_ptr(mx::MxArray) = convert(Ptr{eltype(mx)}, @mxget_attr(mx_get_pi[], Ptr{Cvoid}, mx))\n\nmxnfields(mx::MxArray) = convert(Int, @mxget_attr(mx_get_nfields[], Cint, mx))\n\n# validation functions\n\nmacro mx_test_is(fun, mx)\n    :((ccall($(esc(fun)), Bool, (Ptr{Cvoid},), $(esc(mx)))))\nend\n\nis_double(mx::MxArray) = @mx_test_is(mx_is_double[], mx)\nis_single(mx::MxArray) = @mx_test_is(mx_is_single[], mx)\nis_int64(mx::MxArray)  = @mx_test_is(mx_is_int64[], mx)\nis_uint64(mx::MxArray) = @mx_test_is(mx_is_uint64[], mx)\nis_int32(mx::MxArray)  = @mx_test_is(mx_is_int32[], mx)\nis_uint32(mx::MxArray) = @mx_test_is(mx_is_uint32[], mx)\nis_int16(mx::MxArray)  = @mx_test_is(mx_is_int16[], mx)\nis_uint16(mx::MxArray) = @mx_test_is(mx_is_uint16[], mx)\nis_int8(mx::MxArray)   = @mx_test_is(mx_is_int8[], mx)\nis_uint8(mx::MxArray)  = @mx_test_is(mx_is_uint8[], mx)\n\nis_numeric(mx::MxArray) = @mx_test_is(mx_is_numeric[], mx)\nis_logical(mx::MxArray) = @mx_test_is(mx_is_logical[], mx)\nis_complex(mx::MxArray) = @mx_test_is(mx_is_complex[], mx)\nis_sparse(mx::MxArray)  = @mx_test_is(mx_is_sparse[], mx)\nis_struct(mx::MxArray)  = @mx_test_is(mx_is_struct[], mx)\nis_cell(mx::MxArray)    = @mx_test_is(mx_is_cell[], mx)\nis_char(mx::MxArray)    = @mx_test_is(mx_is_char[], mx)\nis_empty(mx::MxArray)   = @mx_test_is(mx_is_empty[], mx)\n\n# size function\n\nfunction size(mx::MxArray)\n    nd = ndims(mx)\n    pdims::Ptr{mwSize} = @mxget_attr(mx_get_dims[], Ptr{mwSize}, mx)\n    _dims = unsafe_wrap(Array, pdims, (nd,))\n    dims = Vector{Int}(undef, nd)\n    for i = 1:nd\n        dims[i] = Int(_dims[i])\n    end\n    tuple(dims...)\nend\n\nfunction size(mx::MxArray, d::Integer)\n    d <= 0 && throw(ArgumentError(\"The dimension must be a positive integer.\"))\n\n    nd = ndims(mx)\n    if nd == 2\n        d == 1 ? nrows(mx) :\n        d == 2 ? ncols(mx) : 1\n    else\n        pdims::Ptr{mwSize} = @mxget_attr(mx_get_dims[], Ptr{mwSize}, mx)\n        _dims = unsafe_wrap(Array, pdims, (nd,))\n        d <= nd ? Int(_dims[d]) : 1\n    end\nend\n\n###########################################################\n#\n#  functions to create & delete MATLAB arrays\n#\n###########################################################\n\nfunction _dims_to_mwSize(dims::Tuple{Vararg{Integer,N}}) where {N}\n    _dims = Vector{mwSize}(undef, N)\n    for i = 1:N\n        _dims[i] = mwSize(dims[i])\n    end\n    _dims\nend\n\nfunction mxarray(::Type{T}, dims::Tuple{Vararg{Integer,N}}) where {T<:MxNum,N}\n    pm = ccall(mx_create_numeric_array[], Ptr{Cvoid},\n        (mwSize, Ptr{mwSize}, mxClassID, mxComplexity),\n        N, _dims_to_mwSize(dims), mxclassid(T), mxcomplexflag(T))\n    MxArray(pm)\nend\nmxarray(::Type{T}, dims::Integer...) where {T<:MxNum} = mxarray(T, dims)\n\n# create scalars\n\nfunction mxarray(x::Float64)\n    pm = ccall(mx_create_double_scalar[], Ptr{Cvoid}, (Cdouble,), x)\n    MxArray(pm)\nend\n\nfunction mxarray(x::Bool)\n    pm = ccall(mx_create_logical_scalar[], Ptr{Cvoid}, (Bool,), x)\n    MxArray(pm)\nend\n\nfunction mxarray(x::T) where {T<:MxRealNum}\n    pm = ccall(mx_create_numeric_matrix[], Ptr{Cvoid},\n        (mwSize, mwSize, mxClassID, mxComplexity),\n        1, 1, mxclassid(T), mxcomplexflag(T))\n\n    pdat = ccall(mx_get_data[], Ptr{T}, (Ptr{Cvoid},), pm)\n\n    unsafe_wrap(Array, pdat, (1,))[1] = x\n    MxArray(pm)\nend\nmxarray(x::T) where {T<:MxComplexNum} = mxarray([x])\n\n# conversion from Julia variables to MATLAB\n# Note: the conversion is deep-copy, as there is no way to let\n# mxArray use Julia array's memory\n\nfunction mxarray(a::Array{T}) where {T<:MxRealNum}\n    mx = mxarray(T, size(a))\n    ccall(\n        :memcpy,\n        Ptr{Cvoid},\n        (Ptr{Cvoid}, Ptr{Cvoid}, UInt),\n        data_ptr(mx),\n        a,\n        length(a) * sizeof(T),\n    )\n    return mx\nend\n\nfunction mxarray(a::Array{T}) where {T<:MxComplexNum}\n    mx = mxarray(T, size(a))\n    na = length(a)\n    rdat = unsafe_wrap(Array, real_ptr(mx), na)\n    idat = unsafe_wrap(Array, imag_ptr(mx), na)\n    for i = 1:na\n        rdat[i] = real(a[i])\n        idat[i] = imag(a[i])\n    end\n    mx\nend\n\nfunction mxarray(a::AbstractArray{T}) where {T<:MxRealNum}\n    mx = mxarray(T, size(a))\n    ptr = data_ptr(mx)\n    na = length(a)\n    dat = unsafe_wrap(Array{T}, ptr, na)\n    for (i, ix) in enumerate(eachindex(a))\n        dat[i] = a[ix]\n    end\n    return mx\nend\n\nfunction mxarray(a::AbstractArray{T}) where {T<:MxComplexNum}\n    mx = mxarray(T, size(a))\n    na = length(a)\n    rdat = unsafe_wrap(Array, real_ptr(mx), na)\n    idat = unsafe_wrap(Array, imag_ptr(mx), na)\n    for (i, ix) in enumerate(eachindex(a))\n        rdat[i] = real(a[ix])\n        idat[i] = imag(a[ix])\n    end\n    return mx\nend\n\nfunction mxarray(a::NTuple{N,T}) where {N,T<:MxRealNum}\n    mx = mxarray(T, N)\n    pdat = ccall(mx_get_data[], Ptr{T}, (Ptr{Cvoid},), mx)\n    dat = unsafe_wrap(Array, pdat, N)\n    for i = 1:N\n        dat[i] = a[i]\n    end\n    return mx\nend\n\nfunction mxarray(a::NTuple{N,T}) where {N,T<:MxComplexNum}\n    mx = mxarray(T, size(a))\n    na = length(a)\n    rdat = unsafe_wrap(Array, real_ptr(mx), na)\n    idat = unsafe_wrap(Array, imag_ptr(mx), na)\n    for (i, ix) in enumerate(eachindex(a))\n        rdat[i] = real(a[ix])\n        idat[i] = imag(a[ix])\n    end\n    return mx\nend\n\nfunction mxarray(a::Tuple)\n    mx = mxcellarray(length(a))\n    for i = 1:length(a)\n        set_cell(mx, i, mxarray(a[i]))\n    end\n    return mx\nend\n\n# sparse matrix\n\nfunction mxsparse(ty::Type{Float64}, m::Integer, n::Integer, nzmax::Integer)\n    pm = ccall(mx_create_sparse[], Ptr{Cvoid},\n        (mwSize, mwSize, mwSize, mxComplexity), m, n, nzmax, mxREAL)\n    MxArray(pm)\nend\n\nfunction mxsparse(ty::Type{ComplexF64}, m::Integer, n::Integer, nzmax::Integer)\n    pm = ccall(mx_create_sparse[], Ptr{Cvoid},\n        (mwSize, mwSize, mwSize, mxComplexity), m, n, nzmax, mxCOMPLEX)\n    MxArray(pm)\nend\n\nfunction mxsparse(ty::Type{Bool}, m::Integer, n::Integer, nzmax::Integer)\n    pm = ccall(mx_create_sparse_logical[], Ptr{Cvoid},\n        (mwSize, mwSize, mwSize), m, n, nzmax)\n    MxArray(pm)\nend\n\nfunction _copy_sparse_mat(\n    a::SparseMatrixCSC{V,I},\n    ir_p::Ptr{mwIndex},\n    jc_p::Ptr{mwIndex},\n    pr_p::Ptr{Float64},\n    pi_p::Ptr{Float64},\n) where {V<:ComplexF64,I}\n    colptr::Vector{I} = a.colptr\n    rinds::Vector{I} = a.rowval\n    vr::Vector{Float64} = real(a.nzval)\n    vi::Vector{Float64} = imag(a.nzval)\n    n::Int = a.n\n    nnz::Int = length(vr)\n\n    # Note: ir and jc contain zero-based indices\n\n    ir = unsafe_wrap(Array, ir_p, (nnz,))\n    for i = 1:nnz\n        ir[i] = rinds[i] - 1\n    end\n\n    jc = unsafe_wrap(Array, jc_p, (n + 1,))\n    for i = 1:(n+1)\n        jc[i] = colptr[i] - 1\n    end\n\n    copyto!(unsafe_wrap(Array, pr_p, (nnz,)), vr)\n    copyto!(unsafe_wrap(Array, pi_p, (nnz,)), vi)\nend\n\nfunction _copy_sparse_mat(\n    a::SparseMatrixCSC{V,I},\n    ir_p::Ptr{mwIndex},\n    jc_p::Ptr{mwIndex},\n    pr_p::Ptr{V},\n) where {V,I}\n    colptr::Vector{I} = a.colptr\n    rinds::Vector{I} = a.rowval\n    v::Vector{V} = a.nzval\n    n::Int = a.n\n    nnz::Int = length(v)\n\n    # Note: ir and jc contain zero-based indices\n\n    ir = unsafe_wrap(Array, ir_p, (nnz,))\n    for i = 1:nnz\n        ir[i] = rinds[i] - 1\n    end\n\n    jc = unsafe_wrap(Array, jc_p, (n + 1,))\n    for i = 1:(n+1)\n        jc[i] = colptr[i] - 1\n    end\n\n    copyto!(unsafe_wrap(Array, pr_p, (nnz,)), v)\nend\n\nfunction mxarray(a::SparseMatrixCSC{V,I}) where {V<:Union{Float64,ComplexF64,Bool},I}\n    m::Int = a.m\n    n::Int = a.n\n    nnz = length(a.nzval)\n    @assert nnz == a.colptr[n+1] - 1\n\n    mx = mxsparse(V, m, n, nnz)\n    ir_p = ccall(mx_get_ir[], Ptr{mwIndex}, (Ptr{Cvoid},), mx)\n    jc_p = ccall(mx_get_jc[], Ptr{mwIndex}, (Ptr{Cvoid},), mx)\n\n    if V <: ComplexF64\n        pr_p = ccall(mx_get_pr[], Ptr{Float64}, (Ptr{Cvoid},), mx)\n        pi_p = ccall(mx_get_pi[], Ptr{Float64}, (Ptr{Cvoid},), mx)\n        _copy_sparse_mat(a, ir_p, jc_p, pr_p, pi_p)\n    else\n        pr_p = ccall(mx_get_pr[], Ptr{V}, (Ptr{Cvoid},), mx)\n        _copy_sparse_mat(a, ir_p, jc_p, pr_p)\n    end\n    return mx\nend\n\n# char arrays and string\n\nfunction mxarray(s::String)\n    utf16string = transcode(UInt16, s)\n    pm = ccall(mx_create_char_array[], Ptr{Cvoid}, (mwSize, Ptr{mwSize}), 2,\n        _dims_to_mwSize((1, length(utf16string))))\n    mx = MxArray(pm)\n    ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), data_ptr(mx), utf16string,\n        length(utf16string) * sizeof(UInt16))\n    return mx\nend\n\n# cell arrays\n\nfunction mxcellarray(dims::Tuple{Vararg{Integer,N}}) where {N}\n    pm = ccall(mx_create_cell_array[], Ptr{Cvoid}, (mwSize, Ptr{mwSize}),\n        N, _dims_to_mwSize(dims))\n    MxArray(pm)\nend\nmxcellarray(dims::Integer...) = mxcellarray(dims)\n\nfunction get_cell(mx::MxArray, i::Integer)\n    pm = ccall(mx_get_cell[], Ptr{Cvoid}, (Ptr{Cvoid}, mwIndex), mx, i - 1)\n    MxArray(pm, false)\nend\n\nfunction set_cell(mx::MxArray, i::Integer, v::MxArray)\n    v.own = false\n    ccall(mx_set_cell[], Cvoid, (Ptr{Cvoid}, mwIndex, Ptr{Cvoid}), mx, i - 1, v)\n    return nothing\nend\n\nfunction mxcellarray(a::Array)\n    pm = mxcellarray(size(a))\n    for i = 1:length(a)\n        set_cell(pm, i, mxarray(a[i]))\n    end\n    return pm\nend\n\nmxarray(a::Array) = mxcellarray(a)\n\n# struct arrays\n\nfunction _fieldname_array(fieldnames::String...)\n    n = length(fieldnames)\n    a = Vector{Ptr{UInt8}}(undef, n)\n    for i = 1:n\n        a[i] = unsafe_convert(Ptr{UInt8}, fieldnames[i])\n    end\n    return a\nend\n\nfunction mxstruct(fns::Vector{String})\n    a = _fieldname_array(fns...)\n    pm = ccall(mx_create_struct_matrix[], Ptr{Cvoid},\n        (mwSize, mwSize, Cint, Ptr{Ptr{UInt8}}), 1, 1, length(a), a)\n    MxArray(pm)\nend\n\nfunction mxstruct(fn1::String, fnr::String...)\n    a = _fieldname_array(fn1, fnr...)\n    pm = ccall(mx_create_struct_matrix[], Ptr{Cvoid},\n        (mwSize, mwSize, Cint, Ptr{Ptr{UInt8}}), 1, 1, length(a), a)\n    MxArray(pm)\nend\n\nfunction set_field(mx::MxArray, i::Integer, f::String, v::MxArray)\n    v.own = false\n    ccall(\n        mx_set_field[],\n        Cvoid,\n        (Ptr{Cvoid}, mwIndex, Ptr{UInt8}, Ptr{Cvoid}),\n        mx,\n        i - 1,\n        f,\n        v,\n    )\n    return nothing\nend\n\nset_field(mx::MxArray, f::String, v::MxArray) = set_field(mx, 1, f, v)\n\nfunction get_field(mx::MxArray, i::Integer, f::String)\n    pm = ccall(mx_get_field[], Ptr{Cvoid}, (Ptr{Cvoid}, mwIndex, Ptr{UInt8}), mx, i - 1, f)\n    pm == C_NULL && throw(ArgumentError(\"Failed to get field.\"))\n    MxArray(pm, false)\nend\n\nget_field(mx::MxArray, f::String) = get_field(mx, 1, f)\n\nfunction get_field(mx::MxArray, i::Integer, fn::Integer)\n    pm = ccall(\n        mx_get_field_bynum[],\n        Ptr{Cvoid},\n        (Ptr{Cvoid}, mwIndex, Cint),\n        mx,\n        i - 1,\n        fn - 1,\n    )\n    pm == C_NULL && throw(ArgumentError(\"Failed to get field.\"))\n    MxArray(pm, false)\nend\n\nget_field(mx::MxArray, fn::Integer) = get_field(mx, 1, fn)\n\nfunction get_fieldname(mx::MxArray, i::Integer)\n    p = ccall(mx_get_fieldname[], Ptr{UInt8}, (Ptr{Cvoid}, Cint), mx, i - 1)\n    unsafe_string(p)\nend\n\nconst Pairs = Union{Pair,NTuple{2}}\n\nfunction mxstruct(pairs::Pairs...)\n    nf = length(pairs)\n    fieldnames = Vector{String}(undef, nf)\n    for i = 1:nf\n        fn = pairs[i][1]\n        fieldnames[i] = string(fn)\n    end\n    mx = mxstruct(fieldnames)\n    for i = 1:nf\n        set_field(mx, fieldnames[i], mxarray(pairs[i][2]))\n    end\n    return mx\nend\n\nfunction mxstruct(d::T) where {T}\n    names = fieldnames(T)\n    names_str = map(string, names)\n    mx = mxstruct(names_str...)\n    for i = 1:length(names)\n        set_field(mx, names_str[i], mxarray(getfield(d, names[i])))\n    end\n    return mx\nend\n\nfunction mxstructarray(d::Array{T}) where {T}\n    names = fieldnames(T)\n    names_str = map(string, names)\n    a = _fieldname_array(names_str...)\n\n    pm = ccall(mx_create_struct_array[], Ptr{Cvoid},\n        (mwSize, Ptr{mwSize}, Cint,\n            Ptr{Ptr{UInt8}}), ndims(d), _dims_to_mwSize(size(d)), length(a), a)\n    mx = MxArray(pm)\n\n    for i = 1:length(d), j = 1:length(names)\n        set_field(mx, i, names_str[j], mxarray(getfield(d[i], names[j])))\n    end\n    return mx\nend\n\nmxstruct(d::AbstractDict) = mxstruct(collect(d)...)\nmxarray(d) = mxstruct(d)\n\n###########################################################\n#\n#  convert from MATLAB to Julia\n#\n###########################################################\n\n# use deep-copy from MATLAB variable to Julia array\n# in practice, MATLAB variable often has shorter life-cycle\n\nfunction _jarrayx(fun::String, mx::MxArray, siz::Tuple)\n    if is_numeric(mx) || is_logical(mx)\n        @assert !is_sparse(mx)\n        T = eltype(mx)\n        if is_complex(mx)\n            rdat = unsafe_wrap(Array, real_ptr(mx), siz)\n            idat = unsafe_wrap(Array, imag_ptr(mx), siz)\n            a = complex.(rdat, idat)\n        else\n            a = Array{T}(undef, siz)\n            if !isempty(a)\n                ccall(\n                    :memcpy,\n                    Ptr{Cvoid},\n                    (Ptr{Cvoid}, Ptr{Cvoid}, UInt),\n                    a,\n                    data_ptr(mx),\n                    length(a) * sizeof(T),\n                )\n            end\n        end\n        return a\n        #unsafe_wrap(Array, data_ptr(mx), siz)\n    elseif is_cell(mx)\n        a = Array{Any}(undef, siz)\n        for i = 1:length(a)\n            a[i] = jvalue(get_cell(mx, i))\n        end\n        return a\n    else\n        throw(ArgumentError(\"$(fun) only applies to numeric, logical or cell arrays.\"))\n    end\nend\n\njarray(mx::MxArray) = _jarrayx(\"jarray\", mx, size(mx))\njvector(mx::MxArray) = _jarrayx(\"jvector\", mx, (nelems(mx),))\n\nfunction jmatrix(mx::MxArray)\n    if ndims(mx) != 2\n        throw(ArgumentError(\"jmatrix only applies to MATLAB arrays with ndims == 2.\"))\n    end\n    return _jarrayx(\"jmatrix\", mx, (nrows(mx), ncols(mx)))\nend\n\nfunction jscalar(mx::MxArray)\n    if !(nelems(mx) == 1 && (is_logical(mx) || is_numeric(mx)))\n        throw(\n            ArgumentError(\n                \"jscalar only applies to numeric or logical arrays with exactly one element.\",\n            ),\n        )\n    end\n    @assert !is_sparse(mx)\n    if is_complex(mx)\n        return unsafe_wrap(Array, real_ptr(mx), (1,))[1] +\n               im * unsafe_wrap(Array, imag_ptr(mx), (1,))[1]\n    else\n        return unsafe_wrap(Array, data_ptr(mx), (1,))[1]\n    end\nend\n\nfunction _jsparse(ty::Type{T}, mx::MxArray) where {T<:MxRealNum}\n    m = nrows(mx)\n    n = ncols(mx)\n    ir_ptr = ccall(mx_get_ir[], Ptr{mwIndex}, (Ptr{Cvoid},), mx)\n    jc_ptr = ccall(mx_get_jc[], Ptr{mwIndex}, (Ptr{Cvoid},), mx)\n\n    jc_a::Vector{mwIndex} = unsafe_wrap(Array, jc_ptr, (n + 1,))\n    nnz = jc_a[n+1]\n\n    ir = Vector{Int}(undef, nnz)\n    jc = Vector{Int}(undef, n + 1)\n\n    ir_x = unsafe_wrap(Array, ir_ptr, (nnz,))\n    for i = 1:nnz\n        ir[i] = ir_x[i] + 1\n    end\n\n    jc_x = unsafe_wrap(Array, jc_ptr, (n + 1,))\n    for i = 1:(n+1)\n        jc[i] = jc_x[i] + 1\n    end\n\n    pr_ptr = ccall(mx_get_pr[], Ptr{T}, (Ptr{Cvoid},), mx)\n    pr::Vector{T} = copy(unsafe_wrap(Array, pr_ptr, (nnz,)))\n    if is_complex(mx)\n        pi_ptr = ccall(mx_get_pi[], Ptr{T}, (Ptr{Cvoid},), mx)\n        pi::Vector{T} = copy(unsafe_wrap(Array, pi_ptr, (nnz,)))\n        return SparseMatrixCSC(m, n, jc, ir, pr + im .* pi)\n    else\n        return SparseMatrixCSC(m, n, jc, ir, pr)\n    end\nend\n\nfunction jsparse(mx::MxArray)\n    if !is_sparse(mx)\n        throw(ArgumentError(\"jsparse only applies to sparse matrices.\"))\n    end\n    return _jsparse(eltype(mx), mx)\nend\n\nfunction String(mx::MxArray)\n    if !(\n        classid(mx) == mxCHAR_CLASS && ((ndims(mx) == 2 && nrows(mx) == 1) || is_empty(mx))\n    )\n        throw(\n            ArgumentError(\n                \"String(mx::MxArray) only applies to strings (i.e. char vectors)\",\n            ),\n        )\n    end\n    return transcode(String, unsafe_wrap(Array, Ptr{UInt16}(data_ptr(mx)), ncols(mx)))\nend\n\nfunction Dict(mx::MxArray)\n    if !(is_struct(mx) && nelems(mx) == 1)\n        throw(ArgumentError(\"Dict(mx::MxArray) only applies to a single struct\"))\n    end\n    nf = mxnfields(mx)\n    fnames = Vector{String}(undef, nf)\n    fvals = Vector{Any}(undef, nf)\n    for i = 1:nf\n        fnames[i] = get_fieldname(mx, i)\n        pv =\n            ccall(\n                mx_get_field_bynum[],\n                Ptr{Cvoid},\n                (Ptr{Cvoid}, mwIndex, Cint),\n                mx,\n                0,\n                i - 1,\n            )\n        fx = MxArray(pv, false)\n        fvals[i] = jvalue(fx)\n    end\n    Dict(zip(fnames, fvals))\nend\n\nfunction jvalue(mx::MxArray)\n    if is_numeric(mx) || is_logical(mx)\n        if !is_sparse(mx)\n            nelems(mx) == 1 ? jscalar(mx) :\n            ndims(mx) == 2 ? (ncols(mx) == 1 ? jvector(mx) : jmatrix(mx)) :\n            jarray(mx)\n        else\n            jsparse(mx)\n        end\n    elseif is_char(mx) && (nrows(mx) == 1 || is_empty(mx))\n        String(mx)\n    elseif is_cell(mx)\n        ndims(mx) == 2 ? (ncols(mx) == 1 ? jvector(mx) : jmatrix(mx)) :\n        jarray(mx)\n    elseif is_struct(mx) && nelems(mx) == 1\n        Dict(mx)\n    else\n        throw(ArgumentError(\"Unsupported kind of variable.\"))\n    end\nend\n\n# deep conversion from MATLAB variable to Julia array\n\njvalue(mx::MxArray, ::Type{Array}) = jarray(mx)\njvalue(mx::MxArray, ::Type{Vector}) = jvector(mx)\njvalue(mx::MxArray, ::Type{Matrix}) = jmatrix(mx)\njvalue(mx::MxArray, ::Type{Number}) = jscalar(mx)::Number\njvalue(mx::MxArray, ::Type{String}) = String(mx)\njvalue(mx::MxArray, ::Type{Dict}) = Dict(mx)\njvalue(mx::MxArray, ::Type{SparseMatrixCSC}) = jsparse(mx)\n\n# legacy support (eventually drop, when all constructors added)\njdict(mx::MxArray) = Dict(mx)\njstring(mx::MxArray) = String(mx)\n"
  },
  {
    "path": "test/engine.jl",
    "content": "using MATLAB\nusing Test\nusing SparseArrays\n\n# test engine\n\nrestart_default_msession()\n\na = [1.0 2.0 3.0; 4.0 5.0 6.0]\nb = [2.0 3.0 4.0; 8.0 7.0 6.0]\n\n@mput a b\nmat\"\"\"\n    r1 = a .* b;\n    r2 = a + b;\n\"\"\"\n@mget r1 r2\n\n@test isequal(r1, a .* b)\n@test isequal(r2, a + b)\n\n@mget r1::Vector\n@test isequal(r1, vec(a .* b))\n\ns = sparse([1.0 0.0 0.0; 2.0 3.0 0.0; 0.0 4.0 5.0])\nput_variable(:s, s)\ns2 = get_variable(:s)\n@test isequal(s, s2)\n\n# mxcall\n\nr = mxcall(:plus, 1, a, b)\n@test isequal(r, a + b)\n\n(xx, yy) = mxcall(:meshgrid, 2, [1.0, 2.0], [3.0, 4.0])\n@test isequal(xx, [1.0 2.0; 1.0 2.0])\n@test isequal(yy, [3.0 3.0; 4.0 4.0])\n\nclose_default_msession()\n\n# test for segfault\n\ns = MSession()\nclose(s)\n@test_throws UndefRefError close(s)\n\n# segfault on deleted references\nx = mxarray(3.0)\ndelete(x)\n@test_throws UndefRefError delete(x)\n@test_throws UndefRefError nrows(x)\n@test_throws UndefRefError is_numeric(x)\n@test_throws UndefRefError jscalar(x)\n@test_throws UndefRefError jvalue(x)\n\n# make sure restart_default_msession() doesn't error on null references on\n# default msession\ns = get_default_msession()\nclose(s)\nrestart_default_msession()\n"
  },
  {
    "path": "test/matfile.jl",
    "content": "using MATLAB\nusing Test\n\n# test MMAT file I/O\nfn = \"$(tempname()).mat\"\n\na32 = Int32[1 2 3; 4 5 6]\na64 = Int64[1 2 3; 4 5 6]\nb = [1.2, 3.4, 5.6, 7.8]\nc = [[0.0, 1.0], [1.0, 2.0], [1.0, 2.0, 3.0]]\nd = Dict(\"name\" => \"MATLAB\", \"score\" => 100.0)\n\nstruct S\n    x::Float64\n    y::Bool\n    z::Vector{Float64}\nend\n\nss = S[S(1.0, true, [1.0, 2.0]), S(2.0, false, [3.0, 4.0])]\n\nwrite_matfile(fn; a32=a32, a64=a64, b=b, c=c, d=d, ss=mxstructarray(ss))\n\nr = read_matfile(fn)\n@test isa(r, Dict{String,MxArray})\n@test length(r) == 6\n\nra32 = jmatrix(r[\"a32\"])\nra64 = jmatrix(r[\"a64\"])\nrb = jvector(r[\"b\"])\nrc = jvalue(r[\"c\"])\nrd = jdict(r[\"d\"])\nrss = r[\"ss\"]\n\nGC.gc()  # make sure that ra, rb, rc, rd remain valid\n\n@test ra32 == a32\n@test ra64 == a64\n@test rb == b\n@test rc == c\n\n@test rd[\"name\"] == d[\"name\"]\n@test rd[\"score\"] == d[\"score\"]\n\n@test is_struct(rss)\n@test jscalar(get_field(rss, 1, \"x\")) == 1.0\n@test jscalar(get_field(rss, 1, \"y\"))\n@test jvector(get_field(rss, 1, \"z\")) == ss[1].z\n@test jscalar(get_field(rss, 2, \"x\")) == 2.0\n@test !jscalar(get_field(rss, 2, \"y\"))\n@test jvector(get_field(rss, 2, \"z\")) == ss[2].z\n\n# segfault on deleted references\ns = MatFile(fn)\nclose(s)\n@test_throws UndefRefError close(s)\n\nrm(fn)\n"
  },
  {
    "path": "test/matstr.jl",
    "content": "using MATLAB\nusing Test\n\n@test mat\"1\" == 1\n@test mat\"[1, 2, 3]\" == [1 2 3]\n\n# Test interpolation\nx = 1\n@test mat\"$x + 1\" == 2\n\nret = mat\"$y = $x + 2\"\n@test ret === nothing\n@test y == 3\n\nret = mat\"$y = $(x + 3)\"\n@test ret === nothing\n@test y == 4\n\nx = 5\n@test mat\"$x == 5\"\n\n# Test assignment\nx = [1, 2, 3, 4, 5]\nret = mat\"$x(1:3) = 1\"\n@test ret === nothing\n@test x == [1, 1, 1, 4, 5]\nret = mat\"$(x[1:3]) = 2\"\n@test ret === nothing\n@test x == [2, 2, 2, 4, 5]\n\n# Test a more complicated case with assignments on LHS and RHS\nx = 20\nmat\"\"\"\nfor i = 1:10\n   $x = $x + 1;\nend\n\"\"\"\n\n# Test assignment then use\nret = mat\"\"\"\n$z = 5;\n$q = $z;\n\"\"\"\n@test ret === nothing\n@test z == 5\n@test q == 5\n\n# Test multiple assignment\nret = mat\"[$a, $b] = sort([4, 3])\"\n@test ret === nothing\n@test a == [3 4]\n@test b == [2 1]\n\n# Test comments\na = 5\n@test mat\"$a + 1; % = 2\" == 6\n\n# Test indexing\nc = [1, 2]\n@test mat\"$c($c == 2)\" == 2\n\n# Test line continuations\nret = mat\"\"\"\n$d ...\n= 3\n\"\"\"\n@test ret === nothing\n@test d == 3\n\n# Test strings with =\ntext = \"hello = world\"\n@test mat\"strfind($text, 'o = w')\" == 5\n\n@testset \"Propagate Matlab Exceptions\" begin\n\n    # Checks should be enabled by default\n    @test MATLAB.has_exception_check_enabled() == true\n\n    # Test invalid command\n    @test_throws MATLAB.MatlabException mat\"invalid_command\"\n\n    # Test invalid assignment\n    @test_throws MATLAB.MatlabException mat\"1 = 2\"\n\n    # Test invalid command within a block\n    @test_throws MATLAB.MatlabException mat\"\"\"\n    xyz = 1 + 2;\n    invalid_command;\n    abc = 2 * xyz;\n    \"\"\"\n\n    # Disable Checks\n    MATLAB.disable_exception_check!()\n    @test MATLAB.has_exception_check_enabled() == false\n\n    # Test invalid command\n    try\n        mat\"invalid_command\"\n    catch ex\n        @test false # should not throw an exception\n    end\nend\n"
  },
  {
    "path": "test/mxarray.jl",
    "content": "using MATLAB\nusing Test\nusing SparseArrays\n\n# Unit testing for MxArray\n\nm = 5\nn = 6\n\n# test basic types in 1D & 2D\n\nmacro mx_test_basic_types(ty, testfun)\n    quote\n        a = mxarray($(ty), n)\n        @test elsize(a) == sizeof($(ty))\n        @test eltype(a) === $(ty)\n        @test nrows(a) == n\n        @test ncols(a) == 1\n        @test nelems(a) == n\n        @test ndims(a) == 2\n        @test size(a) == (n, 1)\n        @test size(a, 1) == n\n        @test size(a, 2) == 1\n        @test size(a, 3) == 1\n        @test !is_complex(a)\n        @test $(testfun)(a)\n        delete(a)\n\n        b = mxarray($(ty), m, n)\n        @test elsize(b) == sizeof($(ty))\n        @test eltype(b) === $(ty)\n        @test nrows(b) == m\n        @test ncols(b) == n\n        @test nelems(b) == m * n\n        @test ndims(b) == 2\n        @test size(b) == (m, n)\n        @test size(b, 1) == m\n        @test size(b, 2) == n\n        @test size(b, 3) == 1\n        @test !is_complex(b)\n        @test $(testfun)(b)\n        delete(b)\n        return nothing\n    end\nend\n\n# empty array\n\na = mxarray(Float64, 0, 0)\n@test nrows(a) == 0\n@test ncols(a) == 0\n@test nelems(a) == 0\n@test ndims(a) == 2\n@test eltype(a) == Float64\n@test is_empty(a)\n\n# basic arrays\n\n@mx_test_basic_types Float64 is_double\n@mx_test_basic_types Float32 is_single\n@mx_test_basic_types Int64 is_int64\n@mx_test_basic_types UInt64 is_uint64\n@mx_test_basic_types Int32 is_int32\n@mx_test_basic_types UInt32 is_uint32\n@mx_test_basic_types Int16 is_int16\n@mx_test_basic_types UInt16 is_uint16\n@mx_test_basic_types Int8 is_int8\n@mx_test_basic_types UInt8 is_uint8\n@mx_test_basic_types Bool is_logical\n\n# complex arrays\n\nmacro mx_test_complex_type(ty, testfun)\n    quote\n        b = mxarray(Complex{$(ty)}, m, n)\n        @test elsize(b) == sizeof($(ty))\n        @test eltype(b) === $(ty)\n        @test nrows(b) == m\n        @test ncols(b) == n\n        @test nelems(b) == m * n\n        @test ndims(b) == 2\n        @test size(b) == (m, n)\n        @test size(b, 1) == m\n        @test size(b, 2) == n\n        @test size(b, 3) == 1\n        @test is_complex(b)\n        @test $(testfun)(b)\n        delete(b)\n        return nothing\n    end\nend\n@mx_test_complex_type Float64 is_double\n@mx_test_complex_type Float32 is_single\n\n# test creating multi-dimensional arrays\n\na = mxarray(Float64, (6, 5, 4))\n@test elsize(a) == sizeof(Float64)\n@test eltype(a) === Float64\n@test size(a) == (6, 5, 4)\n@test size(a, 1) == 6\n@test size(a, 2) == 5\n@test size(a, 3) == 4\n@test size(a, 4) == 1\n@test nelems(a) == 6 * 5 * 4\n@test is_numeric(a)\n@test !is_sparse(a)\n\na = mxarray(Bool, (6, 5, 4))\n@test elsize(a) == 1\n@test eltype(a) === Bool\n@test size(a) == (6, 5, 4)\n@test size(a, 1) == 6\n@test size(a, 2) == 5\n@test size(a, 3) == 4\n@test size(a, 4) == 1\n@test nelems(a) == 6 * 5 * 4\n@test is_logical(a)\n@test !is_sparse(a)\n\n# scalars\n\na_mx = mxarray(3.25)\n@test eltype(a_mx) == Float64\n@test size(a_mx) == (1, 1)\n@test jscalar(a_mx) == 3.25\ndelete(a_mx)\n\na_mx = mxarray(Int32(12))\n@test eltype(a_mx) == Int32\n@test size(a_mx) == (1, 1)\n@test jscalar(a_mx) == Int32(12)\ndelete(a_mx)\n\na_mx = mxarray(true)\n@test eltype(a_mx) == Bool\n@test size(a_mx) == (1, 1)\n@test jscalar(a_mx)\ndelete(a_mx)\n\na_mx = mxarray(false)\n@test eltype(a_mx) == Bool\n@test size(a_mx) == (1, 1)\n@test !jscalar(a_mx)\ndelete(a_mx)\n\na_mx = mxarray(3.25 + 4im)\n@test eltype(a_mx) == Float64\n@test size(a_mx) == (1, 1)\n@test jscalar(a_mx) == 3.25 + 4im\ndelete(a_mx)\n\n# conversion between Julia and MATLAB numeric arrays\n\na = rand(5, 6)\na_mx = mxarray(a)\na2 = jarray(a_mx)\n@test isequal(a, a2)\ndelete(a_mx)\n\na = rand(5)\na_mx = mxarray(a)\na2 = jvector(a_mx)\n@test isequal(a, a2)\ndelete(a_mx)\n\na_t = reshape(a, 1, 5)\na_mx = mxarray(a_t)\na2 = jvector(a_mx)\n@test isequal(a, a2)\ndelete(a_mx)\n\na = 1:5\na_mx = mxarray(a)\na2 = jvector(a_mx)\n@test isequal([1:5;], a2)\ndelete(a_mx)\n\na = rand(5, 6) + rand(5, 6) * im\na_mx = mxarray(a)\na2 = jarray(a_mx)\n@test isequal(a, a2)\ndelete(a_mx)\n\n# sparse matrices\n\na = sprand(8, 9, 0.2)\na_mx = mxarray(a)\n@test is_double(a_mx)\n@test is_sparse(a_mx)\n@test nrows(a_mx) == 8\n@test ncols(a_mx) == 9\n\na2 = jsparse(a_mx)\n@test size(a2) == (8, 9)\n@test count(!iszero, a2) == count(!iszero, a)\n@test isequal(a2, a)\ndelete(a_mx)\n\na = sparse(convert(Array{Bool}, rand(8, 9) .< 0.3))\na_mx = mxarray(a)\n@test is_logical(a_mx)\n@test is_sparse(a_mx)\n@test nrows(a_mx) == 8\n@test ncols(a_mx) == 9\n\na2 = jsparse(a_mx)\n@test size(a2) == (8, 9)\n@test count(!iszero, a2) == count(!iszero, a)\n@test isequal(a2, a)\ndelete(a_mx)\n\na = sparse([1.0 1.0im])\na_mx = mxarray(a)\n@test is_sparse(a_mx)\n@test is_double(a_mx)\n@test is_complex(a_mx)\n@test nrows(a_mx) == 1\n@test ncols(a_mx) == 2\ndelete(a_mx)\n\n# strings\n\ns = \"MATLAB.jl\"\ns_mx = mxarray(s)\n@test classid(s_mx) == MATLAB.mxCHAR_CLASS\n@test nrows(s_mx) == 1\n@test ncols(s_mx) == length(s)\n@test nelems(s_mx) == length(s)\n@test ndims(s_mx) == 2\n@test is_char(s_mx)\n\ns2 = jstring(s_mx)\n@test s == s2\ndelete(s_mx)\n\ns = \"\"\ns_mx = mxarray(s)\n@test classid(s_mx) == MATLAB.mxCHAR_CLASS\n@test is_char(s_mx)\n@test is_empty(s_mx)\n\ns2 = jstring(s_mx)\n@test s == s2\ndelete(s_mx)\n\n# cell arrays\n\na = mxcellarray(10)\n@test nrows(a) == 10\n@test ncols(a) == 1\n@test nelems(a) == 10\n@test classid(a) == MATLAB.mxCELL_CLASS\n@test is_cell(a)\ndelete(a)\n\na = mxcellarray(4, 5)\n@test nrows(a) == 4\n@test ncols(a) == 5\n@test nelems(a) == 20\n@test classid(a) == MATLAB.mxCELL_CLASS\n@test is_cell(a)\ndelete(a)\n\na = mxcellarray((3, 4, 5))\n@test size(a) == (3, 4, 5)\n@test nelems(a) == 60\n@test classid(a) == MATLAB.mxCELL_CLASS\n@test is_cell(a)\ndelete(a)\n\ns = [\"abc\", \"efg\"]\ns_mx = mxcellarray(s)\n@test jstring(get_cell(s_mx, 1)) == \"abc\"\n@test jstring(get_cell(s_mx, 2)) == \"efg\"\ndelete(s_mx)\n\n# struct\n\na = mxstruct(\"abc\", \"efg\", \"xyz\")\n@test is_struct(a)\n@test mxnfields(a) == 3\n@test nrows(a) == 1\n@test ncols(a) == 1\n@test nelems(a) == 1\n@test ndims(a) == 2\n\n@test get_fieldname(a, 1) == \"abc\"\n@test get_fieldname(a, 2) == \"efg\"\n@test get_fieldname(a, 3) == \"xyz\"\ndelete(a)\n\ns = Dict(\"name\" => \"MATLAB\", \"version\" => 12.0, \"data\" => [1, 2, 3])\na = mxstruct(s)\n@test is_struct(a)\n@test mxnfields(a) == 3\n@test jstring(get_field(a, \"name\")) == \"MATLAB\"\n@test jscalar(get_field(a, \"version\")) == 12.0\n@test isequal(jvector(get_field(a, \"data\")), [1, 2, 3])\ndelete(a)\n\nmutable struct TestType\n    name::String\n    version::Float64\n    data::Vector{Int}\nend\nt = TestType(\"MATLAB\", 12.0, [1, 2, 3])\na = mxstruct(t)\n@test is_struct(a)\n@test mxnfields(a) == 3\n@test jstring(get_field(a, \"name\")) == \"MATLAB\"\n@test jscalar(get_field(a, \"version\")) == 12.0\n@test isequal(jvector(get_field(a, \"data\")), [1, 2, 3])\ndelete(a)\n\na = mxstructarray([TestType(\"MATLAB\", 12.0, [1, 2, 3]),\n    TestType(\"Julia\", 0.2, [4, 5, 6])])\n@test is_struct(a)\n@test mxnfields(a) == 3\n@test jstring(get_field(a, 1, \"name\")) == \"MATLAB\"\n@test jscalar(get_field(a, 1, \"version\")) == 12.0\n@test isequal(jvector(get_field(a, 1, \"data\")), [1, 2, 3])\n@test jstring(get_field(a, 2, \"name\")) == \"Julia\"\n@test jscalar(get_field(a, 2, \"version\")) == 0.2\n@test isequal(jvector(get_field(a, 2, \"data\")), [4, 5, 6])\ndelete(a)\n\n# bi-directional conversions\n\nx = mxarray(12.0)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Float64)\n@test y == 12.0\n\na = rand(5)\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Vector{Float64})\n@test isequal(y, a)\n\na = rand(3, 4)\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Matrix{Float64})\n@test isequal(y, a)\n\na = rand(3, 4, 5)\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Array{Float64,3})\n@test isequal(y, a)\n\na = sparse([1.0 2.0im; 0 -1.0im])\na_mx = mxarray(a)\na_jl = jvalue(a_mx)\ndelete(a_mx)\n@test a == a_jl\n@test isa(a_jl, SparseMatrixCSC{Complex{Float64}})\n\n##############################\n# Abstract Array Conversions\n##############################\n\na = transpose(rand(10))\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Array{Float64,2})\n@test size(y) == size(a)\n@test isequal(y, a)\n\na = rand(10, 10)\na_ = @view a[3:7, 4:8]\nx = mxarray(a_)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Array{Float64,2})\n@test size(y) == size(a_)\n@test isequal(y, a_)\n\na_ = rand(ComplexF32, 10, 10, 10)\na = @view a_[3:7, 4:8, 2:5]\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Array{ComplexF32,3})\n@test size(y) == size(a)\n\na = 1:100 # range\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Array{Int64,1})\n@test isequal(y, collect(a))\n\na = BitArray(rand(Bool, 5, 20, 10))\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Array{Bool,3})\n@test isequal(y, a)\n\n# Issue: Tuples converted to MATLAB structs\n# https://github.com/JuliaInterop/MATLAB.jl/issues/178\na = (2.5, 2.6)\nx = mxarray(a)\ny = jvalue(x)\n@test classid(x) == MATLAB.mxDOUBLE_CLASS\n@test nrows(x) == 2\n@test ncols(x) == 1\ndelete(x)\n@test isa(y, Vector{Float64})\n@test isequal(y, collect(a))\n\n# Tuple with mixed types\na = (1, 2.0, \"MATLAB\", [1, 2, 3])\nx = mxarray(a)\ny = jvalue(x)\n@test nrows(x) == 4\n@test ncols(x) == 1\n@test classid(x) == MATLAB.mxCELL_CLASS\n@test isa(y, Vector{Any})\n@test length(y) == length(a)\n@test isequal(y, collect(a))\n\n##############################\n# String Conversions\n##############################\n\na = \"MATLAB\"\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, String)\n@test y == a\n\na = [\"abc\", 3, \"efg\"]\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Vector{Any})\n@test length(y) == 3\n@test y[1] == a[1]\n@test y[2] == a[2]\n@test y[3] == a[3]\n\na = Dict(\"abc\" => 10.0, \"efg\" => [1, 2, 3], \"xyz\" => \"MATLAB\")\nx = mxarray(a)\ny = jvalue(x)\ndelete(x)\n@test isa(y, Dict{String,Any})\n\n@test y[\"abc\"] == 10.0\n@test isequal(y[\"efg\"], [1, 2, 3])\n@test y[\"xyz\"] == \"MATLAB\"\n\n# Test string encoding\nstr = \"λ α γ\"\n@test jstring(mxarray(str)) == str\n@test mat\"all($str == [955 32 945 32 947])\"\n\nGC.gc()\n"
  },
  {
    "path": "test/runtests.jl",
    "content": "using MATLAB\nusing Test\n\nis_ci() = lowercase(get(ENV, \"CI\", \"false\")) == \"true\"\n\nif !is_ci() # only test if not CI\n    include(\"engine.jl\")\n    include(\"matfile.jl\")\n    include(\"matstr.jl\")\n    include(\"mxarray.jl\")\nend\n"
  }
]