[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[Bug]\"\nlabels: bug, Needs Priority\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches: [ \"main\", \"dev\" ]\n  pull_request:\n    branches: [ \"main\", \"dev\" ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: configure\n      run:  |\n        sudo apt update\n        sudo apt install libsuitesparse-dev\n        sudo apt install liblapacke-dev\n        python -m pip install --upgrade pip\n        python -m pip install regex colored\n    - name: make\n      run: (mkdir build; cd build; cmake ..; sudo make install)\n"
  },
  {
    "path": ".github/workflows/buildandtest.yml",
    "content": "name: Build and Test\n\non:\n  push:\n    branches: [ \"main\", \"dev\" ]\n  pull_request:\n    branches: [ \"main\", \"dev\" ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: configure\n      run:  |\n        sudo apt update\n        sudo apt install libsuitesparse-dev\n        sudo apt install liblapacke-dev\n        sudo apt install libunistring-dev\n        python -m pip install --upgrade pip\n        python -m pip install regex colored\n    - name: make\n      run: |\n        mkdir build\n        cd build\n        cmake -DCMAKE_BUILD_TYPE=Release ..\n        sudo make install\n        sudo mkdir /usr/local/lib/morpho\n    - name: getcli\n      run: |\n        git clone https://github.com/Morpho-lang/morpho-cli.git\n        cd morpho-cli \n        mkdir build\n        cd build\n        cmake ..\n        sudo make install\n    - name: test \n      run: |\n        cd test\n        python3 test.py -c\n"
  },
  {
    "path": ".github/workflows/buildandtestmultithreaded.yml",
    "content": "name: TestMultiThreaded\n\non:\n  push:\n    branches: [ \"main\", \"dev\" ]\n  pull_request:\n    branches: [ \"main\", \"dev\" ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: configure\n      run:  |\n        sudo apt update\n        sudo apt install libsuitesparse-dev\n        sudo apt install liblapacke-dev\n        sudo apt install libunistring-dev\n        python -m pip install --upgrade pip\n        python -m pip install regex colored\n    - name: make\n      run: |\n        mkdir build\n        cd build\n        cmake -DCMAKE_BUILD_TYPE=Release ..\n        sudo make install\n        sudo mkdir /usr/local/lib/morpho\n    - name: getcli\n      run: |\n        git clone https://github.com/Morpho-lang/morpho-cli.git\n        cd morpho-cli \n        mkdir build\n        cd build\n        cmake ..\n        sudo make install\n    - name: test \n      run: |\n        cd test\n        python3 test.py -c -m\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL Advanced\"\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n  schedule:\n    - cron: '24 4 * * 5'\n\njobs:\n  analyze:\n    name: Analyze (${{ matrix.language }})\n    # Runner size impacts CodeQL analysis time. To learn more, please see:\n    #   - https://gh.io/recommended-hardware-resources-for-running-codeql\n    #   - https://gh.io/supported-runners-and-hardware-resources\n    #   - https://gh.io/using-larger-runners (GitHub.com only)\n    # Consider using larger runners or machines with greater resources for possible analysis time improvements.\n    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}\n    permissions:\n      # required for all workflows\n      security-events: write\n\n      # required to fetch internal or private CodeQL packs\n      packages: read\n\n      # only required for workflows in private repositories\n      actions: read\n      contents: read\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n        - language: c-cpp\n          build-mode: manual\n        # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'\n        # Use `c-cpp` to analyze code written in C, C++ or both\n        # Use 'java-kotlin' to analyze code written in Java, Kotlin or both\n        # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both\n        # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,\n        # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.\n        # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how\n        # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n        build-mode: ${{ matrix.build-mode }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n        # queries: security-extended,security-and-quality\n\n    # If the analyze step fails for one of the languages you are analyzing with\n    # \"We were unable to automatically build your code\", modify the matrix above\n    # to set the build mode to \"manual\" for that language. Then modify this step\n    # to build your code.\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n    - if: matrix.build-mode == 'manual'\n      shell: bash\n      run: |\n        sudo apt update\n        sudo apt install libsuitesparse-dev\n        sudo apt install liblapacke-dev\n        python -m pip install --upgrade pip\n        python -m pip install regex colored\n        mkdir build\n        cd build\n        cmake ..\n        sudo make install\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/examples.yml",
    "content": "name: Examples\n\non:\n  push:\n    branches: [ \"main\", \"dev\" ]\n  pull_request:\n    branches: [ \"main\", \"dev\" ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: configure\n      run:  |\n        sudo apt update\n        sudo apt install libglfw3-dev \n        sudo apt install povray\n        sudo apt install libfreetype6-dev\n        sudo apt install fonts-freefont-ttf\n        sudo apt install libsuitesparse-dev\n        sudo apt install liblapacke-dev\n        sudo apt install libunistring-dev\n        python -m pip install --upgrade pip\n        python -m pip install regex colored\n    - name: make\n      run: |\n        mkdir build\n        cd build\n        cmake -DCMAKE_BUILD_TYPE=Release ..\n        sudo make install\n        sudo mkdir /usr/local/lib/morpho\n    - name: getcli\n      run: |\n        git clone https://github.com/Morpho-lang/morpho-cli.git\n        cd morpho-cli \n        mkdir build\n        cd build\n        cmake ..\n        sudo make install\n    - name: morphoview\n      run: |\n        git clone https://github.com/morpho-lang/morpho-morphoview.git\n        cd morpho-morphoview\n        mkdir build\n        cd build\n        cmake ..\n        sudo make install\n    - name: morphopm \n      run: |\n        git clone https://github.com/Morpho-lang/morpho-morphopm.git\n        cd morpho-morphopm \n        ./morphopm install optimize4 \n    - name: test \n      run: |\n        cd examples\n        python3 examples.py -c\n"
  },
  {
    "path": ".github/workflows/nonanboxing.yml",
    "content": "name: No NANBoxing\n\non:\n  push:\n    branches: [ \"main\", \"dev\" ]\n  pull_request:\n    branches: [ \"main\", \"dev\" ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: configure\n      run:  |\n        sudo apt update\n        sudo apt install libsuitesparse-dev\n        sudo apt install liblapacke-dev\n        sudo apt install libunistring-dev\n        python -m pip install --upgrade pip\n        python -m pip install regex colored\n    - name: make\n      run: |\n        mkdir build\n        cd build\n        cmake -DMORPHO_DISABLENANBOXING=ON ..\n        sudo make install\n        sudo mkdir /usr/local/lib/morpho\n    - name: getcli\n      run: |\n        git clone https://github.com/Morpho-lang/morpho-cli.git\n        cd morpho-cli \n        mkdir build\n        cd build\n        cmake -DMORPHO_DISABLENANBOXING=ON ..\n        sudo make install\n    - name: test \n      run: |\n        cd test\n        python3 test.py -c\n"
  },
  {
    "path": ".gitignore",
    "content": "# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n*.su\n*.idb\n*.pdb\n\n# POVray files\n*.pov\n\n# Kernel Module Compile Results\n*.mod*\n*.cmd\n.tmp_versions/\nmodules.order\nModule.symvers\nMkfile.old\ndkms.conf\n\n.DS_Store\n.entitlements\n.vscode/settings.json\n.vscode/\n\ntest/FailedTests*.txt\n*.png\n*.out\nmanual/src/manual.lyx~\ntest/vtk/data.case2.vtk\ntest/vtk/data.case3.0.vtk\nexamples/qtensor/Qtensor_K_0.01.png\nexamples/qtensor/Qtensor_K_0.01.png\ntest/vtk/data.vtk\ntest/vtk/square.vtk\ntest/vtk/tetrahedron.vtk\ndevguide/devguide.lyx~\nbuild/*\nbuild-xcode/*\nbuild-win/*\n*.valgrind\n/.vs\n"
  },
  {
    "path": ".readthedocs.yml",
    "content": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\n# Required\nversion: 2\n\n# Set the version of Python and other tools you might need\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.9\"\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n   configuration: help/conf.py\n\n# Optionally set the version of Python and requirements required to build your docs\npython:\n   install:\n   - requirements: help/requirements.txt\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "#-------------------------------------------------------------------------------\n# Morpho/CMakeLists.txt\n#-------------------------------------------------------------------------------\n\ncmake_minimum_required(VERSION 3.13)\n\nproject(morpho-libmorpho)\n\n#-------------------------------------------------------------------------------\n# Platform dependent options\n#-------------------------------------------------------------------------------\n\nset(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)\n\nadd_library(morpho SHARED \"\") \n\n#-------------------------------------------------------------------------------\n# Options\n#-------------------------------------------------------------------------------\n\noption(MORPHO_DISABLENANBOXING \"Disables NAN Boxing\" OFF)\noption(MORPHO_GCSTRESSTEST \"Stress tests the garbage collector\" OFF)\noption(MORPHO_BUILD_LINALG \"Builds with linear algebra\" ON)\noption(MORPHO_BUILD_SPARSE \"Builds with sparse matrix support\" ON)\noption(MORPHO_BUILD_GEOMETRY \"Builds with geometry library\" ON)\n\n#-------------------------------------------------------------------------------\n# Process options\n#-------------------------------------------------------------------------------\n\n# Option to disable NAN Boxing\nif(MORPHO_DISABLENANBOXING)\ntarget_compile_definitions(morpho PUBLIC _NO_NAN_BOXING)\nendif() \n\n# Option to stress test Garbage Collector\nif(MORPHO_GCSTRESSTEST)\ntarget_compile_definitions(morpho PUBLIC _DEBUG_STRESSGARBAGECOLLECTOR)\nendif() \n\n# Set help directory\nif(MORPHO_HELP_BASEDIR)\ntarget_compile_definitions(morpho PUBLIC MORPHO_HELP_BASEDIR=\\\"${MORPHO_HELP_BASEDIR}\\\")\nendif() \n\n# Set directory for modules supplied with morpho\nif(MORPHO_MODULE_BASEDIR)\ntarget_compile_definitions(morpho PUBLIC MORPHO_MODULE_BASEDIR=\\\"${MORPHO_MODULE_BASEDIR}\\\")\nendif() \n\n# Include geometry as part of the build\nif(MORPHO_BUILD_GEOMETRY)\ntarget_compile_definitions(morpho PUBLIC MORPHO_INCLUDE_GEOMETRY)\nendif()\n\n#-------------------------------------------------------------------------------\n# BLAS and LAPACK\n#-------------------------------------------------------------------------------\n\nif (MORPHO_BUILD_LINALG)\n\nmessage(STATUS \"Searching for BLAS and LAPACK\")\n# Locate a lapack version\n# Currently we prefer LAPACKE \n# TODO: Fix morpho source to select between lapack and lapacke\nfind_library(LAPACK_LIBRARY\n    NAMES lapacke liblapacke lapack liblapack libopenblas\n    HINTS \n        \"C:\\\\Program Files\\\\Morpho\\\\lib\\\\\"\n)\nif (LAPACK_LIBRARY)\nmessage(STATUS \"Found LAPACK at ${LAPACK_LIBRARY}\")\nendif()\n\n# Locate cblas\nfind_library(CBLAS_LIBRARY\n    NAMES cblas libcblas blas libblas openblas libopenblas\n    HINTS\n        \"C:\\\\Program Files\\\\Morpho\\\\lib\\\\\"\n)\nif (CBLAS_LIBRARY)\nmessage(STATUS \"Found blas at ${CBLAS_LIBRARY}\")\nendif()\n\n# Find cblas.h header file \nfind_path(CBLAS_INCLUDE cblas.h \n          HINTS \n            \"C:\\\\Program Files\\\\Morpho\\\\include\\\\lapack\")\n\n# Add cblas headers to include folders\ntarget_include_directories(morpho PUBLIC ${CBLAS_INCLUDE})\n\nmessage(STATUS \"Found cblas headers at ${CBLAS_INCLUDE}\")\n\ntarget_link_libraries(morpho ${CBLAS_LIBRARY})\ntarget_link_libraries(morpho ${LAPACK_LIBRARY})\n\ntarget_compile_definitions(morpho PUBLIC MORPHO_INCLUDE_LINALG)\n\nendif()\n\n#-------------------------------------------------------------------------------\n# SuiteSparse\n#-------------------------------------------------------------------------------\n\nif (MORPHO_BUILD_SPARSE)\nmessage(STATUS \"Searching for Suitesparse\")\n# Locate suitesparse\nfind_library(SUITESPARSE_LIBRARY\n    NAMES cxsparse libcxsparse\n    HINTS \n        \"C:\\\\Program Files\\\\Morpho\\\\lib\\\\\"\n)\nif (SUITESPARSE_LIBRARY)\nmessage(STATUS \"Found suitesparse at ${SUITESPARSE_LIBRARY}\")\nendif()\n\n# Find suitesparse cs.h header file \nfind_file(SUITESPARSE_HEADER cs.h \n          HINTS \n            /home/linuxbrew/.linuxbrew/include/suitesparse \n            /usr/local/include\n            /usr/local/include/suitesparse\n            /opt/homebrew/include/suitesparse\n            /usr/include/suitesparse\n            \"C:\\\\Program Files\\\\Morpho\\\\include\\\\suitesparse\")\n\n# Identify folder that cs.h is located in from SUITESPARSE_HEADER and store in SUITESPARSE_INCLUDE\nget_filename_component(SUITESPARSE_INCLUDE ${SUITESPARSE_HEADER} DIRECTORY)\n\n# Add suitesparse headers to include folders\ntarget_include_directories(morpho PUBLIC ${SUITESPARSE_INCLUDE})\nmessage(STATUS \"Found cs.h at ${SUITESPARSE_INCLUDE}\")\n\ntarget_compile_definitions(morpho PUBLIC MORPHO_INCLUDE_SPARSE)\n\ntarget_link_libraries(morpho ${SUITESPARSE_LIBRARY})\nendif()\n\n#-------------------------------------------------------------------------------\n# Threads\n#-------------------------------------------------------------------------------\n\nset(THREADS_PREFER_PTHREAD_FLAG ON)\nfind_package(Threads REQUIRED)\n\n#-------------------------------------------------------------------------------\n# Add morpho sources\n#-------------------------------------------------------------------------------\n\nadd_subdirectory(src)\n\n# Include morpho header files across the project \ntarget_include_directories(morpho PUBLIC src \n                                  src/builtin\n                                  src/classes\n                                  src/core\n                                  src/datastructures\n                                  src/debug\n                                  src/geometry\n                                  src/linalg\n                                  src/support )\n\n# Create source groups for IDE\nsource_group(\"builtin\" REGULAR_EXPRESSION src/builtin/.*\\\\.[ch])\nsource_group(\"classes\" REGULAR_EXPRESSION src/classes/.*\\\\.[ch])\nsource_group(\"core\" REGULAR_EXPRESSION src/core/.*\\\\.[ch])\nsource_group(\"datastructures\" REGULAR_EXPRESSION src/datastructures/.*\\\\.[ch])\nsource_group(\"debug\" REGULAR_EXPRESSION src/debug/.*\\\\.[ch])\nsource_group(\"geometry\" REGULAR_EXPRESSION src/geometry/.*\\\\.[ch])\nsource_group(\"linalg\" REGULAR_EXPRESSION src/linalg/.*\\\\.[ch])\nsource_group(\"support\" REGULAR_EXPRESSION src/support/.*\\\\.[ch])\n\ntarget_link_libraries(morpho ${CMAKE_DL_LIBS} Threads::Threads)\n\n#-------------------------------------------------------------------------------\n# Install\n#-------------------------------------------------------------------------------\n\n# Install the resulting library\ninstall(TARGETS morpho)\n\n# Install morpho header files\n# The below works in 3.23, which is too recent for Ubuntu\n# install(TARGETS morpho \n#        FILE_SET public_headers \n#        DESTINATION include/morpho\n#)\n\n# So we'll use a hacky way for now\nfile(GLOB_RECURSE MORPHO_HEADER_FILES \"src/*.h\")\ninstall(FILES ${MORPHO_HEADER_FILES} DESTINATION include/morpho)\n\n# Similarly install morpho modules\nfile(GLOB_RECURSE MORPHO_MODULES_FILES \"modules/*.morpho\")\ninstall(FILES ${MORPHO_MODULES_FILES} DESTINATION share/morpho/modules)\n\n# Similarly install morpho help files\nfile(GLOB_RECURSE MORPHO_HELP_FILES \"help/*.md\")\ninstall(FILES ${MORPHO_HELP_FILES} DESTINATION share/morpho/help)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Morpho Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\ntimothy.atherton@tufts.edu.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Morpho\n\nThankyou for your interest in helping to improve Morpho! We welcome contributions from everyone. If you are unsure of anything, feel free to reach out via the Github, submit an issue or make a pull request.\n\nThere are many ways you can contribute to Morpho:\n\n* If you have identified a bug, you can report it by [submitting an issue with the `Bug` label](https://github.com/Morpho-lang/morpho/issues/new?assignees=&labels=bug%2C+Needs+Priority&template=bug_report.md&title=%5BBug%5D). If you have solved a bug, first off, that's excellent, and thank you! You can submit your fix as a pull request to the [dev branch](https://github.com/Morpho-lang/morpho/tree/dev) of Morpho. This branch is where the updates are collected before eventually releasing them into a new version in the main branch. All pull requests to `dev` are passed through the automated testing suite. Check [below](#unit-tests) for more on that and about adding your own tests!\n\n* If you want to propose a new feature in Morpho, you can submit an issue with the [`enhancement`](https://github.com/Morpho-lang/morpho/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) label.\n\n* Consult our [Roadmap](https://github.com/Morpho-lang/morpho/wiki/Road-Map) to see the features we've identified as priorities for upcoming releases of Morpho. If you're interested in working on one of these, reach out to the development team. \n\n* If you use Morpho but are new to GitHub, or to contributing to Morpho, the issues labeled [`good first issue`](https://github.com/Morpho-lang/morpho/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) highlight easy-to-fix bugs that will get you started. [Here](https://gist.github.com/Chaser324/ce0505fbed06b947d962) is a guide that explains the best practices for making a pull request.\n\n* Morpho is highly modular and modules providing new features are especially welcome. The [devguide](https://github.com/Morpho-lang/morpho-devguide) explains how to package morpho code into an easily downloadable module. The [morphopm](https://github.com/Morpho-lang/morpho-morphopm) package manager can be used to download and install modules.\n\n* Help with unit tests, additional documentation etc. are also great ways to contribute to the project.\n\nAll contributors are expected to follow the [Morpho Code of Conduct](https://github.com/Morpho-lang/morpho/blob/main/CODE_OF_CONDUCT.md).\n\nFor further guidance and pointers, a developer's guide is gradually being assembled [devguide](https://github.com/Morpho-lang/morpho-devguide/blob/main/devguide.pdf). We also encourage you to [join our Slack community](https://join.slack.com/t/morphoco/shared_invite/zt-1hiby4iqv-UhqKEeqZih0vSG3k4gEfXQ) or get in touch via email.\n\n## Unit tests\n\nMorpho has an extensive set of unit-tests to make sure any new piece of code doesn't break essential functionality. Moreover, code within any pull requests to `dev` or `main` is automatically put through the test suite. While this will catch failing tests, if any, you can make sure all the tests are passing on your branch beforehand by running the test suite locally:\n\n    cd test\n    python3 test.py\n\nIf you have fixed a new bug, chances are the existing unit-tests didn't capture that buggy behavior. In that case, it's a good idea to _add_ new tests to lock down the behavior. You can add tests simply by adding the test file anywhere under the `test/` directory. The files are organized around topic for convenience, but any file therein will get tested.\n\nWe highly welcome contributions to the testing suite. Try writing tests that don't overlap with the existing tests, and help us lock down any remaining bugs in Morpho's functionality.\n\n### Formatting a unit test\n\nWhile a new unit-testing module is [in the works](https://github.com/Morpho-lang/morpho/pull/147), the current unit-test are executed in `python` by looking for the keyword `expect`. For instance, here is an example from the test `power.morpho` that tests the arithmetic power operator:\n\n    // Test power operator\n    print 2^2\n    // expect: 4\n\nHere, the operation is performed and the output is printed. The special comment that starts with `// expect: ` followed by the expected output is picked up by the python testing file and compared with the output. For multiple `print` statements, the `// expect: ` comments can appear anywhere in the code, as long as they are in the right order. Other comments work as regular comments and can be used to annotate the test.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020-21 T J Atherton\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![Morpho](https://github.com/Morpho-lang/morpho-manual/blob/main/src/Figures/morphologosmall.png#gh-light-mode-only)![Morpho](https://github.com/Morpho-lang/morpho-manual/blob/main/src/Figures/morphologosmall-white.png#gh-dark-mode-only)\n\n[![Build](https://github.com/Morpho-lang/morpho/actions/workflows/build.yml/badge.svg)](https://github.com/Morpho-lang/morpho/actions/workflows/build.yml)\n[![Test suite](https://github.com/Morpho-lang/morpho/actions/workflows/buildandtest.yml/badge.svg)](https://github.com/Morpho-lang/morpho/actions/workflows/buildandtest.yml)\n\nThe Morpho language. Morpho is a programmable environment for shape optimization and scientific computing tasks more generally. Morpho aims to be:\n\n* **Familiar**. Morpho uses syntax similar to other C-family languages. The syntax fits on a postcard, so it's easy to learn.\n* **Fast**. Morpho programs run as efficiently as other well-implemented dynamic languages like *wren* or *lua* (Morpho is often significantly faster than Python, for example). Morpho leverages numerical libraries like *BLAS*, *LAPACK* and *SUITESPARSE* to provide high performance.\n* **Class-based**. Morpho is highly object-oriented, which simplifies coding and enables reusability.\n* **Extendable**. Functionality is easy to add via packages, both in Morpho and in C or other compiled languages. Packages can be downloaded, installed and distributed via the [morphopm](https://github.com/Morpho-lang/morpho-morphopm) package manager.\n\n*Morpho is based upon work supported by the National Science Foundation under grants DMR-1654283 and OAC-2003820.*\n\nIn academic publications, please cite morpho as:\n\n* Joshi, C. et al. \"A programmable environment for shape optimization and shapeshifting problems\", Nat Comput Sci (2024) [doi.org/10.1038/s43588-024-00749-7](https://doi.org/10.1038/s43588-024-00749-7).\n\nA preprint of the paper is also available on the [arXiv preprint server](https://arxiv.org/abs/2208.07859).\n\n## Learn and use morpho\n\nDocumentation is available on [readthedocs](https://morpho-lang.readthedocs.io/en/latest/), an extensive [user manual](https://github.com/Morpho-lang/morpho-manual/blob/main/manual.pdf) and a [developer guide](https://github.com/Morpho-lang/morpho-devguide/blob/main/devguide.pdf). A [Slack community](https://join.slack.com/t/morphoco/shared_invite/zt-1o6azavwl-XMtjjFwxW~P6C8rc~YbBlA) is also available for people interested in using morpho and seeking support.\n\nWe now have a sequence of tutorial videos on our [Youtube channel](https://www.youtube.com/@Morpho-lang) to help you learn Morpho: \n\n* An [introduction to the Morpho language](https://youtu.be/eVPGWpNDeq4)\n* Introduction to [shape optimization with Morpho](https://youtu.be/odCkR0PDKa0)\n\n## Community and Contributions\n\nMorpho is under active development and we welcome contributions! Please see the [Contributor's guide](CONTRIBUTING.md) for more information about how you can get involved in the morpho project. For those interested in extending morpho or working with the source a [Developer guide](https://github.com/Morpho-lang/morpho-devguide) is also provided in a separate repository. \n\nWe provide a [Roadmap](https://github.com/Morpho-lang/morpho/wiki/Road-Map) for future development plans that might give you ideas for how you could contribute.\n\nWe also welcome bug reports and suggestions: Please feel free to use the *Issues* feature on our github repository to bring these to the developers' attention.\n\nParticipation in the morpho community, both as users and developers, is bound by our [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## Installation\n\nCode in this repository builds morpho as a shared library. Morpho also requires two subsidiary programs, a [terminal app](https://github.com/Morpho-lang/morpho-cli), and a [viewer application](https://github.com/Morpho-lang/morpho-morphoview).\n\nFor this release, morpho can be installed on all supported platforms using the homebrew package manager. Alternatively, the program can be installed from source as described below. We are continuously working on improving morpho installation, and hope to provide additional mechanisms for installation in upcoming releases.\n\nMorpho packages to extend the program's capability can be downloaded, installed and distributed via the associated [morphopm](https://github.com/Morpho-lang/morpho-morphopm) package manager.\n\n### Installation with homebrew\n\nThe simplest way to install morpho is through the [homebrew package manager](https://brew.sh). To do so:\n\n1. If not already installed, install homebrew on your machine as described on the [homebrew website](https://brew.sh)\n\n2. Open a terminal and type:\n\n```\nbrew update\nbrew tap morpho-lang/morpho\nbrew install morpho morpho-cli morpho-morphoview morpho-morphopm\n```\n\nIf you need to uninstall morpho, simply open a terminal and type `brew uninstall morpho-morphopm morpho-cli morpho-morphoview morpho`. It's very important to uninstall the homebrew morpho in this way before attempting to install from source as below.\n\n### Install from source\n\nThe second way to install morpho is by compiling the source code directly. Morpho now leverages the [Cmake](https://cmake.org) build system, which enables platform independent builds. Windows users must first install Windows Subsystem for Linux; some instructions to do so are found below.\n\n#### Gather dependencies\n\nYou can use any appropriate package manager to install morpho's dependencies via the terminal. \n\nUsing homebrew (preferred on macOS):\n\n```\nbrew update\nbrew install cmake glfw suite-sparse freetype povray libgrapheme\n```\n\nUsing apt (preferred on Ubuntu):\n\n```\nsudo apt update\nsudo apt upgrade\nsudo apt install build-essential cmake\nsudo apt install libglfw3-dev libsuitesparse-dev liblapacke-dev povray libfreetype6-dev libunistring-dev\n```\n\n#### Build the morpho shared library\n\nYou can build the shared library, hosted in this repository.\n\n1. Obtain the source by cloning the repository:\n\n```\ngit clone https://github.com/Morpho-lang/morpho.git\n```\n\n2. Navigate to the morpho folder and build the library:\n\n```\ncd morpho\nmkdir build\ncd build\ncmake -DCMAKE_BUILD_TYPE=Release ..\nsudo make install\n```\n\n3. Navigate back out of the morpho folder:\n\n```\ncd ../../\n```\n\n#### Build the morpho terminal app\n\nThe [terminal app](https://github.com/Morpho-lang/morpho-cli) provides an interactive interface to morpho, and can also run morpho files. To build it:\n\n1. Obtain the source by cloning the github public repository:\n\n```\ngit clone https://github.com/Morpho-lang/morpho-cli.git\n```\n\n2. Navigate to the morpho-cli folder and build the library:\n\n```\ncd morpho-cli\nmkdir build\ncd build\ncmake -DCMAKE_BUILD_TYPE=Release ..\nsudo make install\n```\n\n3. Check it works by typing:\n\n```\nmorpho6\n```\n\n4. Assuming that the morpho terminal app starts correctly, type `quit` to return to the shell and then\n\n```\ncd ../../\n```\n\nto navigate back out of the morph-cli folder.\n\n#### Build the morphoview viewer application\n\n[Morphoview](https://github.com/Morpho-lang/morpho-morphoview) is a simple viewer application to visualize morpho results.\n\n1. Obtain the source by cloning the github public repository:\n\n```\ngit clone https://github.com/Morpho-lang/morpho-morphoview.git\n```\n\n2. Navigate to the morpho-cli folder and build the library:\n\n```\ncd morpho-morphoview\nmkdir build\ncd build\ncmake -DCMAKE_BUILD_TYPE=Release ..\nsudo make install\n```\n\n3. Check it works by typing:\n\n```\nmorphoview\n```\n\nwhich should simply run and quit normally. You can then type\n\n```\ncd ../../\n```\n\nto navigate back out of the morpho-morphoview folder.\n\n### Windows via Windows Subsystem for Linux (WSL2)\n\nWindows support is provided through Windows Subsystem for Linux (WSL), which is an environment that enables windows to run linux applications. We highly recommend using WSL2, which is the most recent version and provides better support for GUI applications; some instructions for WSL1 are provided [in the manual](https://github.com/Morpho-lang/morpho-manual/blob/main/manual.pdf). Detailed information on running GUI applications in WSL2 is found on the [Microsoft WSL support page](https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps).\n\n1. Begin by installing the [Ubuntu App](https://ubuntu.com/desktop/wsl) from the Microsoft store.\n\n2. Once the Ubuntu terminal is working in Windows, you can install morpho either through homebrew or by building from source.\n"
  },
  {
    "path": "examples/catenoid/catenoid.morpho",
    "content": "// This script computes the shape of a soap film attached to two circular rings separated vertically.\n// The surface tension minimizes the area of this film, resulting in a shape called a \"catenoid\". \n// This belongs to the family of \"minimal surfaces\", which are surfaces that locally minimize their area.\n\n// Showcases: AreaMesh, Area, conjugategradient, fixing boundary in the optimizer.\nimport meshtools\nimport plot\nimport optimize\n\n// Set up geometrical parameters\n\nvar r = 1.0 // radius\nvar ratio = 0.4 // Separation to diameter ratio\nvar L = 2*r*ratio // Separation\n\n// Generate a tube / cylindrical mesh\nvar mesh = AreaMesh(fn (u, v) [r*cos(u), v, r*sin(u)], \n                    -Pi...Pi:Pi/10,\n                    -L/2..L/2:L/5, \n                    closed=[true,false]\n)\n\nmesh.addgrade(1)\n\n// Select the boundary\nvar bnd = Selection(mesh, boundary=true)\n\nvar g = plotselection(mesh, bnd, grade=1)\ng.title = \"Before\"\nShow(g)\n\n// Define the optimizataion problem\nvar problem = OptimizationProblem(mesh)\n// Add the area energy using the built-in Area functional\nvar area = Area()\nproblem.addenergy(area)\n\n// Define the optimizer\nvar opt = ShapeOptimizer(problem, mesh)\n// Ask the optimizer to fix the boundary rings\nopt.fix(bnd)\n\n// Minimize!\nopt.conjugategradient(1000)\n\ng = plotselection(mesh, bnd, grade=1)\ng.title = \"After\"\nShow(g)\n"
  },
  {
    "path": "examples/cholesteric/cholesteric.morpho",
    "content": "// Test cholesteric in a square boundary\nimport meshtools\nimport plot\nimport optimize\n\nvar L = 0.5\nvar dx = 0.1\n\nvar m = AreaMesh(fn (u, v) [u, v, 0], -L..L:dx, -L..L:dx)\nm.addgrade(1)\n\nvar substrate = Selection(m, fn (x,y,z) abs(y-(-0.5))<0.001 || abs(y-(0.5))<0.001)\nsubstrate.addgrade(1)\n//Show(plotselection(m, substrate, grade=1))\n\nvar nn = Field(m, Matrix([1,0,0]))\n\nvar problem = OptimizationProblem(m)\n\nvar lnem = Nematic(nn)\nproblem.addenergy(lnem)\n\n// Impose planar degenerate anchoring by penalizing ny\nvar lanch = LineIntegral(fn (x, n) n[1]^2, nn)\nproblem.addenergy(lanch, selection=substrate)\n\nvar ln=NormSq(nn)\nproblem.addlocalconstraint(ln, field=nn, target=1)\nlnem.pitch = Pi/2\n\nvar opt = FieldOptimizer(problem, nn)\nopt.conjugategradient(1000)\n\n// Function to visualize a director field\nfn visualize(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var nv = v.dimensions()[1]\n  var g = Graphics()\n  for (i in 0...nv) {\n    var x = v.column(i)\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=0.3))\n  }\n  return g\n}\n\nShow(visualize(m, nn, dx/4))\n"
  },
  {
    "path": "examples/cube/cube.morpho",
    "content": "/* Minimize the area of a surface at constant enclosed volume. */\n\nimport graphics\nimport meshtools\nimport plot\nimport optimize\n\nvar Nlevels = 4 // Levels of refinement\nvar Nsteps = 1000 // Maximum number of steps per refinement level\n\n// Create an initial cube\nvar m = PolyhedronMesh([ [-0.5, -0.5, -0.5],\n                         [ 0.5, -0.5, -0.5],\n                         [-0.5,  0.5, -0.5],\n                         [ 0.5,  0.5, -0.5],\n                         [-0.5, -0.5,  0.5],\n                         [ 0.5, -0.5,  0.5],\n                         [-0.5,  0.5,  0.5],\n                         [ 0.5,  0.5,  0.5]],\n                       [ [0,1,3,2], [4,5,7,6],\n                         [0,1,5,4], [3,2,6,7],\n                         [0,2,6,4], [1,3,7,5] ])\n\n// Set up the problen\nvar problem = OptimizationProblem(m)\nvar la = Area()\nproblem.addenergy(la)\n\nvar lv = VolumeEnclosed()\nproblem.addconstraint(lv)\n\n// Create the optimizer\nvar opt = ShapeOptimizer(problem, m)\n\n// Perform the optimization\nfor (i in 1..Nlevels) {\n  opt.conjugategradient(Nsteps)\n\n  if (i==Nlevels) break\n  // Refine\n  var mr=MeshRefiner([m])\n  var refmap = mr.refine()\n  for (el in [problem, opt]) el.update(refmap)\n  m = refmap[m]\n}\n\n// Compare with true area of a sphere for the same volume\nvar V0=lv.total(m)\nvar Af=la.total(m)\nvar R=(V0/(4/3*Pi))^(1/3)\nvar area = 4*Pi*R^2\nprint \"Final area: ${Af} True area: ${area} diff: ${abs(Af-area)}\"\n\nvar g = plotmesh(m)\ng.title=\"Cube\"\nShow(g)\n"
  },
  {
    "path": "examples/delaunay/delaunay.morpho",
    "content": "// Demonstrate use of the Delaunay module\n\nimport plot\nimport delaunay\n\nvar N = 100 // Number of points \n\n// Explicitly check that all triangles have the property that no other point is in their circumcircle\nfn checkTriangulation(pts, tri, quiet=false) {\n    var success = true\n    for (t, k in tri) {\n        var sph = Circumsphere(pts, t)\n\n        for (i in 0...pts.count()) {\n            if (t.ismember(i)) continue // Skip the vertices of the triangle\n\n            if (sph.pointinsphere(pts[i])) {\n                if (!quiet) print \"Point ${i} is in triangle ${k}.\"\n                success=false; \n            }\n        }\n    }\n    return success\n}\n\n// 2D random point distribution \nvar a = []\nfor (i in 1..N) a.append(Matrix([2*random()-1, 2*(2*random()-1)]))\n\nvar del = Delaunay(a)\nvar tri = del.triangulate() // Returns a list of triangles [ [i, j, k], ... ]\n\nif (checkTriangulation(a, tri)) {\n    print \"2D triangulation passed.\"\n}\n\n// 3D random point distribution \nvar b = []\nfor (i in 1..N) b.append(Matrix([2*random()-1, 2*(2*random()-1), 2*random()-1]))\n\ndel = Delaunay(b)\ntri = del.triangulate()\n\nif (checkTriangulation(b, tri)) {\n    print \"3D triangulation passed.\"\n}\n\n// Directly create meshes \nvar m2d = DelaunayMesh(a)\nm2d.addgrade(1) // Mesh as returned only contains grade 2 elements (triangles) so add the bars\nShow(plotmesh(m2d, grade=[0,1]))\n\nvar m3d = DelaunayMesh(b)\nm3d.addgrade(1) // Mesh as returned only contains grade 2 elements (triangles) so add the bars\nShow(plotmesh(m3d, grade=[0,1]))\n"
  },
  {
    "path": "examples/dla/dla.morpho",
    "content": "// Diffusion Limited Aggregation\n\nimport kdtree\nimport color\nimport graphics\nimport povray\n\nvar Np = 100 // Number of particles to add\nvar r = 0.05 // radius of a particle\nvar R = 3*r // Initial radius of sphere\nvar delta = r // Size of step to take\n\nvar pts = [ Matrix([0,0,0]) ]\nvar tree = KDTree(pts)\n\n// Generate a random point\nfn randompt() {\n  var x = Matrix([randomnormal(), randomnormal(), randomnormal()])\n  return R*x/x.norm()\n}\n\nfor (n in 1..Np) { // Add particles one-by-one\n  if (mod(n, 10)==0) print \"Added particle no. ${n}\"\n  var x = randompt()\n  while (true) {\n    // Move current particle\n    x+=Matrix([delta*randomnormal(), delta*randomnormal(), delta*randomnormal()])\n\n    // Check if it collided with another particle\n    if ((tree.nearest(x).location-x).norm()<2*r) {\n      tree.insert(x)\n      pts.append(x)\n      if (x.norm()>R/2) R = 2*x.norm()\n      break // Move to next particle\n    }\n\n    // Catch if it wandered out of the boundary\n    if (x.norm()>2*R) x = randompt()\n  }\n}\n\n// Now visualize the result\nvar col = Gray(0.5)\nvar g = Graphics()\ng.background = White\nfor (x in pts) g.display(Sphere(x, r, color=col))\nShow(g)\n\n// And raytrace it too\nvar pov = POVRaytracer(g)\npov.viewangle = 32\npov.light = [Matrix([3,3,10]), Matrix([-3,3,10]), Matrix([0,-3,10])]\npov.render(\"dla.pov\")\n"
  },
  {
    "path": "examples/electrostatics/electrostatics.morpho",
    "content": "// Solve Laplace's equation on a square domain by minimizing |grad V|^2\nimport meshtools\nimport plot\nimport optimize\n\nvar delta=0.25 // Mesh spacing\nvar L = 1 // Size of domain\n\n// Create the mesh\nvar mesh = AreaMesh(fn (u,v) [ u, v, 0 ], -L/2..L/2:delta, -L/2..L/2:delta)\nmesh.addgrade(1)\n\n// Create boundaries and select edges, building up from selecting vertices\nvar bnd = Selection(mesh, boundary=true)\nvar bnd1 = Selection(mesh, fn (x,y,z) abs(x+L/2)<0.01 || abs(y+L/2)<0.01)\nvar bnd2 = Selection(mesh, fn (x,y,z) abs(x-L/2)<0.01 || abs(y-L/2)<0.01)\nfor (b in [bnd, bnd1, bnd2]) b.addgrade(1)\n\nbnd1=bnd.intersection(bnd1)\nbnd2=bnd.intersection(bnd2)\n\n// Create field\nvar phi = Field(mesh, fn (x,y,z) 0)\n\n// Set up the problem\nvar problem = OptimizationProblem(mesh)\n\nvar le = GradSq(phi)\nproblem.addenergy(le)\n\nvar v1 = 0, v2 = 1\nvar lt1 = LineIntegral(fn (x, v) (v-v1)^2, phi)\nproblem.addenergy(lt1, selection=bnd1, prefactor=100)\n\nvar lt2 = LineIntegral(fn (x, v) (v-v2)^2, phi)\nproblem.addenergy(lt2, selection=bnd2, prefactor=100)\n\n// Create the optimizer and perform optimization\nvar opt = FieldOptimizer(problem, phi)\nopt.conjugategradient(100)\n\nfor (i in 1..10) {\n  // Select elements that have an above average contribution to the energy\n  var en = le.integrand(phi) // energy in each element\n  var mean = en.sum()/en.count() // mean energy per element\n  var srefine = Selection(mesh)\n  for (id in 0...en.count()) if (en[0,id]>1.5*mean) srefine[2,id]=true // identify large contributions\n\n  // Refine\n  var ref = MeshRefiner([mesh, phi, bnd, bnd1, bnd2])\n  var refmap = ref.refine(selection=srefine) // perform the refinement\n  for (el in [problem, opt]) el.update(refmap) // update the problem\n  mesh = refmap[mesh] // update all our variables\n  phi = refmap[phi]\n  bnd = refmap[bnd]\n  bnd1 = refmap[bnd1]\n  bnd2 = refmap[bnd2]\n  equiangulate(mesh)\n\n  // Reminimize\n  opt.conjugategradient(1000)\n}\n\nprint \"Final mesh has ${mesh.count(2)} elements\"\n\nShow(plotmesh(mesh, grade=1))\n\n// Display the result\nShow(plotfield(phi, style=\"interpolate\"))\n"
  },
  {
    "path": "examples/elementtypes/README.md",
    "content": "# Finite Element types\n\nThis folder contains some examples using finite elements beyond linear Lagrange elements (CG1). These require the new optimization package, optimize4. To install, type: \n\n    morphopm install optimize4 \n\nin a terminal.\n\n## Examples\n\n* electrostaticsCG2.morpho - Solve Laplace's equation on a square with dirichlet boundary conditions and using CG2 elements. Follows the original example in  ../electrostatics\n\n* qtensorCG2.morpho - Solve the liquid crystal q tensor problem on a disk. Follows the original example in ../qtensor. \n"
  },
  {
    "path": "examples/elementtypes/electrostaticsCG2.morpho",
    "content": "// Solve Laplace's equation on a square domain by minimizing |grad V|^2\n// Uses CG2 elements\n\nimport meshtools\nimport plot\nimport optimize4\n\nvar delta=0.25/8 // Mesh spacing\nvar L = 1 // Size of domain\n\n// Create the mesh\nvar mesh = AreaMesh(fn (u,v) [ u, v, 0 ], -L/2..L/2:delta, -L/2..L/2:delta)\nmesh.addgrade(1)\n\n// Create boundaries and select edges, building up from selecting vertices\nvar bnd = Selection(mesh, boundary=true)\nvar bnd1 = Selection(mesh, fn (x,y,z) abs(x+L/2)<0.01 || abs(y+L/2)<0.01)\nvar bnd2 = Selection(mesh, fn (x,y,z) abs(x-L/2)<0.01 || abs(y-L/2)<0.01)\nfor (b in [bnd, bnd1, bnd2]) b.addgrade(1)\n\nbnd1=bnd.intersection(bnd1)\nbnd2=bnd.intersection(bnd2)\n\n// Create field\nvar phi = Field(mesh, fn (x,y,z) 0, finiteelementspace=FiniteElementSpace(\"CG2\", grade=2))\n\n// Set up the problem\nvar problem = OptimizationProblem(mesh)\n\nfn integrand(x, q) {\n  var dg = grad(q).norm()\n  return dg*dg\n}\n\nvar v1 = 0, v2 = 1\nvar lt1 = LineIntegral(fn (x, v) (v-v1)^2, phi, method={})\nproblem.addenergy(lt1, selection=bnd1, prefactor=100)\n\nvar lt2 = LineIntegral(fn (x, v) (v-v2)^2, phi, method={})\nproblem.addenergy(lt2, selection=bnd2, prefactor=100)\n\nvar le = AreaIntegral(integrand, phi, method={})\nproblem.addenergy(le)\n\nvar adapt = ProblemAdapter(problem, phi)\n\nvar control = LBFGSController(adapt)\ncontrol.optimize(1000)\n\n// Display the result\nShow(plotmesh(mesh, grade=1) + plotfield(phi, style=\"interpolate\"))\n"
  },
  {
    "path": "examples/elementtypes/qtensorCG2.morpho",
    "content": "// Nematic liquid crystal with Q tensor on a disk\n// Solved using CG2 elements\n\nimport meshgen\nimport plot\nimport povray\nimport optimize4\n\nclass QTensor {\n  init(K=0.01) {\n    self.mesh = nil \n    self.problem = nil \n\n    self.rho = 1.3 // Density. rho>1 results in the nematic phase\n    self.EA = 3 // Anchoring strength\n    self.K = K // Bending modulus. K=0.01 yields two +1/2 defects, whereas K=1.0 yields a single +1 defect.\n\n    self.a2 = (1-self.rho) // Coefficient of Tr(Q^2) in the Free energy \n    self.a4 = (1+self.rho)/self.rho^2 // Coefficient of (Tr(Q^2))^2 in the Free energy\n  }\n\n  initialMesh() { \n    var dom = CircularDomain([0,0], 1)\n    var mg = MeshGen(dom, [-1..1:0.4, -1..1:0.4], quiet=true)\n    var m = mg.build()\n    m.addgrade(1)\n    self.mesh = ChangeMeshDimension(m, 3)\n    return self.mesh\n  }\n\n  initialField() { \n    self.q_tensor = Field(self.mesh, \n                          fn(x,y,z) Matrix([0.01*random(1), 0.01*random(1)]),\n                          finiteelementspace=FiniteElementSpace(\"CG2\", grade=2)\n                          )\n  }\n\n  initialSelection() {\n    self.bnd = Selection(self.mesh, boundary=true)\n    self.bnd.addgrade(0) // add point elements\n  }\n\n  buildProblem() { \n    // Specify the problem\n    self.problem = OptimizationProblem(self.mesh)\n\n    // Define bulk free energy functional\n    fn landau(x, q) {\n      var qt = q.norm()\n      var qt2=qt*qt\n      return self.a2*qt2 + self.a4*qt2*qt2\n    }\n    var bulk = AreaIntegral(landau, self.q_tensor, method={})\n    self.problem.addenergy(bulk)\n\n    // Define anchoring energy functional at the boundary\n    fn anchoring(x, q) {\n      var t = tangent()\n      var wxx = t[0]*t[0]-0.5\n      var wxy = t[0]*t[1]\n      return (q[0]-wxx)^2+(q[1]-wxy)^2\n    }\n    var anchor = LineIntegral(anchoring, self.q_tensor, method={})\n    self.problem.addenergy(anchor, selection=self.bnd, prefactor = self.EA)\n\n    // The elastic energy is the grad-squared of the q-tensor\n\n    fn elasticity(x, q) {\n      var g = grad(q)\n      return g[0].inner(g[0]) + g[1].inner(g[1]) \n    }\n\n    var elastic = AreaIntegral(elasticity, self.q_tensor, method={})\n    self.problem.addenergy(elastic, prefactor = self.K)\n\n    return self.problem\n  }\n\n  buildAdapter() { \n    self.adapter = ProblemAdapter(self.problem, self.q_tensor)\n    return self.adapter \n  }\n\n  build() { // Setup the problem and return an Adapter\n    self.initialMesh()\n    self.initialField()\n    self.initialSelection()\n    self.buildProblem()\n    return self.buildAdapter()\n  }\n\n  correctMesh() {\n    for (id in self.bnd.idlistforgrade(0)) {\n      var x = self.mesh.vertexposition(id)\n      self.mesh.setvertexposition(id, x/x.norm())\n    }\n  }\n\n  correctField() {\n    if (self.maxgrade(self.q_tensor)<1) return \n    var conn = self.mesh.connectivitymatrix(0, 1)\n    for (id in 0...self.mesh.count(1)) {\n      var v=conn.rowindices(id)\n      self.q_tensor[1,id]=(self.q_tensor[0,v[0]] + self.q_tensor[0,v[1]])/2\n    } \n  }\n\n  refine(adaptive=true, lambda=1.7) {\n    var srefine\n\n    if (adaptive) {\n      // Select elements that have a large contribution to the elastic energy\n      var en = GradSq(self.q_tensor).integrand(self.q_tensor)\n      var mean = en.sum()/en.count()\n      srefine = Selection(self.mesh) // Start with an empty selection\n      for (id in 0...en.count()) if (en[0,id]>lambda*mean) srefine[2,id]=true // Identify high (compared to the mean) energy elements\n      print \"Refining ${srefine.count(2)} elements\"\n    }\n\n    // Create a mesh refiner\n    var mr=MeshRefiner([self.mesh, self.q_tensor, self.bnd])\n    var refmap = mr.refine(selection=srefine)\n    \n    // Use the new mesh and field\n    self.mesh = refmap[self.mesh]\n    self.q_tensor = refmap[self.q_tensor]\n    self.bnd = refmap[self.bnd]\n\n    self.correctMesh()\n    self.correctField()\n\n    equiangulate(self.mesh)\n\n    // Remake the problem and the adapter\n    self.buildProblem()\n    return self.buildAdapter()\n  }\n\n  maxgrade(f) { // Finds the maximum grade in a Field\n    var shape = f.shape()\n    var maxgrade = 0\n    for (s, k in shape) if (s>0) maxgrade=k \n    return maxgrade\n  }\n\n  visualize() { \n    var mg = self.maxgrade(self.q_tensor)\n\n    // Function to get the director (unit vector n) from the Q-tensor\n    fn qtodirector(q) {\n      var S = 2*q.norm()\n      var Q = q/S\n      var nx = sqrt(Q[0]+0.5)\n      var ny = abs(Q[1]/nx)\n      nx = nx*sign(Q[1])\n      return Matrix([nx,ny,0])\n    }\n\n    // Function to get the scalar order parameter S from the Q-tensor\n    fn qtoorder(q) {\n      var S = 2*q.norm()\n      return S\n    }\n\n    /* Function to visualize a director field\n      This function returns a `Graphics()` object that has the director field visualized in the form of cylinders. Here,\n      1. m is the mesh\n      2. nn is the director field and\n      3. dl is the half-length of the cylinders to be drawn. \n      4. aspectratio (optional argument) is the aspect ratio of the cylinders. (default is 0.3 here) */\n    fn visualize(m, nn, dl, aspectratio=0.3) {\n      var v = m.vertexmatrix()\n      var nv = v.dimensions()[1]\n      var g = Graphics()\n      for (i in 0...nv) {\n        var x = v.column(i)\n        g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=aspectratio))\n      }\n      if (mg>0) {\n        var conn = m.connectivitymatrix(0,1)\n        for (id in 0...m.count(1)) {\n          var vids = conn.rowindices(id)\n          var x = (v.column(vids[0])+v.column(vids[1]))/2\n          g.display(Cylinder(x-nn[1,id]*(2*dl/3), x+nn[1,id]*(2*dl/3), aspectratio=aspectratio/2))\n        }\n      }\n      return g\n    }\n\n    var nn = Field(self.mesh, Matrix([1,0,0]), grade=self.q_tensor.shape()) // Initialize to some vector\n    for (g in 0..mg) for (i in 0...self.mesh.count(g)) {\n      nn[g,i]=qtodirector(self.q_tensor[g,i])\n    }\n    \n    var S = Field(self.mesh, 0) // Initialize to a scalar\n    for (i in 0...self.mesh.count()) S[i]=qtoorder(self.q_tensor[i])\n\n    var splot = plotfield(S, style=\"interpolate\", colormap = ViridisMap(), scalebar=ScaleBar(posn=[1.2,0,0]))\n    var gnn=visualize(self.mesh, nn, 0.05)\n\n    Show(splot+gnn)\n  }\n}\n\nvar example = QTensor()\nvar adapt = example.build()\n\nfor (i in 1..4) {\n    if (i>1) adapt = example.refine(adaptive=false)\n    var control = LBFGSController(adapt)\n    control.optimize(500) \n    example.visualize()\n}\n"
  },
  {
    "path": "examples/examples.py",
    "content": "#!/usr/bin/env python3\n# runexamples.py\n# runs all the example for morpho\n# reporting any errors found\n\nimport os, glob, sys\nfrom queue import Empty\nimport regex as rx\nfrom functools import reduce\nimport operator\nimport colored\nfrom colored import stylize\n\n\nsys.path.append('../test')\next = \"morpho\"\ncommand = 'morpho6'\nstk = '@stacktrace'\nerr = '@error'\n\ndef finderror(str):\n    #return rx.findall(r'\\/\\/ expect ?(.*) error', str)\n    #return rx.findall(r'.*[E|e]rror[ :].*?(.*)', str)\n    return rx.findall(r'@error.*', str)\n\n\ndef simplify_errors(str):\n    # this monster regex extraxts NAME from error messages of the form error ... 'NAME'\n    return rx.sub('.*[E|e]rror[ :]*\\'([A-z;a-z]*)\\'.*', err+'[\\\\1]', str.rstrip())\n\n# Simplify stacktrace\ndef simplify_stacktrace(str):\n    return rx.sub(r'.*at line.*', stk, str.rstrip())\n\ndef iserror(str):\n    #return rx.findall(r'\\/\\/ expect ?(.*) error', str)\n    test=rx.findall(r'@error.*', str)\n    return len(test)>0\ndef remove_control_characters(str):\n    return rx.sub(r'\\x1b[^m]*m', '', str.rstrip())\n\ndef isin(str):\n    #return rx.findall(r'\\/\\/ expect ?(.*) error', str)\n    test=rx.findall(r'.*in .*', str)\n    return len(test)>0\n\n\n\ndef getoutput(filepath):\n    # Load the file\n    file_object = open(filepath, 'r')\n    lines = file_object.readlines()\n    file_object.close()\n    # remove all control characters\n    lines = list(map(remove_control_characters, lines))\n    # Convert errors to our universal error code\n    lines = list(map(simplify_errors, lines))\n    # Identify stack trace lines\n    lines = list(map(simplify_stacktrace, lines))\n    for i in range(len(lines)-1):\n        if (iserror(lines[i])):\n            if (isin(lines[i+1])):\n                lines[i+1]=stk\n    # and remove them\n    return list(filter(lambda x: x!=stk, lines))\n\ndef run(file,testLog,CI):\n    ret = 1\n    print(file+\":\", end=\" \")\n\n    # Create a temporary file in the same directory\n    tmp = file + '.out'\n\n    # Run the test\n    os.system(command + ' ' +file + ' > ' + tmp)\n\n    # If we produced output\n    if os.path.exists(tmp):\n        # Get the output\n        out = getoutput(tmp)\n\n\t\t#look for erros\n        for line in out:\n            err = finderror(line)\n            # Was it expected?\n\n            if (iserror(line)):\n                if not CI:\n                    print(\"Failed\") #stylize(\"Failed\",colored.fg(\"red\"))) // Temporarily disable this 6/19/23 due to colored module API change\n                else:\n                    print(\"::error file = {\",file,\"}::{\",file,\" Failed}\")\n\n                #also print to the test log\n                print(file+\":\", end=\" \", file = testLog)\n                print(\"Failed\", end=\" \", file = testLog)\n                print(\"with error \"+ err[0], file = testLog)\n                print(\"\\n\",file = testLog)\n                ret = 0\n                break\n        \n        if (ret ==1):\n            if not CI:\n                print(file+\":\", end=\" \")\n                print(\"Passed\") #stylize(\"Passed\",colored.fg(\"green\")))\n        # Delete the temporary file\n        os.system('rm ' + tmp)\n    return ret\n\n\n\nprint('--Building Examples---------------------')\n\n\n# open a test log\n# write failures to log\nsuccess=0 # number of successful examples\ntotal=0   # total number of examples\n\n# look for a command line arguement that says\n# this is being run for continous integration\n\nCI = False\nfor arg in sys.argv:\n    if arg == '-c': # if the argument is -c, then we are running in CI mode\n        CI = True\n\nfiles=glob.glob('**/**.'+ext, recursive=True)\nwith open(\"FailedExamples.txt\",'w') as testLog:\n\n    for f in files:\n        success+=run(f,testLog,CI)\n        total+=1\n\n\nprint('--End testing-----------------------')\nprint(success, 'out of', total, 'tests passed.')\nif CI and success<total:\n    exit(-1)\n"
  },
  {
    "path": "examples/implicitmesh/ellipsoid.morpho",
    "content": "\nimport implicitmesh\nimport plot\n\n// Ellipsoid\nvar impl = ImplicitMeshBuilder(fn (x,y,z) x^2/3+y^2+z^2-1)\nvar mesh = impl.build(stepsize=0.25)\n\nmesh.addgrade(1)\nShow(plotmesh(mesh, grade=[1,2]))\n"
  },
  {
    "path": "examples/implicitmesh/threesurface.morpho",
    "content": "\nimport implicitmesh\nimport plot\n\n// 3-surface\nvar rx=6\nvar ry=3.5\nvar rz=4\nvar r1=1.2\nvar x1=3.9\n\nvar impl = ImplicitMeshBuilder(fn (x,y,z) rz^4*z^2 - (1-(x/rx)^2-(y/ry)^2)*((x-x1)^2 + y^2 - r1^2)*(x^2+y^2-r1^2)*((x+x1)^2+y^2-r1^2))\nvar mesh = impl.build(start=Matrix([0,3,0]), stepsize=0.3, maxiterations=4000)\n\nmesh.addgrade(1)\nvar g=plotmesh(mesh, grade=[1,2])\nShow(g)\n\nimport color\nimport povray\n\ng.background = White\n\nvar pov = POVRaytracer(g)\npov.light = [Matrix([-3,3,3]), Matrix([3,3,3]), Matrix([0,-3,3])]\npov.viewpoint = Matrix([0,-4,20])\npov.viewangle = 45\npov.render(\"threesurface.pov\")\n"
  },
  {
    "path": "examples/implicitmesh/torus.morpho",
    "content": "\nimport implicitmesh\nimport plot\n\n// Torus\nvar r=1\nvar a=0.35\n\nvar impl = ImplicitMeshBuilder(fn (x,y,z) (x^2+y^2+z^2+r^2-a^2)^2 - 4*r^2*(x^2+y^2))\nvar mesh = impl.build(start=Matrix([1,0,0.5]), stepsize=0.25, maxiterations=400)\n\nmesh.addgrade(1)\nShow(plotmesh(mesh, grade=[1,2]))\n"
  },
  {
    "path": "examples/meshgen/disk.morpho",
    "content": "// Domain composed of a single disk\nimport meshgen\nimport plot\n\nvar dom = fn (x) -(x[0]^2+x[1]^2-1)\nvar mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshgen/ellipse.morpho",
    "content": "// Ellipse domain\nimport meshgen\nimport plot\n\nvar e0 = Domain(fn (x) -((x[0]/2)^2+x[1]^2-1))\nvar mg = MeshGen(e0, [-2..2:0.2, -1..1:0.2])\nvar m = mg.build()\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshgen/ellipsoidsection.morpho",
    "content": "// 3D Ellipsoidal shell intersecting with a plane\nimport meshgen\nimport plot\n\nvar dh = 0.2\nvar e0 = Domain(fn (x) -((x[0]/2)^2+x[1]^2+x[2]^2-1))\nvar e1 = Domain(fn (x) -((x[0]/2)^2+x[1]^2+x[2]^2-(0.5)^2))\nvar e2 = HalfSpaceDomain(Matrix([0.25,0,0]), Matrix([1,0,0]))\nvar dom = (e0.difference(e1)).intersection(e2)\n\nvar mg = MeshGen(dom, [-2..2:dh, -1.2..1.2:dh, -1.2..1.2:dh], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))"
  },
  {
    "path": "examples/meshgen/halfdisk.morpho",
    "content": "// Domain composed of disk with half space removed\nimport meshgen\nimport plot\n\nvar c = CircularDomain([0,0], 1)\nvar hs = HalfSpaceDomain(Matrix([0,0]), Matrix([-1,0]))\nvar dom = c.difference(hs) \nvar mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshgen/overlappingdisks.morpho",
    "content": "// Domain composed of overlapping disks\nimport meshgen\nimport plot\n\n// Create a complicated domain by composing circular disk domains\nvar a = CircularDomain(Matrix([-0.5,0]), 1)\nvar b = CircularDomain(Matrix([0.5,0]), 1)\nvar c = CircularDomain(Matrix([0,0]), 0.3)\nvar dom = a.union(b).difference(c)\n\nvar mg = MeshGen(dom, [-2..2:0.1, -1..1:0.1], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshgen/sphere.morpho",
    "content": "// Sphere\nimport meshgen\nimport plot\n\nvar dh = 0.2\nvar dom = Domain(fn (x) -(x[0]^2+x[1]^2+x[2]^2-1))\nvar mg = MeshGen(dom, [-1..1:dh, -1..1:dh, -1..1:dh], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))"
  },
  {
    "path": "examples/meshgen/square.morpho",
    "content": "// Square domain with a hole \nimport meshgen\nimport plot\n\nvar e0 = Domain(fn (x) ((x[0])^2+x[1]^2-1))\nvar mg = MeshGen(e0, [-2..2:0.2, -2..2:0.2], quiet=false)\n//mg.maxiterations=10\n//mg.ttol=0.5\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshgen/superellipse.morpho",
    "content": "// Superellipse domain\nimport meshgen\nimport plot\n\n// Superellipse\nvar e1 = Domain(fn (x) -((x[0]^4+x[1]^4)^(1/4)-1))\nvar e2 = Domain(fn (x) -((x[0]^4+x[1]^4)^(1/4)-0.5))\nvar dom = e1.difference(e2)\n\nvar mg = MeshGen(dom, [-1..1:0.1, -1..1:0.1], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshgen/superellipsoid.morpho",
    "content": "// 3D Superellipsoid\nimport meshgen\nimport plot\n\nvar dh = 0.2\n\nvar dom = Domain(fn (x) -((x[0]^4+x[1]^4+x[2]^4)^(1/4)-1))\nvar mg = MeshGen(dom, [-1..1:dh, -1..1:dh, -1..1:dh], quiet=false)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))"
  },
  {
    "path": "examples/meshgen/weighted.morpho",
    "content": "// Circular domain with weight function\nimport meshgen\nimport plot\n\n// Weighted element size\nvar hbar = fn (x) 1+x[0]/2 // larger elements as a function of x\nvar mg = MeshGen(CircularDomain([0,0], 1), [-1..1:0.1, -1..1:0.1], quiet=false, weight=hbar)\nvar m = mg.build()\n\nShow(plotmesh(m, grade=1))\n"
  },
  {
    "path": "examples/meshslice/sphere.mesh",
    "content": "vertices\n\n1 -0.954722 -0.294874 0.0394472\n2 -0.945697 0.223319 -0.23619\n3 -0.925278 0.166153 0.34096\n4 -0.833108 -0.281133 -0.476335\n5 -0.754252 0.649972 0.0929536\n6 -0.726626 -0.463456 0.507172\n7 -0.655019 0.150017 -0.74057\n8 -0.653576 -0.753604 0.0701384\n9 -0.62521 -0.0106071 0.780384\n10 -0.619905 0.631677 -0.465513\n11 -0.578222 -0.707421 -0.406466\n12 -0.575122 0.516234 0.634616\n13 -0.518378 0.0375244 -0.127763\n14 -0.515911 -0.135654 -0.241352\n15 -0.429096 -0.380847 -0.819044\n16 -0.412838 -0.28289 0.292128\n17 -0.381693 0.379087 -0.274841\n18 -0.354031 0.279163 0.341505\n19 -0.331776 0.864488 0.377605\n20 -0.305146 -0.437068 0.846084\n21 -0.303346 0.947264 -0.103305\n22 -0.277188 -0.837024 0.47176\n23 -0.236104 -0.437788 -0.22947\n24 -0.202726 0.453119 -0.868093\n25 -0.195886 -0.977308 -0.0806102\n26 -0.192398 0.0179897 -0.981152\n27 -0.188778 0.254176 0.948555\n28 -0.137553 0.829043 -0.542002\n29 -0.136529 -0.0357935 -0.566425\n30 -0.0716524 -0.781658 -0.619578\n31 -0.0496494 0.547329 -0.0389194\n32 -0.0320227 0.00770868 0.00530305\n33 -0.0182861 0.67614 0.736546\n34 0.0271511 -0.0994216 0.555206\n35 0.100401 -0.522318 0.171556\n36 0.114494 -0.368786 -0.922436\n37 0.114549 -0.164553 0.979694\n38 0.151989 0.25377 -0.452691\n39 0.160284 0.950495 0.266212\n40 0.206156 -0.641845 0.738603\n41 0.2128 0.36825 0.354235\n42 0.246305 -0.934571 0.25673\n43 0.273714 0.15644 -0.949003\n44 0.28029 0.935922 -0.213277\n45 0.295323 -0.922026 -0.250302\n46 0.328752 -0.278382 -0.301566\n47 0.328999 0.646195 -0.688616\n48 0.386232 0.2875 0.876452\n49 0.45546 0.276089 -0.122428\n50 0.46643 -0.640892 -0.609673\n51 0.4675 -0.115378 0.228313\n52 0.499665 0.691483 0.521714\n53 0.600301 -0.217826 -0.769539\n54 0.622677 -0.217782 0.751561\n55 0.64553 -0.641848 0.41391\n56 0.678933 0.687521 -0.257614\n57 0.68551 0.714488 0.139935\n58 0.742604 0.279302 -0.608711\n59 0.748985 -0.651382 -0.121334\n60 0.829148 0.261101 0.494308\n61 0.917857 -0.182455 -0.352489\n62 0.952403 -0.220422 0.210575\n63 0.960171 0.272899 -0.0599808\n\nvolumes\n\n1 8 11 1 14\n2 34 40 35 51\n3 41 57 52 60\n4 49 58 56 63\n5 17 29 38 32\n6 35 20 16 22\n7 38 47 43 24\n8 40 42 35 55\n9 35 46 45 23\n10 41 51 49 60\n11 1 4 2 14\n12 46 59 50 61\n13 18 19 41 31\n14 43 47 38 58\n15 48 51 41 60\n16 38 28 17 31\n17 36 29 15 30\n18 48 52 41 33\n19 5 17 10 21\n20 50 53 46 61\n21 17 24 10 28\n22 49 60 51 63\n23 41 19 39 31\n24 39 52 41 57\n25 40 20 35 22\n26 14 16 8 23\n27 38 53 43 58\n28 46 50 36 53\n29 6 16 9 20\n30 13 14 7 17\n31 38 49 44 31\n32 47 49 38 58\n33 47 56 49 58\n34 17 18 13 32\n35 35 23 45 25\n36 41 48 34 51\n37 7 13 2 14\n38 23 25 11 30\n39 45 50 46 59\n40 6 8 1 16\n41 3 6 1 16\n42 42 45 35 59\n43 34 18 41 32\n44 35 40 34 20\n45 12 27 18 33\n46 1 13 3 16\n47 8 16 35 23\n48 5 13 2 17\n49 41 52 48 60\n50 51 54 40 55\n51 2 13 7 17\n52 35 55 42 59\n53 35 51 46 32\n54 38 47 44 49\n55 45 46 35 59\n56 48 54 51 60\n57 3 16 13 18\n58 44 49 47 56\n59 54 55 51 62\n60 49 56 44 57\n61 35 16 8 22\n62 55 59 51 62\n63 53 58 46 61\n64 43 26 36 29\n65 4 7 2 14\n66 13 29 17 32\n67 38 46 43 53\n68 56 57 49 63\n69 17 31 18 32\n70 7 14 4 15\n71 59 61 51 62\n72 34 48 37 54\n73 34 51 48 54\n74 37 40 34 54\n75 40 51 34 54\n76 35 51 40 55\n77 10 17 7 24\n78 49 51 46 61\n79 2 13 1 14\n80 51 60 54 62\n81 51 62 61 63\n82 38 49 46 58\n83 46 53 38 58\n84 51 59 46 61\n85 46 58 49 61\n86 46 51 35 59\n87 51 55 35 59\n88 51 61 49 63\n89 49 61 58 63\n90 57 60 49 63\n91 49 57 41 60\n92 60 62 51 63\n93 47 24 38 28\n94 34 16 9 18\n95 35 22 8 25\n96 36 26 15 29\n97 41 31 49 32\n98 15 26 7 29\n99 2 10 5 17\n100 41 27 48 33\n101 8 16 6 22\n102 49 51 41 32\n103 8 14 1 16\n104 45 23 46 30\n105 44 47 38 28\n106 9 16 34 20\n107 8 23 35 25\n108 14 23 15 29\n109 46 23 35 32\n110 2 3 1 13\n111 36 50 46 30\n112 45 25 23 30\n113 14 17 13 29\n114 38 24 17 28\n115 34 40 37 20\n116 11 15 14 23\n117 35 45 42 25\n118 1 11 4 14\n119 35 42 40 22\n120 38 31 17 32\n121 2 5 3 13\n122 10 21 17 28\n123 46 51 49 32\n124 13 17 5 18\n125 41 52 39 33\n126 4 14 11 15\n127 46 49 38 32\n128 7 10 2 17\n129 1 14 13 16\n130 37 48 34 27\n131 11 23 8 25\n132 42 22 35 25\n133 17 28 21 31\n134 11 14 8 23\n135 38 29 46 32\n136 39 57 41 31\n137 3 9 6 16\n138 46 29 36 30\n139 12 18 5 19\n140 34 51 35 32\n141 9 20 34 27\n142 16 20 6 22\n143 15 23 11 30\n144 5 12 3 18\n145 3 13 5 18\n146 3 12 9 18\n147 9 16 3 18\n148 17 21 5 31\n149 18 31 41 32\n150 49 31 38 32\n151 46 29 23 32\n152 7 17 14 29\n153 44 57 39 31\n154 5 18 17 31\n155 34 16 35 20\n156 41 51 34 32\n157 39 19 41 33\n158 41 19 18 33\n159 18 19 12 33\n160 19 21 39 31\n161 21 28 44 31\n162 39 21 44 31\n163 44 28 38 31\n164 15 29 23 30\n165 34 20 37 27\n166 46 50 45 30\n167 14 15 7 29\n168 23 29 46 30\n169 41 57 49 31\n170 9 18 12 27\n171 34 18 9 27\n172 34 48 41 27\n173 41 18 34 27\n174 17 24 38 29\n175 43 46 38 29\n176 38 24 43 29\n177 24 26 43 29\n178 7 24 17 29\n179 7 26 24 29\n180 43 53 46 29\n181 46 53 36 29\n182 36 53 43 29\n183 49 57 44 31\n184 18 27 41 33\n185 5 19 18 31\n186 5 21 19 31\n187 35 23 16 32\n188 23 29 14 32\n189 14 29 13 32\n190 35 16 34 32\n191 13 18 16 32\n192 16 18 34 32\n193 13 16 14 32\n194 16 23 14 32"
  },
  {
    "path": "examples/meshslice/testmeshslice.morpho",
    "content": "// Example of using the meshslice module \n\nimport meshtools\nimport plot\nimport meshslice\n\n// We'll create a spherical example mesh \nvar m = Mesh(\"sphere.mesh\")\nm.addgrade(1) \nm.addgrade(2)\n\n// And a couple of example fields \nvar phi = Field(m, fn (x,y,z) x+y+z)\nvar nn = Field(m, fn (x,y,z) Matrix([x,y,z])/sqrt(x^2+y^2+z^2))\n\n// First create the slicer \nvar slice = MeshSlicer(m)\n\n// We'll perform a slice \nvar slc = slice.slice([0,0,0], [1,0,0])\n\n// and then slice the fields \nvar sphi = slice.slicefield(phi) \nvar snn = slice.slicefield(nn)\n\n// We'll slice along a couple of other planes too\nvar slc2 = slice.slice([0,0,0], [0,1,0])\nvar sphi2 = slice.slicefield(phi) \nvar slc3 = slice.slice([0,0,0], [0,0,1])\nvar sphi3 = slice.slicefield(phi) \n\n// Visualization 1: Show the three slices \nShow(plotmesh(slc, grade=[0,1,2])+plotmesh(slc2, grade=[0,1,2])+plotmesh(slc3, grade=[0,1,2]))\n\n// Visualization 2: show the original mesh, plus three slices \nShow(plotmesh(m, grade=[1])+\n     plotfield(sphi, style=\"interpolate\")+\n     plotfield(sphi2, style=\"interpolate\")+\n     plotfield(sphi3, style=\"interpolate\"))\n\n// Helper function to visualize a unit vector field as cylinders \nfn visualizedirector(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var g = Graphics()\n  for (i in 0...m.count()) {\n    var x = v.column(i)\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=0.3))\n  }\n  return g\n}\n\n// Visualization 3: Show a sliced mesh, plus the vector field nn \nShow(plotmesh(slc, grade=[1,2])+\n     visualizedirector(slc, snn, 0.1))\n     "
  },
  {
    "path": "examples/plot/plotfield.morpho",
    "content": "// Plot a selection \nimport plot\nimport implicitmesh\n\n/* Create a simple mesh for demonstration purposes */\nvar impl = ImplicitMeshBuilder(fn (x,y,z) x^2+(y/2)^2+z^2-1)\nvar mesh = impl.build(stepsize=0.2)\n\n// Compute the Mean Curvature Squared and convert to a Field \nvar lmsq = MeanCurvatureSq()\nvar msq = lmsq.integrand(mesh)\nvar f = Field(mesh)\nfor (id in 0...mesh.count()) f[id]=msq[0,id]\n\n// Visualize the field \nShow(plotfield(f, grade=2, style=\"interpolate\", scalebar=ScaleBar(posn=[1.2,0,0])))\n// Visualize with custom limits on the colormap\nShow(plotfield(f, grade=2, style=\"interpolate\", cmin=0.025, cmax=0.075, scalebar=ScaleBar(posn=[1.2,0,0])))\n"
  },
  {
    "path": "examples/plot/plotmeshlabels.morpho",
    "content": "// Plot mesh labels \nimport plot\nimport meshgen \n\n/* Create a lo-res spherical mesh for demonstration purposes */\nvar dom = fn (x) -(x[0]^2+x[1]^2+x[2]^2-1)\nvar mg = MeshGen(dom, [-1..1:0.5, -1..1:0.5, -1..1:0.5], quiet=true)\nvar m = mg.build()\n\n// By default, we just get vertex labels\nfn outward (x) { return 0.1*x }\nShow(plotmesh(m, grade=[0,1])+\n     plotmeshlabels(m, offset=outward)+\n     plotaxes([1.2,0,0],size=0.5)) // And axes too! \n\n// Show selected labels \nvar s = Selection(m, fn (x,y,z) x<0)\ns.addgrade(1)\n\nShow(plotmesh(m, grade=[0,1])+\n     plotmeshlabels(m, selection=s, grade=1, offset=outward))\n\n// Show labels for multiple grades at once in different colors \nShow(plotmesh(m, grade=[0,1])+\n     plotmeshlabels(m, grade=[0,1,3], \n                       fontsize=8, \n                       color={0: White, 1: Red, 3: Blue})\n     ) \n"
  },
  {
    "path": "examples/plot/plotselection.morpho",
    "content": "// Plot a selection \nimport plot\nimport meshtools\n\n/* Create a simple mesh for demonstration purposes */\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:0.2, -1..1:0.2)\nm.addgrade(1)\n\n// Visualize the boundary \nvar s = Selection(m, boundary=true)\nShow(plotselection(m, s, grade=[0,1,2]))\n\n// Select portions of a mesh by region \nvar s2 = Selection(m, fn (x,y,z) x<=0)\ns2.addgrade(2)\nShow(plotselection(m, s2, grade=[0,1,2]))"
  },
  {
    "path": "examples/plot/scalebar.morpho",
    "content": "// Demonstrates drawing a scalebar alongside the output of plotfield \n\nimport color\nimport plot\nimport meshgen \n\nvar m = AreaMesh(fn (u,v) [u, v, 0], -1..1:0.1, -1..1:0.1)\nvar f = Field(m, fn (x,y,z) sin(2*Pi*x*y))\n\n// Add a scalebar \nShow(plotfield(f, style=\"interpolate\", colormap=ViridisMap(), scalebar=ScaleBar(posn=[1.2,0,0]))) \n\n// Showcases some of the many options for Scalebar \nvar sb = ScaleBar(nticks=8,          // Maximum number of ticks\n                  length=1,          // Length of scalebar\n                  posn=[0,-1.2,0],   // Position of scalebar  \n                  dirn=[1,0,0],      // Direction in which scalebar is to be drawn\n                  tickdirn=[0,-1,0], // Direction in which to draw ticks\n                  textdirn=[1,0,0],  // Direction to draw text\n                  textvertical=[0,1,0], // Vertical direction for text \n                  textcolor=White,   // Text color\n                  fontsize=10 )      // Font size \n\nShow(plotfield(f, style=\"interpolate\", colormap=InfernoMap(), scalebar=sb))\n\n\n"
  },
  {
    "path": "examples/povray/testCamera.morpho",
    "content": "import meshtools\nimport implicitmesh\nimport plot\nimport povray \n\nvar c = Matrix([0.1,0.2,0.3])\nvar impl = ImplicitMeshBuilder(fn (x,y,z) (x-c[0])^2/(0.5^2)+(y-c[1])^2/(0.75^2)+(z-c[2])^2-1)\n\nvar m = impl.build(stepsize=0.25)\n\nvar g = plotmesh(m)\n\nvar pov = POVRaytracer(g)\n\npov.render(\"default.pov\")\n\nvar camera = Camera(look_at = c)\n\npov = POVRaytracer(g, camera = camera)\n\npov.render(\"centered.pov\")\n\npov.viewpoint = c + Matrix([-5,0,0])\n\npov.render(\"centered_xview.pov\")\n\npov.viewpoint = c + Matrix([0,-5,0])\n\npov.render(\"centered_yview.pov\")\n\npov.sky = Matrix([1,0,0])\n\npov.render(\"centered_yview_fixed_rotated.pov\")\n"
  },
  {
    "path": "examples/povray/testpovray.morpho",
    "content": "// Demonstrate use of the povray module to raytrace graphics\nimport constants\nimport color\nimport plot\nimport povray\n\nvar icos = [[-0.688191, 0, 0.131433], [0.688191,\n  0, -0.131433], [-0.212663, -0.654508, 0.131433], [-0.212663,\n  0.654508, 0.131433], [0.556758, -0.404508, 0.131433], [0.556758,\n  0.404508, 0.131433], [-0.131433, -0.404508, 0.556758], [-0.131433,\n  0.404508, 0.556758], [-0.344095, -0.25, -0.556758], [-0.344095,\n  0.25, -0.556758], [0.344095, -0.25, 0.556758], [0.344095, 0.25,\n  0.556758], [0.425325,\n  0, -0.556758], [-0.556758, -0.404508, -0.131433], [-0.556758,\n  0.404508, -0.131433], [-0.425325, 0. ,\n  0.556758], [0.131433, -0.404508, -0.556758], [0.131433,\n  0.404508, -0.556758], [0.212663, -0.654508, -0.131433], [0.212663,\n  0.654508, -0.131433]]\n\nvar faces = [[14, 9, 8, 13, 0], [1, 5, 11, 10, 4], [4, 10, 6, 2, 18], [10, 11, 7,\n             15, 6], [11, 5, 19, 3, 7], [5, 1, 12, 17, 19], [1, 4, 18, 16,\n             12], [3, 19, 17, 9, 14], [17, 12, 16, 8, 9], [16, 18, 2, 13, 8], [2,\n             6, 15, 0, 13], [15, 7, 3, 14, 0]]\n\n\nfn demo() {\n  var p=PolyhedronMesh(icos, faces)\n  var f=Field(p, fn (x,y,z) x)\n  var g = plotfield(f, style=\"interpolate\")\n\n  for (i in 0...30) {\n    var xx = Matrix([2*(random()-1/2), 2*(random()-1/2), 2*(random()-1/2)])\n    while (xx.norm()<0.7) xx = Matrix([2*(random()-1/2), 2*(random()-1/2), 2*(random()-1/2)])\n    g.display(Sphere(xx, 0.05*(1+random()), color=Color(random(),random(),random())))\n  }\n  g.display(Cylinder([-1,-1,-1],[1,1,1], aspectratio=0.01, color=Gray(0.3)))\n  g.display(Arrow([-1,-1,-1],[-0.5,-1,-1], aspectratio=0.05, color=Red))\n  g.display(Arrow([-1,-1,-1],[-1,-0.5,-1], aspectratio=0.05, color=Green))\n  g.display(Arrow([-1,-1,-1],[-1,-1,-0.5], aspectratio=0.05, color=Blue))\n\n  var c = []\n  for (t in 0...2*Pi:2*Pi/40) c.append([cos(t), sin(t),0])\n  g.display(Tube(c, 0.05, color=Gray(0.5), closed=true))\n\n  g.background = White\n\n  return g\n}\n\nvar g=demo()\n\nvar pov = POVRaytracer(g)\npov.viewpoint=Matrix([0,0,5])\npov.viewangle=35\npov.light=[Matrix([10,10,10]), Matrix([-10,-10,10])]\npov.render(\"out.pov\")\n\nShow(g)\n"
  },
  {
    "path": "examples/povray/testpovraytext.morpho",
    "content": "import plot \nimport povray \nimport meshtools\nvar g = Graphics()\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:0.5, -1..1:0.5)\nm.addgrade(1)\ng.display(Text(\"Square mesh\", [-0.65,1.1,0], size=20, dirn=[1,0,0], vertical=[0,1,0]))\n\ng = g + plotmesh(m, grade=1)\nShow(g)\nvar pov = POVRaytracer(g)\n\npov.viewpoint = Matrix([0,0,8])\n\npov.render(\"testText.pov\")\n"
  },
  {
    "path": "examples/povray/testpovraytransmitfilter.morpho",
    "content": "// Demonstrate use of the povray module to raytrace graphics.\n// Additionally, demonstrate the use of the transmit and filter\n// attributes in the Graphics objects. These attributes are used by\n// povray to make the graphics transparent.\nimport constants\nimport color\nimport plot\nimport povray\nimport meshtools\n\nvar transmit = 0.5\nvar filter = 0.5\n\nfn demo() {\n\n    var m1 = AreaMesh(fn (u,v) [u,v,-1], -1..1:0.5, -1..1:0.5)\n    var m2 = AreaMesh(fn (u,v) [u,1,v], -1..1:0.2, -1..1:0.2)\n\n    var gauss = Field(m2, fn(x,y,z) exp(-x^2-z^2))\n    var g = plotmesh(m1)\n    g = g + plotfield(gauss, style=\"interpolate\")\n\n    // Plot Blue spheres with increasing transmit values\n    g.display(Sphere(Matrix([-0.45,0.8,-0.8]), 0.1, color=Blue))\n    g.display(Sphere(Matrix([-0.15,0.8,-0.8]), 0.1, color=Blue, transmit=0.1))\n    g.display(Sphere(Matrix([0.15,0.8,-0.8]), 0.1, color = Blue, transmit = 0.4))\n    g.display(Sphere(Matrix([0.45, 0.8, -0.8]), 0.1, color = Blue, transmit = 0.7))\n    \n    // Plot Blue spheres with increasing filter values.\n    // Note the difference in the color of the shadow from the transmit ones.\n    g.display(Sphere(Matrix([-0.45,0.4,-0.8]), 0.1, color=Red))\n    g.display(Sphere(Matrix([-0.15,0.4,-0.8]), 0.1, color=Red, filter = 0.1))\n    g.display(Sphere(Matrix([0.15,0.4,-0.8]), 0.1, color = Red, filter = 0.4))\n    g.display(Sphere(Matrix([0.45, 0.4, -0.8]), 0.1, color = Red, filter = 0.7))\n    \n    // Test all the other Graphics objects with transmit and filter\n    g.display(Arrow([-0.5, 0.0, -0.8], [-0.1, 0.0, -0.8], color = Blue, transmit = 0.75))\n    g.display(Arrow([0.1, 0.0, -0.8], [0.5, 0.0, -0.8], color = Red, filter = 0.75))\n    \n    g.display(Cylinder([-0.5, -0.4, -0.8], [-0.1, -0.4, -0.8], color = Blue, transmit = 0.75))\n    g.display(Cylinder([0.1, -0.4, -0.8], [0.5, -0.4, -0.8], color = Red, filter = 0.75))\n        \n    var pts1 = [[-0.3, -0.75, -0.8], [-0.4, -0.85, -0.8], [-0.2, -0.85, -0.8], [-0.3, -0.75, -0.8]]\n    var pts2 = [[0.3, -0.75, -0.8], [0.2, -0.85, -0.8], [0.4, -0.85, -0.8], [0.3, -0.75, -0.8]]\n    g.display(Tube(pts1, 0.02, color=Blue, transmit = 0.75) )\n    g.display(Tube(pts2, 0.02, color=Red, filter = 0.75) )\n    g.background = White\n\n    return g\n}\n\nvar g = demo()\n\nvar pov = POVRaytracer(g)\npov.viewpoint = Matrix([-1.2, -6, 3.6])\npov.viewangle = 35\n// pov.light = [Matrix([10, 10, 10]), Matrix([-10, -10, 10])]\npov.light =[Matrix([-10, -10, 10])]\npov.render(\"out.pov\")\n\nShow(g)\n"
  },
  {
    "path": "examples/povray/text.morpho",
    "content": "import plot \nimport povray \n\nvar g = Graphics()\n\ng.display(Text(\"Hello World\", [0,0,0]))\n\nvar pov = POVRaytracer(g)\n\npov.render(\"helloworld.pov\")\n"
  },
  {
    "path": "examples/qtensor/dense_disk.mesh",
    "content": "vertices\n\n1 -1 0 0 \n2 -0.951057 -0.309017 0 \n3 -0.951057 0.309017 0 \n4 -0.809017 -0.587785 0 \n5 -0.809017 0.587785 0 \n6 -0.587785 -0.809017 0 \n7 -0.587785 0.809017 0 \n8 -0.5 -0.5 0 \n9 -0.5 0 0 \n10 -0.5 0.5 0 \n11 -0.309017 -0.951057 0 \n12 -0.309017 0.951057 0 \n13 0 -1 0 \n14 0 -0.5 0 \n15 0 0 0 \n16 0 0.5 0 \n17 0 1 0 \n18 0.309017 -0.951057 0 \n19 0.309017 0.951057 0 \n20 0.5 -0.5 0 \n21 0.5 0 0 \n22 0.5 0.5 0 \n23 0.587785 -0.809017 0 \n24 0.587785 0.809017 0 \n25 0.809017 -0.587785 0 \n26 0.809017 0.587785 0 \n27 0.951057 -0.309017 0 \n28 0.951057 0.309017 0 \n29 1 0 0 \n30 -0.725529 -0.404508 0 \n31 -0.880037 -0.448401 0 \n32 -0.654508 -0.543893 0 \n33 -0.698401 -0.698401 0 \n34 -0.543893 -0.654508 0 \n35 -0.154508 -0.975529 0 \n36 0 -0.75 0 \n37 -0.154508 -0.725529 0 \n38 -0.5 -0.25 0 \n39 -0.725529 -0.154508 0 \n40 -0.25 -0.5 0 \n41 -0.404508 -0.725529 0 \n42 0.154508 -0.975529 0 \n43 0.154508 -0.725529 0 \n44 -0.448401 -0.880037 0 \n45 -0.25 -0.25 0 \n46 -0.725529 0.404508 0 \n47 -0.654508 0.543893 0 \n48 -0.880037 0.448401 0 \n49 -0.725529 0.154508 0 \n50 -0.975529 0.154508 0 \n51 -0.75 0 0 \n52 -0.543893 0.654508 0 \n53 -0.698401 0.698401 0 \n54 -0.5 0.25 0 \n55 -0.25 0 0 \n56 -0.25 0.25 0 \n57 -0.448401 0.880037 0 \n58 -0.404508 0.725529 0 \n59 -0.25 0.5 0 \n60 -0.154508 0.725529 0 \n61 -0.975529 -0.154508 0 \n62 0 -0.25 0 \n63 0.25 -0.5 0 \n64 0.25 -0.25 0 \n65 0.404508 -0.725529 0 \n66 0.448401 -0.880037 0 \n67 0.543893 -0.654508 0 \n68 0.5 -0.25 0 \n69 0.25 0 0 \n70 0.725529 -0.404508 0 \n71 0.654508 -0.543893 0 \n72 0.880037 -0.448401 0 \n73 0.725529 -0.154508 0 \n74 0.975529 -0.154508 0 \n75 0.75 0 0 \n76 0.698401 -0.698401 0 \n77 0.5 0.25 0 \n78 0.25 0.25 0 \n79 0.25 0.5 0 \n80 0.404508 0.725529 0 \n81 0.154508 0.725529 0 \n82 0 0.25 0 \n83 0.154508 0.975529 0 \n84 0 0.75 0 \n85 0.543893 0.654508 0 \n86 0.448401 0.880037 0 \n87 -0.154508 0.975529 0 \n88 0.654508 0.543893 0 \n89 0.725529 0.404508 0 \n90 0.880037 0.448401 0 \n91 0.698401 0.698401 0 \n92 0.725529 0.154508 0 \n93 0.975529 0.154508 0 \n\nedges\n\n1 2 30 \n2 2 31 \n3 4 32 \n4 4 33 \n5 6 34 \n6 11 35 \n7 13 36 \n8 11 37 \n9 8 38 \n10 2 39 \n11 8 40 \n12 8 41 \n13 13 42 \n14 14 43 \n15 6 44 \n16 9 45 \n17 3 46 \n18 5 47 \n19 3 48 \n20 3 49 \n21 1 50 \n22 1 51 \n23 7 52 \n24 5 53 \n25 9 54 \n26 9 55 \n27 10 56 \n28 7 57 \n29 10 58 \n30 10 59 \n31 12 60 \n32 1 61 \n33 14 62 \n34 14 63 \n35 15 64 \n36 18 65 \n37 18 66 \n38 20 67 \n39 20 68 \n40 15 69 \n41 20 70 \n42 20 71 \n43 25 72 \n44 21 73 \n45 27 74 \n46 21 75 \n47 23 76 \n48 21 77 \n49 15 78 \n50 16 79 \n51 19 80 \n52 16 81 \n53 15 82 \n54 17 83 \n55 16 84 \n56 22 85 \n57 19 86 \n58 12 87 \n59 22 88 \n60 22 89 \n61 26 90 \n62 24 91 \n63 21 92 \n64 28 93 \n65 8 30 \n66 4 31 \n67 8 32 \n68 6 33 \n69 8 34 \n70 13 35 \n71 14 36 \n72 14 37 \n73 9 38 \n74 9 39 \n75 14 40 \n76 11 41 \n77 18 42 \n78 18 43 \n79 11 44 \n80 14 45 \n81 10 46 \n82 10 47 \n83 5 48 \n84 9 49 \n85 3 50 \n86 9 51 \n87 10 52 \n88 7 53 \n89 10 54 \n90 15 55 \n91 15 56 \n92 12 57 \n93 12 58 \n94 16 59 \n95 16 60 \n96 2 61 \n97 15 62 \n98 20 63 \n99 20 64 \n100 20 65 \n101 23 66 \n102 23 67 \n103 21 68 \n104 21 69 \n105 27 70 \n106 25 71 \n107 27 72 \n108 27 73 \n109 29 74 \n110 29 75 \n111 25 76 \n112 22 77 \n113 22 78 \n114 22 79 \n115 22 80 \n116 19 81 \n117 16 82 \n118 19 83 \n119 17 84 \n120 24 85 \n121 24 86 \n122 17 87 \n123 26 88 \n124 28 89 \n125 28 90 \n126 26 91 \n127 28 92 \n128 29 93 \n129 30 31 \n130 30 32 \n131 31 32 \n132 32 33 \n133 32 34 \n134 33 34 \n135 35 37 \n136 35 36 \n137 36 37 \n138 30 39 \n139 30 38 \n140 38 39 \n141 37 41 \n142 37 40 \n143 40 41 \n144 36 42 \n145 36 43 \n146 42 43 \n147 34 44 \n148 34 41 \n149 41 44 \n150 38 40 \n151 38 45 \n152 40 45 \n153 46 48 \n154 46 47 \n155 47 48 \n156 49 50 \n157 49 51 \n158 50 51 \n159 47 53 \n160 47 52 \n161 52 53 \n162 54 55 \n163 54 56 \n164 55 56 \n165 52 57 \n166 52 58 \n167 57 58 \n168 58 59 \n169 58 60 \n170 59 60 \n171 46 49 \n172 46 54 \n173 49 54 \n174 39 61 \n175 39 51 \n176 51 61 \n177 45 55 \n178 45 62 \n179 55 62 \n180 62 63 \n181 62 64 \n182 63 64 \n183 43 63 \n184 43 65 \n185 63 65 \n186 65 66 \n187 65 67 \n188 66 67 \n189 64 69 \n190 64 68 \n191 68 69 \n192 70 71 \n193 70 72 \n194 71 72 \n195 73 75 \n196 73 74 \n197 74 75 \n198 68 70 \n199 68 73 \n200 70 73 \n201 67 71 \n202 67 76 \n203 71 76 \n204 69 78 \n205 69 77 \n206 77 78 \n207 79 81 \n208 79 80 \n209 80 81 \n210 78 82 \n211 78 79 \n212 79 82 \n213 81 84 \n214 81 83 \n215 83 84 \n216 80 86 \n217 80 85 \n218 85 86 \n219 60 87 \n220 60 84 \n221 84 87 \n222 88 89 \n223 88 90 \n224 89 90 \n225 85 88 \n226 85 91 \n227 88 91 \n228 77 92 \n229 77 89 \n230 89 92 \n231 75 92 \n232 75 93 \n233 92 93 \n234 56 59 \n235 56 82 \n236 59 82 \nfaces\n\n1 2 30 31 \n2 4 32 33 \n3 11 35 37 \n4 2 30 39 \n5 11 37 41 \n6 13 36 42 \n7 6 34 44 \n8 8 38 40 \n9 3 46 48 \n10 3 49 50 \n11 5 47 53 \n12 9 54 55 \n13 7 52 57 \n14 10 58 59 \n15 3 46 49 \n16 2 39 61 \n17 9 45 55 \n18 14 62 63 \n19 14 43 63 \n20 18 65 66 \n21 15 64 69 \n22 20 70 71 \n23 21 73 75 \n24 20 68 70 \n25 20 67 71 \n26 15 69 78 \n27 16 79 81 \n28 15 78 82 \n29 16 81 84 \n30 19 80 86 \n31 12 60 87 \n32 22 88 89 \n33 22 85 88 \n34 21 77 92 \n35 21 75 92 \n36 10 56 59 \n37 8 30 32 \n38 4 31 32 \n39 30 31 32 \n40 8 32 34 \n41 6 33 34 \n42 32 33 34 \n43 13 35 36 \n44 14 36 37 \n45 35 36 37 \n46 8 30 38 \n47 9 38 39 \n48 30 38 39 \n49 14 37 40 \n50 8 40 41 \n51 37 40 41 \n52 14 36 43 \n53 18 42 43 \n54 36 42 43 \n55 8 34 41 \n56 11 41 44 \n57 34 41 44 \n58 9 38 45 \n59 14 40 45 \n60 38 40 45 \n61 10 46 47 \n62 5 47 48 \n63 46 47 48 \n64 9 49 51 \n65 1 50 51 \n66 49 50 51 \n67 10 47 52 \n68 7 52 53 \n69 47 52 53 \n70 10 54 56 \n71 15 55 56 \n72 54 55 56 \n73 10 52 58 \n74 12 57 58 \n75 52 57 58 \n76 12 58 60 \n77 16 59 60 \n78 58 59 60 \n79 10 46 54 \n80 9 49 54 \n81 46 49 54 \n82 9 39 51 \n83 1 51 61 \n84 39 51 61 \n85 14 45 62 \n86 15 55 62 \n87 45 55 62 \n88 15 62 64 \n89 20 63 64 \n90 62 63 64 \n91 18 43 65 \n92 20 63 65 \n93 43 63 65 \n94 20 65 67 \n95 23 66 67 \n96 65 66 67 \n97 20 64 68 \n98 21 68 69 \n99 64 68 69 \n100 27 70 72 \n101 25 71 72 \n102 70 71 72 \n103 27 73 74 \n104 29 74 75 \n105 73 74 75 \n106 21 68 73 \n107 27 70 73 \n108 68 70 73 \n109 23 67 76 \n110 25 71 76 \n111 67 71 76 \n112 21 69 77 \n113 22 77 78 \n114 69 77 78 \n115 22 79 80 \n116 19 80 81 \n117 79 80 81 \n118 22 78 79 \n119 16 79 82 \n120 78 79 82 \n121 19 81 83 \n122 17 83 84 \n123 81 83 84 \n124 22 80 85 \n125 24 85 86 \n126 80 85 86 \n127 16 60 84 \n128 17 84 87 \n129 60 84 87 \n130 26 88 90 \n131 28 89 90 \n132 88 89 90 \n133 24 85 91 \n134 26 88 91 \n135 85 88 91 \n136 22 77 89 \n137 28 89 92 \n138 77 89 92 \n139 29 75 93 \n140 28 92 93 \n141 75 92 93 \n142 15 56 82 \n143 16 59 82 \n144 56 59 82 \n"
  },
  {
    "path": "examples/qtensor/qtensor.morpho",
    "content": "/* Problem of a 2D nematic confined to a disk with parallel anchoring boundary condition, modeled by a Q-tensor free energy.\nShowcases: AreaIntegral, LineIntegral, MeshRefiner, POVRaytracer, Graphics\nThis example demonstrates the following features of morpho:\n* It uses AreaIntegral to define the polynomial form of free energy. \n* It uses mesh refinement to get a better resolution for the solution at the nematic defect locations, where there is more distortion in the field value.\n* To visualize the Q-tensor, we need to see both the director (orientations) and the scalar order parameter (degree of order) together. This script shows how that can be done by adding the graphics together.\n*/\nimport meshtools\nimport optimize\nimport plot\nimport povray\n\n// Define parameters that control the script options\n\n/* The flag `refine_adaptively` is set to decide whether to refine our mesh in regions of high elastic energy. In comparison to refining everywhere, this method gives a speed up while still giving an accurate result. When this flag is `true`, the script performs `refine_iters` (defined below) number of steps of successive adaptive refinement and relaxation. While running this script for the first time, it's ideal to set this to `false`. After getting the result, the user can then turn on adaptive refinement and compare.\nThe flag `visualize_refinement`, when `true`, gives further visual insight into the refinement by displaying the regions it selects for refinement, superimposed with the director. It also displays the refined mesh at each stage.\n*/\nvar refine_adaptively = false // Flag to turn mesh-refinement on/off\nvar refine_iters = 4 // Number of iterations of refimenent\nvar visualize_refinement = false // Flag to turn plotting the refinement regions and refined meshes on/off\n\n// Define physical parameters\n\nvar rho = 1.3 // Density. rho>1 results in the nematic phase\nvar EA = 3 // Anchoring strength\nvar K = 0.01 // Bending modulus. K=0.01 yields two +1/2 defects, whereas K=1.0 yields a single +1 defect.\n\nvar a2 = (1-rho) // Coefficient of Tr(Q^2) in the Free energy \nvar a4 = (1+rho)/rho^2 // Coefficient of (Tr(Q^2))^2 in the Free energy\n\n// Import disk mesh\n\nvar m = Mesh(\"dense_disk.mesh\")\n\n// Select boundary\nvar bnd = Selection(m, boundary=true)\nbnd.addgrade(0) // add point elements\n\n// Create director field\n\n/* \nSince a 2D Q-tensor for a uniaxial nematic is symmetric and traceless, there are only two independent components, Qxx and Qxy. We thus define the q_tensor to be a 2D vector, with its components being Qxx and Qxy. We initialize it to be zero and add a random noise of 1% strength as a perturbation to start the gradient descent. The Q-tensor usually takes values of order 1, so we can use 0.01*random(1) as the initial 1% noise.\n*/\n\nvar q_tensor = Field(m, fn(x,y,z) Matrix([0.01*random(1), 0.01*random(1)]))\n\n// Define various components of the energy.\n// Since these functions are meant to be arguments to AreaIntegral and LineIntegral, their first input argument has to be the position vector `x`. The field argument (the q-tensor in our case) comes after.\n\n// Define bulk free energy functional\nfn landau(x, q) {\n  var qt = q.norm()\n  var qt2=qt*qt\n  return a2*qt2 + a4*qt2*qt2\n}\n\n// Define anchoring energy functional at the boundary\nfn anchoring(x, q) {\n  var t = tangent()\n  var wxx = t[0]*t[0]-0.5\n  var wxy = t[0]*t[1]\n  return (q[0]-wxx)^2+(q[1]-wxy)^2\n}\n\n// The bulk free energy is now the area-integral of the landau functional with the field input being q_tensor\nvar bulk = AreaIntegral(landau, q_tensor)\n// Similarly, the anchoring free energy is now the line-integral of the anchoring functional with the field input being q_tensor\nvar anchor = LineIntegral(anchoring, q_tensor)\n\n// The elastic energy is the grad-squared of the q-tensor\nvar elastic = GradSq(q_tensor)\n\n// Specify the problem\nvar problem = OptimizationProblem(m)\n\n// Add the energies to the problem\nproblem.addenergy(bulk)\n// The anchoring energy has a prefactor of EA, and is applied only to the boundary\nproblem.addenergy(anchor, selection=bnd, prefactor = EA)\n// The elastic energy has a prefactor of K\nproblem.addenergy(elastic, prefactor = K)\n\n// Set up the optimizer to optimize this problem wrt the Field values.\nvar opt = FieldOptimizer(problem, q_tensor)\n\n// `iters` specifies the number of iterations for the linelearch. From emperical observation, iters=500 works well. \nvar iters = 500 \n// Minimize the free energy!\nopt.conjugategradient(iters)\n\n// Define some helper functions to plot the result\n\n// Function to get the director (unit vector n) from the Q-tensor\nfn qtodirector(q) {\n  var S = 2*q.norm()\n  var Q = q/S\n  var nx = sqrt(Q[0]+0.5)\n  var ny = abs(Q[1]/nx)\n  nx = nx*sign(Q[1])\n  return Matrix([nx,ny,0])\n}\n\n// Function to get the scalar order parameter S from the Q-tensor\nfn qtoorder(q) {\n  var S = 2*q.norm()\n  return S\n}\n\n/* Function to visualize a director field\nThis function returns a `Graphics()` object that has the director field visualized in the form of cylinders. Here,\n1. m is the mesh\n2. nn is the director field and\n3. dl is the half-length of the cylinders to be drawn. \n4. aspectratio (optional argument) is the aspect ratio of the cylinders. (default is 0.3 here)\n*/\nfn visualize(m, nn, dl, aspectratio=0.3) {\n  var v = m.vertexmatrix()\n  var nv = v.dimensions()[1]\n  var g = Graphics()\n  for (i in 0...nv) {\n    var x = v.column(i)\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=aspectratio))\n  }\n  return g\n}\n\n/* Adaptive mesh refinement\nHere, we refine the mesh based on the elastic energy density and linesearch successively.\n*/\nif (refine_adaptively){\n  for (i in 1..refine_iters){\n  \n    // Select elements that have a large contribution to the elastic energy\n    var en = elastic.integrand(q_tensor)\n    var mean = en.sum()/en.count()\n    var srefine = Selection(m) // Start with an empty selection\n    for (id in 0...en.count()) if (en[0,id]>1.5*mean) srefine[2,id]=true // Identify high (compared to the mean) energy elements\n  \n    // Visualize the selected region for refimenemt \n    \n    if (visualize_refinement){\n      var gs = plotselection(m, srefine, grade=2)\n      var nn = Field(m, Matrix([1,0,0])) // Initialize to some vector\n      for (i in 0...m.count()) nn[i]=qtodirector(q_tensor[i])\n      var gnn=visualize(m, nn, 0.05)\n      Show(gs+gnn)\n    }\n  \n    // Create a mesh refiner\n    var mr=MeshRefiner([m, q_tensor, bnd])\n  \n    // Perform the refinement\n    var refmap = mr.refine(selection=srefine)\n  \n    \n    // Now that refinement is done, update the problems and optimizers\n    for (el in [problem, opt]) el.update(refmap)\n    \n    // Use the new mesh and field\n    m = refmap[m]\n    q_tensor = refmap[q_tensor]\n    bnd = refmap[bnd]\n    \n    equiangulate(m)\n  \n    // Visualize the refined mesh at each stage\n    if (visualize_refinement) Show(plotmesh(m, grade=1))\n  \n    opt.conjugategradient(iters)\n  }\n}\n\n// Visualize the result\n\n  \n// Convert the q-tensor to the director and order\n\nvar nn = Field(m, Matrix([1,0,0])) // Initialize to some vector\nfor (i in 0...m.count()) nn[i]=qtodirector(q_tensor[i])\n\nvar S = Field(m, 0) // Initialize to a scalar\nfor (i in 0...m.count()) S[i]=qtoorder(q_tensor[i])\n\n// Plot the scalar order field\nvar splot = plotfield(S, style=\"interpolate\", colormap = ViridisMap(), scalebar=ScaleBar(posn=[1.2,0,0]))\n// Plot the director using the visualize function we wrote above\nvar gnn=visualize(m, nn, 0.05)\n\n// Both `plotfield` and `visualize` return a Graphics object. We can just add the two to get the final plot \nvar gdisp = splot+gnn\n\n// Generate a POVRaytracer object using the Graphics() object\nvar pov = POVRaytracer(gdisp)\n\n// Set the viewing angle in degrees. The larger the angle, the larger the field of view.\npov.viewangle=35\npov.viewpoint = Matrix([0,0,6])\npov.light = [Matrix([3,4,5]), Matrix([-3,-4,5])]\n// Render to a .pov file. This also generates a .png image with the same name.\npov.render(\"Qtensor_K_${K}.pov\")\n\n// Open up the viewer application\nShow(gdisp) \n"
  },
  {
    "path": "examples/qtensor/src.lyx",
    "content": "#LyX file created by tex2lyx 2.3\n\\lyxformat 544\n\\begin_document\n\\begin_header\n\\save_transient_properties true\n\\origin /Users/timatherton/Documents/Programs/morpho-public/morpho/examples/qtensor/\n\\textclass article\n\\begin_preamble\n\\usepackage{physics}\n\\usepackage{xcolor}\n\\definecolor{codegray}{rgb}{0.9,0.9,0.9}\n\\usepackage{listings}\n\\newcommand{\\morpho}{\\textit{morpho}}\n\\newcommand{\\Morpho}{\\textit{Morpho}}\n\\title{Q-tensor model of nematics}\n\\author{Chaitanya Joshi}\n\\date{October 2021}\n\n\n\\end_preamble\n\\use_default_options false\n\\maintain_unincluded_children false\n\\language english\n\\language_package none\n\\inputencoding utf8\n\\fontencoding default\n\\font_roman \"default\" \"default\"\n\\font_sans \"default\" \"default\"\n\\font_typewriter \"default\" \"default\"\n\\font_math \"auto\" \"auto\"\n\\font_default_family default\n\\use_non_tex_fonts false\n\\font_sc false\n\\font_osf false\n\\font_sf_scale 100 100\n\\font_tt_scale 100 100\n\\use_microtype false\n\\use_dash_ligatures true\n\\graphics default\n\\default_output_format default\n\\output_sync 0\n\\bibtex_command default\n\\index_command default\n\\paperfontsize default\n\\spacing single\n\\use_hyperref false\n\\papersize default\n\\use_geometry false\n\\use_package amsmath 1\n\\use_package amssymb 0\n\\use_package cancel 0\n\\use_package esint 1\n\\use_package mathdots 0\n\\use_package mathtools 0\n\\use_package mhchem 0\n\\use_package stackrel 0\n\\use_package stmaryrd 0\n\\use_package undertilde 0\n\\cite_engine basic\n\\cite_engine_type default\n\\biblio_style plain\n\\use_bibtopic false\n\\use_indices false\n\\paperorientation portrait\n\\suppress_date false\n\\justification true\n\\use_refstyle 0\n\\use_minted 0\n\\index Index\n\\shortcut idx\n\\color #008000\n\\end_index\n\\secnumdepth 3\n\\tocdepth 3\n\\paragraph_separation indent\n\\paragraph_indentation default\n\\is_math_indent 0\n\\math_numbering_side default\n\\quotes_style english\n\\dynamic_quotes 0\n\\papercolumns 1\n\\papersides 1\n\\paperpagestyle default\n\\tracking_changes false\n\\output_changes false\n\\html_math_output 0\n\\html_css_as_file 0\n\\html_be_strict false\n\\end_header\n\n\\begin_body\n\n\\begin_layout Standard\n\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n\\backslash\nlstset\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n{\n\\end_layout\n\n\\end_inset\n\n language=Java, tabsize=4, basicstyle=\n\\family typewriter\n, backgroundcolor=\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n\\backslash\ncolor{codegray}\n\\end_layout\n\n\\end_inset\n\n, showstringspaces=false \n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\end_inset\n\n \n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n\\backslash\nmaketitle\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nIntroduction\n\\end_layout\n\n\\begin_layout Standard\nIn 2D, for a uniaxial nematic, we can define a Q-tensor: \n\\begin_inset Formula \\[\nQ_{ij} = S (n_i n_j - 1/2 \\delta_{ij})\n\\]\n\\end_inset\n\nHere, the \n\\begin_inset Formula $-1/2 \\delta_{ij}$\n\\end_inset\n\n is added for convenience, to make the matrix traceless: \n\\begin_inset Formula \\[\\text{Tr}(\\mathbf{Q}) = Q_{ii} = S(n_i n_i - 1/2 \\delta_{ii}) = S(1 - 1/2(2)) = 0\n\\]\n\\end_inset\n\nNow, the Q-tensor is also symmetric by definition: \n\\begin_inset Formula \\[\nQ_{ij} = Q_{ji}\n\\]\n\\end_inset\n\nDue to these two reasons we can write the Q-tensor as a function of only \n\\begin_inset Formula $Q_{xx}$\n\\end_inset\n\n and \n\\begin_inset Formula $Q_{xy}$\n\\end_inset\n\n: \n\\begin_inset Formula \\[\n\\mathbf{Q} = \\begin{bmatrix}Q_{xx} & Q_{xy} \\\\ Q_{xy} & -Q_{xx} \\end{bmatrix}\n\\]\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSimple Passive Nematic Model\n\\end_layout\n\n\\begin_layout Standard\nThe Landau-de Gennes equilibrium free energy for a nematic liquid crystal can be written in terms of the Q-tensor: \n\\begin_inset Formula \\begin{align*}\nF_{LDG} = &\\int_\\Omega d^2{\\bf x} \\ \\left(\\frac{a_2}{2} \\text{Tr}(\\mathbf{Q}^2) + \\frac{a_4}{4} (\\text{Tr} \\mathbf{Q}^2)^2 + \\frac{K}{2}(\\nabla\\mathbf{Q})^2 \\right) \\\\\n&+ \\oint_{\\partial\\Omega} d{\\bf x} \\frac{1}{2}E_A \\text{Tr}[(\\mathbf{Q}-\\mathbf{W})^2]\n\\end{align*}\n\\end_inset\n\nwhere \n\\begin_inset Formula $a_2 = (\\rho-1)$\n\\end_inset\n\n and \n\\begin_inset Formula $a_4 = (\\rho+1)/\\rho^2$\n\\end_inset\n\n set the isotropic to nematic transition with \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n being the non-dimensional density. The system is in the isotropic state for \n\\begin_inset Formula $\\rho<1$\n\\end_inset\n\n and in the nematic phase when \n\\begin_inset Formula $\\rho>1$\n\\end_inset\n\n. In the nematic phase, \n\\begin_inset Formula $\\ell_n = \\sqrt{K/a_2}$\n\\end_inset\n\n sets the nematic coherence length. Now, \n\\begin_inset Formula \\[\n\\mathbf{Q}^2 = \\begin{bmatrix}Q_{xx} & Q_{xy} \\\\ Q_{xy} & -Q_{xx} \\end{bmatrix} \\begin{bmatrix}Q_{xx} & Q_{xy} \\\\ Q_{xy} & -Q_{xx} \\end{bmatrix} = (Q_{xx}^2+Q_{xy}^2) \\begin{bmatrix} 1 & 0 \\\\ 0 & 1 \\end{bmatrix}\n\\]\n\\end_inset\n\nHence, \n\\begin_inset Formula \\[\n\\text{Tr}(\\mathbf{Q}^2) = 2(Q_{xx}^2+Q_{xy}^2)\n\\]\n\\end_inset\n\nSimilarly, \n\\begin_inset Formula \\[\n(\\nabla \\mathbf{Q})^2 = \\partial_i Q_{kj}\\partial_i Q_{kj} = 2 \\{ (\\partial_x Q_{xx})^2+(\\partial_xQ_{xy})^2 + (\\partial_y Q_{xx})^2+(\\partial_y Q_{xy})^2 \\}\n\\]\n\\end_inset\n\nNow, the second term is a boundary integral, with \n\\begin_inset Formula $E_A$\n\\end_inset\n\n being the anchoring strength. \n\\begin_inset Formula $\\mathbf{W}$\n\\end_inset\n\n is the tensor corresponding to the boundary condition. For instance, for parallel anchoring, \n\\begin_inset Formula \\[\nW_{ij} = (t_i t_j - 1/2 \\delta_{ij})\n\\]\n\\end_inset\n\nwhere \n\\begin_inset Formula $t_i$\n\\end_inset\n\n is a component of the tangent vector at the boundary. \n\\begin_inset Formula $\\mathbf{W}$\n\\end_inset\n\n is also a symmetric traceless tensor with two independent components \n\\begin_inset Formula $W_{xx}$\n\\end_inset\n\n and \n\\begin_inset Formula $W_{xy}$\n\\end_inset\n\n. The boundary term becomes: \n\\begin_inset Formula \\[ \\text{Tr}[(\\mathbf{Q}-\\mathbf{W})^2] = 2\\{ Q_{xx}^2 + Q_{xy}^2 - 2(Q_{xx}W_{xx} + Q_{xy}W_{xy}) + W_{xx}^2 + W_{xy}^2 \\} \n\\]\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nVector formulation\n\\end_layout\n\n\\begin_layout Standard\nWe can formulate all these expressions in terms of vector quantities: \n\\begin_inset Formula \\[\\vec{q} \\equiv \\{ Q_{xx}, Q_{xy}\\}\\]\n\\end_inset\n\n\n\\begin_inset Formula \\[\\vec{w} \\equiv \\{ w_{xx}, w_{xy}\\}\\]\n\\end_inset\n\nThus, \n\\begin_inset Formula \\[\n\\text{Tr}(\\mathbf{Q}^2) = 2 ||\\vec{q}||^2\n\\]\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\begin_inset Formula \\[\n(\\nabla \\mathbf{Q})^2 = 2 ||\\nabla \\vec{q}||^2\n\\]\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\begin_inset Formula \\[\n\\text{Tr}[(\\mathbf{Q}-\\mathbf{W})^2] = 2 ||\\vec{q}-\\vec{w}||^2\n\\]\n\\end_inset\n\nWith these, we want to minimize the area-integral of \n\\begin_inset Formula \\[\nF = \\int_\\Omega d^2{\\bf x} \\ \\left( a_2 ||\\vec{q}||^2 + a_4 ||\\vec{q}||^4 + K||\\nabla\\vec{q}||^2 \\right)\n\\]\n\\end_inset\n\ntogether with the line-integral energy \n\\begin_inset Formula \\[\n\\oint_{\\partial\\Omega} d{\\bf x} \\ E_A ||\\vec{q}-\\vec{w}||^2\n\\]\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\n\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n\\backslash\nMorpho\n\\end_layout\n\n\\end_inset\n\n \n\\begin_inset space \\space{}\n\n\\end_inset\n\nimplementation\n\\end_layout\n\n\\begin_layout Standard\nThis free energy is readily set up in \n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n\\backslash\nmorpho\n\\end_layout\n\n\\end_inset\n\n. For this problem, we will consider a 2D disk geometry with unit radius. We use \n\\begin_inset Formula $\\rho=1.3$\n\\end_inset\n\n, so that we are deep in the nematic regime. We fix \n\\begin_inset Formula $E_{\\text{A}}=3$\n\\end_inset\n\n, which sets strong anchoring at the boundary. With this strong tangential anchoring, we get a topological charge of \n\\begin_inset Formula $+1$\n\\end_inset\n\n at the boundary, and this acts as a constraint. When the nematic coherence length is comparable to the disk diameter (\n\\begin_inset Formula $\\ell_n \\sim R$\n\\end_inset\n\n), the \n\\begin_inset Formula $+1$\n\\end_inset\n\n charge penetrates throughout the disk, whereas if (\n\\begin_inset Formula $\\ell_n \\ll R$\n\\end_inset\n\n), then a formation with 2 \n\\begin_inset Formula $+1/2$\n\\end_inset\n\n defects is more stable. To test this, we use two different values of \n\\begin_inset Formula $K$\n\\end_inset\n\n:, 0.01 and 1.0.\n\\end_layout\n\n\\begin_layout Standard\nWe first define all our parameters and import \n\\begin_inset Formula $\\texttt{disk.mesh}$\n\\end_inset\n\n from the tactoid example: \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nvar rho = 1.3 // Deep in the nematic phase\n\\end_layout\n\n\\begin_layout Plain Layout\nvar EA = 3 // Anchoring strength\n\\end_layout\n\n\\begin_layout Plain Layout\nvar K = 0.01 // Bending modulus\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\begin_layout Plain Layout\nvar a2 = (1-rho)\n\\end_layout\n\n\\begin_layout Plain Layout\nvar a4 = (1+rho)/rho^2\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\begin_layout Plain Layout\nvar m = Mesh(\"disk.mesh\")\n\\end_layout\n\n\\begin_layout Plain Layout\nvar m = refinemesh(m) // Refining for a better result\n\\end_layout\n\n\\begin_layout Plain Layout\nvar bnd = Selection(m, boundary=true)\n\\end_layout\n\n\\begin_layout Plain Layout\nbnd.addgrade(0) // add point elements\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\nWe define the Q-tensor in its vector form as discussed above, initializing it to small random values: \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nvar q_tensor = Field(m, fn(x,y,z)\n\\end_layout\n\n\\begin_layout Plain Layout\nMatrix([0.01*random(1), 0.01*random(1)]))\n\\end_layout\n\n\\end_inset\n\nNote that this incidentally makes the director parallel to a 45 degree line. We now define the bulk energy, the anchoring energy and the distortion free energy as follows: \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\n// Define bulk free energy\n\\end_layout\n\n\\begin_layout Plain Layout\nfn landau(x, q) {\n\\end_layout\n\n\\begin_layout Plain Layout\n  var qt = q.norm()\n\\end_layout\n\n\\begin_layout Plain Layout\n  var qt2=qt*qt\n\\end_layout\n\n\\begin_layout Plain Layout\n  return a2*qt2 + a4*qt2*qt2\n\\end_layout\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\begin_layout Plain Layout\n// Define anchoring energy at the boundary\n\\end_layout\n\n\\begin_layout Plain Layout\nfn anchoring(x, q) {\n\\end_layout\n\n\\begin_layout Plain Layout\n  var t = tangent()\n\\end_layout\n\n\\begin_layout Plain Layout\n  var wxx = t[0]*t[0]-0.5\n\\end_layout\n\n\\begin_layout Plain Layout\n  var wxy = t[0]*t[1]\n\\end_layout\n\n\\begin_layout Plain Layout\n  return (q[0]-wxx)^2+(q[1]-wxy)^2\n\\end_layout\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\begin_layout Plain Layout\nvar bulk = AreaIntegral(landau, q_tensor)\n\\end_layout\n\n\\begin_layout Plain Layout\nvar anchor = LineIntegral(anchoring, q_tensor)\n\\end_layout\n\n\\begin_layout Plain Layout\nvar elastic = GradSq(q_tensor)\n\\end_layout\n\n\\end_inset\n\nEquipped with the energies, we define the \n\\family typewriter\nOptimizationProblem\n\\family default\n: \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nvar problem = OptimizationProblem(m)\n\\end_layout\n\n\\begin_layout Plain Layout\nproblem.addenergy(bulk)\n\\end_layout\n\n\\begin_layout Plain Layout\nproblem.addenergy(elastic, prefactor = K)\n\\end_layout\n\n\\begin_layout Plain Layout\nproblem.addenergy(anchor, selection=bnd, prefactor=EA)\n\\end_layout\n\n\\end_inset\n\nTo minimize the energy with respect to the field, we define the \n\\family typewriter\nFieldOptimizer\n\\family default\n and perform a \n\\family typewriter\nlinesearch\n\\family default\n: \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nvar opt = FieldOptimizer(problem, q_tensor)\n\\end_layout\n\n\\begin_layout Plain Layout\nopt.linesearch(500)\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nVisualizing the result\n\\end_layout\n\n\\begin_layout Standard\nFor visualizing the final configuration, we use the same piece of code we used for the tactoid example, and define some additional helper functions to extract the director and the order from the Q-tensor. \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nfn sign(x) {\n\\end_layout\n\n\\begin_layout Plain Layout\n  if (x<0.0) return -1.0\n\\end_layout\n\n\\begin_layout Plain Layout\n  else if (x>0.0) return 1.0\n\\end_layout\n\n\\begin_layout Plain Layout\n  return 0.0\n\\end_layout\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nfn qtodirector(q) {\n\\end_layout\n\n\\begin_layout Plain Layout\n  var S = 2*q.norm()\n\\end_layout\n\n\\begin_layout Plain Layout\n  var Q = q/S\n\\end_layout\n\n\\begin_layout Plain Layout\n  var nx = sqrt(Q[0]+0.5)\n\\end_layout\n\n\\begin_layout Plain Layout\n  var ny = abs(Q[1]/nx)\n\\end_layout\n\n\\begin_layout Plain Layout\n  nx*=sign(Q[1])\n\\end_layout\n\n\\begin_layout Plain Layout\n  return Matrix([nx,ny,0])\n\\end_layout\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nfn qtoorder(q) {\n\\end_layout\n\n\\begin_layout Plain Layout\n  var S = 2*q.norm()\n\\end_layout\n\n\\begin_layout Plain Layout\n  return S\n\\end_layout\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\n// Convert the q-tensor to the director and order\n\\end_layout\n\n\\begin_layout Plain Layout\nvar nn = Field(m, Matrix([1,0,0]))\n\\end_layout\n\n\\begin_layout Plain Layout\nfor (i in 0...m.count()) nn[i]=qtodirector(q_tensor[i])\n\\end_layout\n\n\\begin_layout Plain Layout\nvar S = Field(m, 0)\n\\end_layout\n\n\\begin_layout Plain Layout\nfor (i in 0...m.count()) S[i]=qtoorder(q_tensor[i])\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\n// Function to visualize a director field\n\\end_layout\n\n\\begin_layout Plain Layout\nfn visualize(m, nn, dl) {\n\\end_layout\n\n\\begin_layout Plain Layout\n  var v = m.vertexmatrix()\n\\end_layout\n\n\\begin_layout Plain Layout\n  var nv = v.dimensions()[1]\n\\end_layout\n\n\\begin_layout Plain Layout\n  var g = Graphics()\n\\end_layout\n\n\\begin_layout Plain Layout\n  for (i in 0...nv) {\n\\end_layout\n\n\\begin_layout Plain Layout\n    var x = v.column(i)\n\\end_layout\n\n\\begin_layout Plain Layout\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl,\n\\end_layout\n\n\\begin_layout Plain Layout\n    aspectratio=0.3))\n\\end_layout\n\n\\begin_layout Plain Layout\n  }\n\\end_layout\n\n\\begin_layout Plain Layout\n  return g\n\\end_layout\n\n\\begin_layout Plain Layout\n}\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\begin_layout Plain Layout\n// Visualize the result\n\\end_layout\n\n\\begin_layout Plain Layout\n// var g=plotmesh(m, grade=1)\n\\end_layout\n\n\\begin_layout Plain Layout\nvar splot = plotfield(S, style=\"interpolate\")\n\\end_layout\n\n\\begin_layout Plain Layout\nvar gnn=visualize(m, nn, 0.05)\n\\end_layout\n\n\\begin_layout Plain Layout\nvar gdisp = splot+gnn\n\\end_layout\n\n\\begin_layout Plain Layout\nShow(gdisp)\n\\end_layout\n\n\\end_inset\n\nWe can go further and use the \n\\family typewriter\npovray\n\\family default\n module to render a \n\\family typewriter\n.png\n\\family default\n output: \n\\begin_inset listings\ninline false\nstatus open\n\n\\begin_layout Plain Layout\nvar pov = POVRaytracer(gdisp)\n\\end_layout\n\n\\begin_layout Plain Layout\npov.viewangle=35\n\\end_layout\n\n\\begin_layout Plain Layout\npov.render(\"Qtensor_K_${K}.pov\")\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Float figure\nwide false\nsideways false\nstatus open\n\n\n\\begin_layout Standard\n\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n\\backslash\ncentering\n\\end_layout\n\n\\end_inset\n\n \n\\begin_inset Graphics \n\tfilename Qtensor_K_1.png\n\twidth 45line%\n\n\\end_inset\n\n \n\\begin_inset Graphics \n\tfilename Qtensor_K_0.01.png\n\twidth 45line%\n\n\\end_inset\n\n \n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nFinal configuration of the director and order for (left) \n\\begin_inset Formula $K=1$\n\\end_inset\n\n and (right) \n\\begin_inset Formula $K=0.01$\n\\end_inset\n\n. The cylinders indicate the nematic director whereas the color indicates the scalar order parameter. Note how the \n\\begin_inset Formula $K=1$\n\\end_inset\n\n case has a single \n\\begin_inset Formula $+1$\n\\end_inset\n\n defect at the center whereas the \n\\begin_inset Formula $K=0.01$\n\\end_inset\n\n case has two \n\\begin_inset Formula $+1/2$\n\\end_inset\n\n defects.\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:qtensor\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis creates beautiful plots of the nematic, as seen in the example Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:qtensor\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\n\n\\end_inset\n\n. Like the tactoid example, we can do adaptive mesh refinement based on the elastic energy density as well. The full code, along with the optional adaptive refinement can be found under \n\\family typewriter\nexamples/qtensor/qtensor.morpho\n\\family default\n. \n\\end_layout\n\n\\end_body\n\\end_document\n"
  },
  {
    "path": "examples/qtensor/src.tex",
    "content": "\\documentclass{article}\n\\usepackage[utf8]{inputenc}\n\\usepackage{physics}\n\\usepackage{xcolor}\n\\usepackage{graphicx}\n\\definecolor{codegray}{rgb}{0.9,0.9,0.9}\n\\usepackage{listings}\n\\newcommand{\\morpho}{\\textit{morpho}}\n\\newcommand{\\Morpho}{\\textit{Morpho}}\n\\title{Q-tensor model of nematics}\n\\author{Chaitanya Joshi}\n\\date{October 2021}\n\n\\begin{document}\n\\lstset{  \nlanguage=Java,  \ntabsize=4,  \nbasicstyle=\\ttfamily,  \nbackgroundcolor=\\color{codegray},  \nshowstringspaces=false  \n}\n\\maketitle\n\n\\section{Introduction}\nIn 2D, for a uniaxial nematic, we can define a Q-tensor:\n\\[\nQ_{ij} = S (n_i n_j - 1/2 \\delta_{ij})\n\\]\nHere, the $-1/2 \\delta_{ij}$ is added for convenience, to make the matrix traceless: \n\\[\\text{Tr}(\\mathbf{Q}) = Q_{ii} = S(n_i n_i - 1/2 \\delta_{ii}) = S(1 - 1/2(2)) = 0\n\\]\nNow, the Q-tensor is also symmetric by definition:\n\\[\nQ_{ij} = Q_{ji}\n\\]\nDue to these two reasons we can write the Q-tensor as a function of only $Q_{xx}$ and $Q_{xy}$:\n\\[\n\\mathbf{Q} = \\begin{bmatrix}Q_{xx} & Q_{xy} \\\\ Q_{xy} & -Q_{xx} \\end{bmatrix}\n\\]\n\\subsection{Simple Passive Nematic Model}\nThe Landau-de Gennes equilibrium free energy for a nematic liquid crystal can be written in terms of the Q-tensor:\n\\begin{align*}\nF_{LDG} = &\\int_\\Omega d^2{\\bf x} \\ \\left(\\frac{a_2}{2} \\text{Tr}(\\mathbf{Q}^2) + \\frac{a_4}{4} (\\text{Tr} \\mathbf{Q}^2)^2 + \\frac{K}{2}(\\nabla\\mathbf{Q})^2 \\right) \\\\\n&+ \\oint_{\\partial\\Omega} d{\\bf x} \\frac{1}{2}E_A \\text{Tr}[(\\mathbf{Q}-\\mathbf{W})^2]\n\\end{align*}\nwhere $a_2 = (\\rho-1)$ and $a_4 = (\\rho+1)/\\rho^2$ \nset the isotropic to nematic transition with $\\rho$ being the non-dimensional density. The system is in the isotropic state for $\\rho<1$ and in the nematic phase when $\\rho>1$. In the nematic phase, $\\ell_n = \\sqrt{K/a_2}$ sets the nematic coherence length. Now,\n\\[\n\\mathbf{Q}^2 = \\begin{bmatrix}Q_{xx} & Q_{xy} \\\\ Q_{xy} & -Q_{xx} \\end{bmatrix} \\begin{bmatrix}Q_{xx} & Q_{xy} \\\\ Q_{xy} & -Q_{xx} \\end{bmatrix} = (Q_{xx}^2+Q_{xy}^2) \\begin{bmatrix} 1 & 0 \\\\ 0 & 1 \\end{bmatrix}\n\\]\nHence, \n\\[\n\\text{Tr}(\\mathbf{Q}^2) = 2(Q_{xx}^2+Q_{xy}^2)\n\\]\nSimilarly,\n\\[\n(\\nabla \\mathbf{Q})^2 = \\partial_i Q_{kj}\\partial_i Q_{kj} = 2 \\{ (\\partial_x Q_{xx})^2+(\\partial_xQ_{xy})^2 + (\\partial_y Q_{xx})^2+(\\partial_y Q_{xy})^2 \\}\n\\]\nNow, the second term is a boundary integral, with $E_A$ being the anchoring strength. $\\mathbf{W}$ is the tensor corresponding to the boundary condition. For instance, for parallel anchoring, \n\\[\nW_{ij} = (t_i t_j - 1/2 \\delta_{ij})\n\\]\nwhere $t_i$ is a component of the tangent vector at the boundary.\n$\\mathbf{W}$ is also a symmetric traceless tensor with two independent components $W_{xx}$ and $W_{xy}$.\nThe boundary term becomes:\n\\[ \\text{Tr}[(\\mathbf{Q}-\\mathbf{W})^2] = 2\\{ Q_{xx}^2 + Q_{xy}^2 - 2(Q_{xx}W_{xx} + Q_{xy}W_{xy}) + W_{xx}^2 + W_{xy}^2 \\} \n\\]\n\\section{Vector formulation}\nWe can formulate all these expressions in terms of vector quantities:\n\\[\\vec{q} \\equiv \\{ Q_{xx}, Q_{xy}\\}\\]\n\\[\\vec{w} \\equiv \\{ w_{xx}, w_{xy}\\}\\]\nThus,\n\\[\n\\text{Tr}(\\mathbf{Q}^2) = 2 ||\\vec{q}||^2\n\\]\n\n\\[\n(\\nabla \\mathbf{Q})^2 = 2 ||\\nabla \\vec{q}||^2\n\\]\n\n\\[\n\\text{Tr}[(\\mathbf{Q}-\\mathbf{W})^2] = 2 ||\\vec{q}-\\vec{w}||^2\n\\]\nWith these, we want to minimize the area-integral of \n\\[\nF = \\int_\\Omega d^2{\\bf x} \\ \\left( a_2 ||\\vec{q}||^2 + a_4 ||\\vec{q}||^4 + K||\\nabla\\vec{q}||^2 \\right)\n\\]\ntogether with the line-integral energy\n\\[\n\\oint_{\\partial\\Omega} d{\\bf x} \\ E_A ||\\vec{q}-\\vec{w}||^2\n\\]\n\n\\section{\\Morpho \\ implementation}\nThis free energy is readily set up in \\morpho. For this problem, we will consider a 2D disk geometry with unit radius. We use $\\rho=1.3$, so that we are deep in the nematic regime. We fix $E_{\\text{A}}=3$, which sets strong anchoring at the boundary. With this strong tangential anchoring, we get a topological charge of $+1$ at the boundary, and this acts as a constraint. When the nematic coherence length is comparable to the disk diameter ($\\ell_n \\sim R$), the $+1$ charge penetrates throughout the disk, whereas if ($\\ell_n \\ll R$), then a formation with 2 $+1/2$ defects is more stable. To test this, we use two different values of $K$:, 0.01 and 1.0.\n\nWe first define all our parameters and import $\\texttt{disk.mesh}$ from the tactoid example:\n\\begin{lstlisting}\nvar rho = 1.3 // Deep in the nematic phase\nvar EA = 3 // Anchoring strength\nvar K = 0.01 // Bending modulus\n\nvar a2 = (1-rho)\nvar a4 = (1+rho)/rho^2\n\nvar m = Mesh(\"disk.mesh\")\nvar m = refinemesh(m) // Refining for a better result\nvar bnd = Selection(m, boundary=true)\nbnd.addgrade(0) // add point elements\n\n\\end{lstlisting}\nWe define the Q-tensor in its vector form as discussed above, initializing it to small random values:\n\\begin{lstlisting}\nvar q_tensor = Field(m, fn(x,y,z)\nMatrix([0.01*random(1), 0.01*random(1)]))\n\\end{lstlisting}\nNote that this incidentally makes the director parallel to a 45 degree line. \nWe now define the bulk energy, the anchoring energy and the distortion free energy as follows:\n\\begin{lstlisting}\n// Define bulk free energy\nfn landau(x, q) {\n  var qt = q.norm()\n  var qt2=qt*qt\n  return a2*qt2 + a4*qt2*qt2\n}\n// Define anchoring energy at the boundary\nfn anchoring(x, q) {\n  var t = tangent()\n  var wxx = t[0]*t[0]-0.5\n  var wxy = t[0]*t[1]\n  return (q[0]-wxx)^2+(q[1]-wxy)^2\n}\n\nvar bulk = AreaIntegral(landau, q_tensor)\nvar anchor = LineIntegral(anchoring, q_tensor)\nvar elastic = GradSq(q_tensor)\n\\end{lstlisting}\nEquipped with the energies, we define the \\texttt{OptimizationProblem}:\n\\begin{lstlisting}\nvar problem = OptimizationProblem(m)\nproblem.addenergy(bulk)\nproblem.addenergy(elastic, prefactor = K)\nproblem.addenergy(anchor, selection=bnd, prefactor=EA)\n\\end{lstlisting}\nTo minimize the energy with respect to the field, we define the \\texttt{FieldOptimizer} and perform a \\texttt{linesearch}:\n\\begin{lstlisting}\nvar opt = FieldOptimizer(problem, q_tensor)\nopt.linesearch(500)\n\\end{lstlisting}\n\\section{Visualizing the result}\nFor visualizing the final configuration, we use the same piece of code we used for the tactoid example, and define some additional helper functions to extract the director and the order from the Q-tensor.\n\\begin{lstlisting}\nfn sign(x) {\n  if (x<0.0) return -1.0\n  else if (x>0.0) return 1.0\n  return 0.0\n}\n\\end{lstlisting}\n\\begin{lstlisting}\nfn qtodirector(q) {\n  var S = 2*q.norm()\n  var Q = q/S\n  var nx = sqrt(Q[0]+0.5)\n  var ny = abs(Q[1]/nx)\n  nx*=sign(Q[1])\n  return Matrix([nx,ny,0])\n}\n\\end{lstlisting}\n\\begin{lstlisting}\nfn qtoorder(q) {\n  var S = 2*q.norm()\n  return S\n}\n\\end{lstlisting}\n\\begin{lstlisting}\n// Convert the q-tensor to the director and order\nvar nn = Field(m, Matrix([1,0,0]))\nfor (i in 0...m.count()) nn[i]=qtodirector(q_tensor[i])\nvar S = Field(m, 0)\nfor (i in 0...m.count()) S[i]=qtoorder(q_tensor[i])\n\\end{lstlisting}\n\\begin{lstlisting}\n// Function to visualize a director field\nfn visualize(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var nv = v.dimensions()[1]\n  var g = Graphics()\n  for (i in 0...nv) {\n    var x = v.column(i)\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl,\n    aspectratio=0.3))\n  }\n  return g\n}\n\n// Visualize the result\n// var g=plotmesh(m, grade=1)\nvar splot = plotfield(S, style=\"interpolate\")\nvar gnn=visualize(m, nn, 0.05)\nvar gdisp = splot+gnn\nShow(gdisp)\n\\end{lstlisting}\nWe can go further and use the \\texttt{povray} module to render a \\texttt{.png} output:\n\\begin{lstlisting}\nvar pov = POVRaytracer(gdisp)\npov.viewangle=35\npov.render(\"Qtensor_K_${K}.pov\")\n\\end{lstlisting}\n\\begin{figure}\n    \\centering\n    \\includegraphics[width=0.45\\linewidth]{Qtensor_K_1.png}\n    \\includegraphics[width=0.45\\linewidth]{Qtensor_K_0.01.png}\n    \\caption{Final configuration of the director and order for (left) $K=1$ and (right) $K=0.01$. The cylinders indicate the nematic director whereas the color indicates the scalar order parameter. Note how the $K=1$ case has a single $+1$ defect at the center whereas the $K=0.01$ case has two $+1/2$ defects.}\n    \\label{fig:qtensor}\n\\end{figure}\nThis creates beautiful plots of the nematic, as seen in the example Figure \\ref{fig:qtensor}. \nLike the tactoid example, we can do adaptive mesh refinement based on the elastic energy density as well. The full code, along with the optional adaptive refinement can be found under \\texttt{examples/qtensor/qtensor.morpho}.\n\\end{document}\n"
  },
  {
    "path": "examples/tactoid/disk.mesh",
    "content": "vertices\n\n1 -1. 0. 0\n2 -0.951057 -0.309017 0\n3 -0.951057 0.309017 0\n4 -0.809017 -0.587785 0\n5 -0.809017 0.587785 0\n6 -0.587785 -0.809017 0\n7 -0.587785 0.809017 0\n8 -0.5 -0.5 0\n9 -0.5 0. 0\n10 -0.5 0.5 0\n11 -0.309017 -0.951057 0\n12 -0.309017 0.951057 0\n13 0. -1. 0\n14 0. -0.5 0\n15 0. 0. 0\n16 0. 0.5 0\n17 0. 1. 0\n18 0.309017 -0.951057 0\n19 0.309017 0.951057 0\n20 0.5 -0.5 0\n21 0.5 0. 0\n22 0.5 0.5 0\n23 0.587785 -0.809017 0\n24 0.587785 0.809017 0\n25 0.809017 -0.587785 0\n26 0.809017 0.587785 0\n27 0.951057 -0.309017 0\n28 0.951057 0.309017 0\n29 1. 0. 0\n\nedges\n\n1 8 2\n2 2 4\n3 4 8\n4 4 6\n5 6 8\n6 11 13\n7 13 14\n8 14 11\n9 8 9\n10 9 2\n11 14 8\n12 8 11\n13 13 18\n14 18 14\n15 6 11\n16 14 9\n17 3 10\n18 10 5\n19 5 3\n20 9 3\n21 3 1\n22 1 9\n23 10 7\n24 7 5\n25 10 9\n26 9 15\n27 15 10\n28 12 7\n29 10 12\n30 10 16\n31 16 12\n32 1 2\n33 14 15\n34 14 20\n35 20 15\n36 18 20\n37 18 23\n38 23 20\n39 20 21\n40 21 15\n41 27 20\n42 20 25\n43 25 27\n44 21 27\n45 27 29\n46 29 21\n47 23 25\n48 21 22\n49 22 15\n50 16 22\n51 22 19\n52 19 16\n53 16 15\n54 19 17\n55 17 16\n56 22 24\n57 24 19\n58 17 12\n59 26 22\n60 22 28\n61 28 26\n62 26 24\n63 21 28\n64 29 28\n\nfaces\n\n1 8 2 4\n2 8 4 6\n3 11 13 14\n4 2 8 9\n5 14 8 11\n6 13 18 14\n7 11 8 6\n8 9 8 14\n9 3 10 5\n10 9 3 1\n11 10 7 5\n12 10 9 15\n13 12 7 10\n14 10 16 12\n15 10 3 9\n16 1 2 9\n17 15 9 14\n18 14 20 15\n19 20 14 18\n20 20 18 23\n21 15 20 21\n22 27 20 25\n23 21 27 29\n24 21 20 27\n25 25 20 23\n26 15 21 22\n27 16 22 19\n28 16 15 22\n29 19 17 16\n30 22 24 19\n31 16 17 12\n32 26 22 28\n33 24 22 26\n34 22 21 28\n35 29 28 21\n36 15 16 10\n"
  },
  {
    "path": "examples/tactoid/tactoid.morpho",
    "content": "import meshtools\nimport optimize\nimport plot\nimport povray\n\n// Create mesh and director field\nvar m = Mesh(\"disk.mesh\")\nvar nn = Field(m, Matrix([1,0,0]))\n\n// Create functionals\nvar lf=Nematic(nn, kbend=1)\nvar ln=NormSq(nn)\nvar la=LineIntegral(fn (x, n) n.inner(tangent())^2, nn)\nvar lt=Length()\nvar laa=Area()\nvar leq=EquiElement()\n\n// Initial boundary selection\nvar bnd=Selection(m, boundary=true)\nbnd.addgrade(0)\n\n// Material parameters\nvar sigma=5.0*0.04 // Surface tension\nvar W=3.0  // Anchoring\n\nvar itermax = 4\n\n// Set up the optimization problem\nvar problem = OptimizationProblem(m)\nproblem.addenergy(lf)\nproblem.addenergy(la, selection=bnd, prefactor=-W/2)\nproblem.addenergy(lt, selection=bnd, prefactor=sigma+W)\nproblem.addconstraint(laa)\nproblem.addlocalconstraint(ln, field=nn, target=1)\n\n// Set up a regularization problem\nvar reg = OptimizationProblem(m)\nreg.addenergy(leq)\n\n// Create shape and field optimizers\nvar sopt = ShapeOptimizer(problem, m)\nvar fopt = FieldOptimizer(problem, nn)\nvar ropt = ShapeOptimizer(reg, m)\nropt.fix(bnd)\n\n// Set up initial stepsize\nsopt.stepsize=0.05\nsopt.steplimit=0.1\n\nvar mr, refmap // Refinement\n\n// A function we'll use to check each contribution to the energy\nfn checkenergy(problem, opt) {\n  print \"--Contributions to energy\"\n  for (en in problem.energies) {\n    print en.functional.clss()\n    print opt.total(en)\n  }\n}\n\n// Main optimization loop\nfor (iter in 1..itermax) {\n  for (i in 1..10) {\n      if (iter<itermax) {\n        print \"-Regularize\"\n        leq.weight=lf.integrand(nn)\n        ropt.stepsize=0.0001/iter\n        ropt.steplimit=0.0001/iter\n        ropt.linesearch(2)\n        equiangulate(m)\n      }\n\n      print \"-Field\"\n      fopt.conjugategradient(200)\n      print \"-Shape\"\n      sopt.conjugategradient(50)\n  }\n\n  // Check each contribution to the energy\n  checkenergy(problem, sopt)\n\n  // Refine if necessary\n  if (iter<itermax) {\n    // Select elements that have a large contribution to the energy\n    var en = lf.integrand(nn)\n    var mean = en.sum()/en.count()\n    var srefine = Selection(m)\n    for (id in 0...en.count()) if (en[0,id]>1.5*mean) srefine[2,id]=true\n\n    // Create a mesh refiner\n    mr=MeshRefiner([m, nn, bnd])\n    if (srefine.count(2)>0) {\n       refmap = mr.refine(selection=srefine)\n    } else {\n       refmap = mr.refine()\n    }\n\n    // Now refinement is done update the problems and optimizers\n    for (el in [problem, reg, sopt, fopt, ropt]) el.update(refmap)\n\n    // Use the new mesh and field\n    m = refmap[m]\n    nn = refmap[nn]\n    bnd = refmap[bnd]\n    // Must ensure boundary remains correctly fixed\n    ropt.fixed=[]\n    ropt.fix(Selection(m, boundary=true))\n\n    // Equiangulate\n    equiangulate(m)\n  }\n\n  // Reduce stepsize\n  sopt.stepsize=0.05/iter\n  sopt.steplimit=0.1/iter\n}\n\n// Function to visualize a director field\n// m - the mesh\n// nn - the director Field to visualize\n// dl - scale the director\nfn visualize(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var nv = m.count()\n  var g = Graphics()\n  for (i in 0...nv) {\n    var x = v.column(i)\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=0.3))\n  }\n  return g\n}\n\n// Visualize the result\nvar g=plotmesh(m, grade=1)\nvar gnn=visualize(m, nn, 0.2/itermax)\nvar gdisp = g+gnn\nShow(gdisp)\n\n// Visualize the elastic energy density\nvar fe = lf.integrand(nn)\nvar ff = Field(m, grade=2)\nfor (i in 0...m.count(2)) {\n  ff[2,i]=fe[0,i]\n}\n\nvar sb = ScaleBar(nticks=3,          // Maximum number of ticks\n                  length=1,          // Length of scalebar\n                  posn=[0,-1,0],   // Position of scalebar  \n                  dirn=[1,0,0],      // Direction in which scalebar is to be drawn\n                  tickdirn=[0,-1,0], // Direction in which to draw ticks\n                  textdirn=[1,0,0],  // Direction to draw text\n                  textvertical=[0,1,0], // Vertical direction for text \n                  textcolor=White,   // Text color\n                  fontsize=6 )      // Font size \n\n\nShow(plotfield(ff, colormap=GrayMap(), scalebar=sb))\n\ngdisp.background = White\nvar pov = POVRaytracer(gdisp)\npov.viewpoint = Matrix([0,0,10])\npov.background = White\npov.light=[Matrix([10,10,10]), Matrix([-10,-10,10])]\npov.render(\"out.pov\")\n"
  },
  {
    "path": "examples/tactoid/tactoid2dmesh.morpho",
    "content": "import meshtools\nimport optimize\nimport plot\nimport povray\nimport meshgen\n\n// Create mesh and director field\nvar dom = fn (x) -(x[0]^2+x[1]^2-1)\nvar mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2])\nvar m = mg.build()\n\nvar nn = Field(m, Matrix([1,0,0]))\n\n// Create functionals\nvar lf=Nematic(nn, kbend=1)\nvar ln=NormSq(nn)\n\nfn integrand(x, n) {\n  var t = tangent()\n  return (n[0]*t[0]+n[1]*t[1])^2\n}\n\nvar la=LineIntegral(integrand, nn)\nvar lt=Length()\nvar laa=Area()\nvar leq=EquiElement()\n\n// Initial boundary selection\nvar bnd=Selection(m, boundary=true)\nbnd.addgrade(0)\n\n// Material parameters\nvar sigma=5.0*0.04 // Surface tension\nvar W=3.0  // Anchoring\n\nvar itermax = 4\n\n// Set up the optimization problem\nvar problem = OptimizationProblem(m)\nproblem.addenergy(lf)\nproblem.addenergy(la, selection=bnd, prefactor=-W/2)\nproblem.addenergy(lt, selection=bnd, prefactor=sigma+W)\nproblem.addconstraint(laa)\nproblem.addlocalconstraint(ln, field=nn, target=1)\n\n// Set up a regularization problem\nvar reg = OptimizationProblem(m)\nreg.addenergy(leq)\n\n// Create shape and field optimizers\nvar sopt = ShapeOptimizer(problem, m)\nvar fopt = FieldOptimizer(problem, nn)\nvar ropt = ShapeOptimizer(reg, m)\nropt.fix(bnd)\n\n// Set up initial stepsize\nsopt.stepsize=0.05\nsopt.steplimit=0.1\n\nvar mr, refmap // Refinement\n\n// A function we'll use to check each contribution to the energy\nfn checkenergy(problem, opt) {\n  print \"--Contributions to energy\"\n  for (en in problem.energies) {\n    print en.functional.clss()\n    print opt.total(en)\n  }\n}\n\n// Main optimization loop\nfor (iter in 1..itermax) {\n  for (i in 1..10) {\n      if (iter<itermax) {\n        print \"-Regularize\"\n        leq.weight=lf.integrand(nn)\n        ropt.stepsize=0.0001/iter\n        ropt.steplimit=0.0001/iter\n        ropt.linesearch(2)\n        equiangulate(m)\n      }\n\n      print \"-Field\"\n      fopt.conjugategradient(200)\n      print \"-Shape\"\n      sopt.conjugategradient(50)\n  }\n\n  // Check each contribution to the energy\n  checkenergy(problem, sopt)\n\n  // Refine if necessary\n  if (iter<itermax) {\n    // Select elements that have a large contribution to the energy\n    var en = lf.integrand(nn)\n    var mean = en.sum()/en.count()\n    var srefine = Selection(m)\n    for (id in 0...en.count()) if (en[0,id]>1.5*mean) srefine[2,id]=true\n\n    // Create a mesh refiner\n    mr=MeshRefiner([m, nn, bnd])\n    if (srefine.count(2)>0) {\n       refmap = mr.refine(selection=srefine)\n    } else {\n       refmap = mr.refine()\n    }\n\n    // Now refinement is done update the problems and optimizers\n    for (el in [problem, reg, sopt, fopt, ropt]) el.update(refmap)\n\n    // Use the new mesh and field\n    m = refmap[m]\n    nn = refmap[nn]\n    bnd = refmap[bnd]\n    // Must ensure boundary remains correctly fixed\n    ropt.fixed=[]\n    ropt.fix(Selection(m, boundary=true))\n\n    // Equiangulate\n    equiangulate(m)\n  }\n\n  // Reduce stepsize\n  sopt.stepsize=0.05/iter\n  sopt.steplimit=0.1/iter\n}\n\n// Function to visualize a director field\n// m - the mesh\n// nn - the director Field to visualize\n// dl - scale the director\nfn visualize(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var nv = m.count()\n  var g = Graphics()\n  for (i in 0...nv) {\n    var x = v.column(i)\n    var y = Matrix([x[0], x[1], 0])\n    g.display(Cylinder(y-nn[i]*dl, y+nn[i]*dl, aspectratio=0.3))\n  }\n  return g\n}\n\n//System.exit() \n\n// Visualize the result\nvar g=plotmesh(m, grade=1)\nvar gnn=visualize(m, nn, 0.2/itermax)\nvar gdisp = g+gnn\nShow(gdisp)\n\n// Visualize the elastic energy density\nvar fe = lf.integrand(nn)\nvar ff = Field(m, grade=2)\nfor (i in 0...m.count(2)) {\n  ff[2,i]=fe[0,i]\n}\n\nvar sb = ScaleBar(nticks=3,          // Maximum number of ticks\n                  length=1,          // Length of scalebar\n                  posn=[0,-1,0],   // Position of scalebar  \n                  dirn=[1,0,0],      // Direction in which scalebar is to be drawn\n                  tickdirn=[0,-1,0], // Direction in which to draw ticks\n                  textdirn=[1,0,0],  // Direction to draw text\n                  textvertical=[0,1,0], // Vertical direction for text \n                  textcolor=White,   // Text color\n                  fontsize=6 )      // Font size \n\n\nShow(plotfield(ff, colormap=GrayMap(), scalebar=sb))\n\ngdisp.background = White\nvar pov = POVRaytracer(gdisp)\npov.viewpoint = Matrix([0,0,10])\npov.background = White\npov.light=[Matrix([10,10,10]), Matrix([-10,-10,10])]\npov.render(\"out.pov\")\n"
  },
  {
    "path": "examples/thomson/thomson.morpho",
    "content": "// Thomson problem of arranging charges on a sphere\n// to minimize the electrostatic energy\n// Showcases: MeshBuilder, PairwisePotential, ScalarPotential\nimport meshtools\nimport plot\nimport optimize\nimport functionals\n\nvar Np = 100 // Number of particles\n\n// Create the mesh, which consists of Np random points each representing\n// a charge on the unit sphere.\nvar build = MeshBuilder()\nfor (i in 1..Np) {\n  var x = Matrix([2*random()-1, 2*random()-1, 2*random()-1])\n  x/=x.norm() // Project onto unit sphere\n  build.addvertex(x)\n}\nvar mesh = build.build() // Tell the MeshBuilder to build the mesh\n\n// Specify the problem\nvar problem = OptimizationProblem(mesh)\n\n// The particle repel one another by a Coulomb potential.\n// We supply the potential and it's derivative wrt r as anonymous functions.\nvar lv = PairwisePotential(fn (r) 1/r, fn (r) -1/r^2)\nproblem.addenergy(lv)\n\n// Constrain the particles on the unit sphere via a level set constraint.\n// The level set function and its gradient are supplied as anonymous functions.\nvar lsph = ScalarPotential(fn (x,y,z) x^2+y^2+z^2-1, fn (x,y,z) Matrix([2*x, 2*y, 2*z]))\nproblem.addlocalconstraint(lsph)\n\n// Set up the optimizer to optimize this problem wrt the mesh vertex positions.\nvar opt = ShapeOptimizer(problem, mesh)\n\n// Choose a stepsize\nopt.stepsize=0.01/sqrt(Np)\n\n// Do a few iterations at fixed stepsize to move away from the initally random\n// condition. [This helps condition the problem]\nopt.relax(5)\n// Now perform gradient descent\nopt.conjugategradient(1000) // Perform up to 1000 iterations of direct gradient descent\n\n// Visualize the results\nvar g = Graphics()\nfor (i in 0...mesh.count()) {\n  // Display each particle as a sphere\n  g.display(Sphere(mesh.vertexposition(i),1/sqrt(Np)))\n}\nShow(g) // Open up the viewer application\n"
  },
  {
    "path": "examples/tutorial/disk.mesh",
    "content": "vertices\n\n1 -1. 0. 0\n2 -0.951057 -0.309017 0\n3 -0.951057 0.309017 0\n4 -0.809017 -0.587785 0\n5 -0.809017 0.587785 0\n6 -0.587785 -0.809017 0\n7 -0.587785 0.809017 0\n8 -0.5 -0.5 0\n9 -0.5 0. 0\n10 -0.5 0.5 0\n11 -0.309017 -0.951057 0\n12 -0.309017 0.951057 0\n13 0. -1. 0\n14 0. -0.5 0\n15 0. 0. 0\n16 0. 0.5 0\n17 0. 1. 0\n18 0.309017 -0.951057 0\n19 0.309017 0.951057 0\n20 0.5 -0.5 0\n21 0.5 0. 0\n22 0.5 0.5 0\n23 0.587785 -0.809017 0\n24 0.587785 0.809017 0\n25 0.809017 -0.587785 0\n26 0.809017 0.587785 0\n27 0.951057 -0.309017 0\n28 0.951057 0.309017 0\n29 1. 0. 0\n\nedges\n\n1 8 2\n2 2 4\n3 4 8\n4 4 6\n5 6 8\n6 11 13\n7 13 14\n8 14 11\n9 8 9\n10 9 2\n11 14 8\n12 8 11\n13 13 18\n14 18 14\n15 6 11\n16 14 9\n17 3 10\n18 10 5\n19 5 3\n20 9 3\n21 3 1\n22 1 9\n23 10 7\n24 7 5\n25 10 9\n26 9 15\n27 15 10\n28 12 7\n29 10 12\n30 10 16\n31 16 12\n32 1 2\n33 14 15\n34 14 20\n35 20 15\n36 18 20\n37 18 23\n38 23 20\n39 20 21\n40 21 15\n41 27 20\n42 20 25\n43 25 27\n44 21 27\n45 27 29\n46 29 21\n47 23 25\n48 21 22\n49 22 15\n50 16 22\n51 22 19\n52 19 16\n53 16 15\n54 19 17\n55 17 16\n56 22 24\n57 24 19\n58 17 12\n59 26 22\n60 22 28\n61 28 26\n62 26 24\n63 21 28\n64 29 28\n\nfaces\n\n1 8 2 4\n2 8 4 6\n3 11 13 14\n4 2 8 9\n5 14 8 11\n6 13 18 14\n7 11 8 6\n8 9 8 14\n9 3 10 5\n10 9 3 1\n11 10 7 5\n12 10 9 15\n13 12 7 10\n14 10 16 12\n15 10 3 9\n16 1 2 9\n17 15 9 14\n18 14 20 15\n19 20 14 18\n20 20 18 23\n21 15 20 21\n22 27 20 25\n23 21 27 29\n24 21 20 27\n25 25 20 23\n26 15 21 22\n27 16 22 19\n28 16 15 22\n29 19 17 16\n30 22 24 19\n31 16 17 12\n32 26 22 28\n33 24 22 26\n34 22 21 28\n35 29 28 21\n36 15 16 10\n"
  },
  {
    "path": "examples/tutorial/tutorial.morpho",
    "content": "// Morpho tutorial example\nimport meshtools\nimport optimize\nimport plot\n\nvar m = Mesh(\"disk.mesh\")\n\n// Initial boundary selection\nvar bnd=Selection(m, boundary=true)\nbnd.addgrade(0)\n//Show(plotselection(m, bnd, grade=1))\n\nvar nn = Field(m, Matrix([1,0,0]))\n\n// Define functionals\nvar lf=Nematic(nn)\nvar lt=Length()\nvar la=LineIntegral(fn (x, n) n.inner(tangent())^2, nn)\nvar ln=NormSq(nn)\nvar laa=Area()\n\n// Set up the optimization problem\nvar W = 1\nvar sigma = 1\n\nvar problem = OptimizationProblem(m)\nproblem.addenergy(lf)\nproblem.addenergy(la, selection=bnd, prefactor=-W/2)\nproblem.addenergy(lt, selection=bnd, prefactor=sigma)\nproblem.addconstraint(laa)\nproblem.addlocalconstraint(ln, field=nn, target=1)\n\n// Create shape and field optimizers\nvar sopt = ShapeOptimizer(problem, m)\nvar fopt = FieldOptimizer(problem, nn)\n\n// Optimization loop\nfor (i in 1..100) {\n  fopt.linesearch(20)\n  sopt.linesearch(20)\n}\n\n// Visualize results\nvar g=plotmesh(m, grade=1)\n\n// Function to visualize a director field\n// m - the mesh\n// nn - the director Field to visualize\n// dl - scale the director\nfn visualize(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var nv = m.count() // Number of vertices\n  var g = Graphics() // Create a graphics object\n  for (i in 0...nv) {\n    var x = v.column(i) // Get the ith vertex\n\t// Draw a cylinder aligned with nn at this vertex\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=0.3))\n  }\n  return g\n}\n\nvar gnn=visualize(m, nn, 0.2)\n\nvar gdisp = g+gnn\nShow(gdisp)\n"
  },
  {
    "path": "examples/tutorial/tutorial2.morpho",
    "content": "// Morpho tutorial example\nimport meshtools\nimport optimize\nimport plot\n\nvar m = Mesh(\"disk.mesh\")\n\n// Initial boundary selection\nvar bnd=Selection(m, boundary=true)\nbnd.addgrade(0)\n//Show(plotselection(m, bnd, grade=1))\n\nvar nn = Field(m, Matrix([1,0,0]))\n\n// Define functionals\nvar lf=Nematic(nn)\nvar lt=Length()\nvar la=LineIntegral(fn (x, n) n.inner(tangent())^2, nn)\nvar ln=NormSq(nn)\nvar laa=Area()\n\n// Set up the optimization problem\nvar W = 1\nvar sigma = 1\n\nvar problem = OptimizationProblem(m)\nproblem.addenergy(lf)\nproblem.addenergy(la, selection=bnd, prefactor=-W/2)\nproblem.addenergy(lt, selection=bnd, prefactor=sigma)\nproblem.addconstraint(laa)\nproblem.addlocalconstraint(ln, field=nn, target=1)\n\n// Create shape and field optimizers\nvar sopt = ShapeOptimizer(problem, m)\nvar fopt = FieldOptimizer(problem, nn)\n\n// Optimization loop\nvar refmax = 3\nfor (refiter in 1..refmax) {\n  print \"===Refinement level ${refiter}===\"\n  for (i in 1..100) {\n    fopt.linesearch(20)\n    sopt.linesearch(20)\n  }\n\n  // Refinement\n  if (refiter==refmax) break\n  var mr=MeshRefiner([m, nn, bnd]) // Set the refiner up\n  var refmap=mr.refine() // Perform the refinement\n  for (el in [problem, sopt, fopt]) el.update(refmap) // Update the problem\n  m=refmap[m]; nn=refmap[nn]; bnd=refmap[bnd] // Update variables\n}\n\n// Visualize results\nvar g=plotmesh(m, grade=1)\n\n// Function to visualize a director field\n// m - the mesh\n// nn - the director Field to visualize\n// dl - scale the director\nfn visualize(m, nn, dl) {\n  var v = m.vertexmatrix()\n  var nv = m.count() // Number of vertices\n  var g = Graphics() // Create a graphics object\n  for (i in 0...nv) {\n    var x = v.column(i) // Get the ith vertex\n\t// Draw a cylinder aligned with nn at this vertex\n    g.display(Cylinder(x-nn[i]*dl, x+nn[i]*dl, aspectratio=0.3))\n  }\n  return g\n}\n\nvar gnn=visualize(m, nn, 0.2/refmax)\n\nvar gdisp = g+gnn\nShow(gdisp)\n"
  },
  {
    "path": "examples/wrap/wrap.morpho",
    "content": "/* Finds a minimal surface that connects two ellipsoids.\nThis models a fluid interface between two colloidal particles, for example */\n\nimport graphics\nimport meshtools\nimport plot\nimport optimize\n\n// Create a initial cube\nvar L = 2\n\nvar cube = [[-L, -L, -L], [-L, -L, L], [-L, L, -L],\n            [-L, L, L], [L, -L, -L], [L, -L, L],\n            [L, L, -L], [L, L, L]]\n\nvar faces = [[7, 3, 1, 5], [7, 5, 4, 6], [7, 6, 2, 3], [3, 2, 0, 1], [0, 2, 6,\n  4], [1, 0, 4, 5]]\n\nvar m=PolyhedronMesh(cube, faces)\nm=refinemesh(m)\n\n// Make a class to manufacture axis aligned ellipsoids.\n// To create one, call Ellipsoid(origin, principalradii)\nclass Ellipsoid {\n  init(x, r) {\n    self.origin = x\n    self.principalradii = r\n  }\n\n  // Returns a level set function for this Ellipsoid\n  levelset() {\n    fn phi (x,y,z) {\n      var x0 = self.origin, rr = self.principalradii\n      return ((x-x0[0])/rr[0])^2 + ((y-x0[1])/rr[1])^2 + ((z-x0[2])/rr[2])^2 - 1\n    }\n\n    return phi\n  }\n\n  // Returns the a function that returns the gradient\n  // of the level set function for this Ellipsoid\n  gradient() {\n    fn dphi (x,y,z) {\n      var x0 = self.origin, rr = self.principalradii\n      return Matrix([2*(x-x0[0])/rr[0]^2,\n                     2*(y-x0[1])/rr[1]^2,\n                     2*(z-x0[2])/rr[2]^2])\n    }\n\n    return dphi\n  }\n}\n\n// Now use this to manufacture some Ellipsoids\nvar ell1 = Ellipsoid([0,1/2,0],[1/2,1/2,1])\nvar ell2 = Ellipsoid([0,-1/2,0],[1,1/2,1/2])\n\n// We want to minimize the area\nvar la = Area()\n// Subject to level set constraints\nvar ls1 = ScalarPotential( ell1.levelset(), ell1.gradient() )\nvar ls2 = ScalarPotential( ell2.levelset(), ell2.gradient() )\nvar leq = EquiElement()\n\nvar problem = OptimizationProblem(m)\nproblem.addenergy(la)\nproblem.addlocalconstraint(ls1, onesided=true)\nproblem.addlocalconstraint(ls2, onesided=true)\n\nvar reg = OptimizationProblem(m)\nreg.addenergy(leq)\nreg.addlocalconstraint(ls1, onesided=true)\nreg.addlocalconstraint(ls2, onesided=true)\n\nvar sopt = ShapeOptimizer(problem, m)\nvar ropt = ShapeOptimizer(reg, m)\n\nsopt.stepsize=0.025\nsopt.steplimit=0.1\nsopt.ctol = 1e-9\nsopt.maxconstraintsteps = 100\n\nropt.stepsize=0.01\nropt.steplimit=0.2\n\nfor (refine in 1..3) {\n  for (i in 1..100) {\n    sopt.relax(5)\n    ropt.conjugategradient(5)\n    equiangulate(m)\n  }\n\n  var mr=MeshRefiner([m])\n  var refmap = mr.refine()\n  for (el in [problem, reg, sopt, ropt]) el.update(refmap)\n  m = refmap[m]\n}\n\nShow(plotmesh(m, grade=2))\n"
  },
  {
    "path": "help/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "help/array.md",
    "content": "[comment]: # (Array class help)\n[version]: # (0.5)\n\n# Array\n[tagarray]: # (Array)\n\nArrays are collection objects that can have any number of indices. Their size is set when they are created:\n\n    var a[5]\n    var b[2,2]\n    var c[nv,nv,nv]\n\nValues can be retrieved with appropriate indices:\n\n    print a[0,0]\n\nArrays can be indexed with slices:\n\n\tprint a[[0,2,4],2]\n\tprint a[1,0..2]\n\nAny morpho value can be stored in an array element\n\n    a[0,0] = [1,2,3]\n\n[showsubtopics]: # (subtopics)\n\n## Dimensions\n[tagdimensions]: # (Dimensions)\n\nGet the dimensions of an Array object:\n\n    var a[2,2]\n    print a.dimensions() // expect: [ 2, 2 ]\n"
  },
  {
    "path": "help/builtin.md",
    "content": "[comment]: # (Builtin function help)\n[version]: # (0.5)\n\n# Builtin functions\n[tagbuiltin]: # (builtin)\n\nMorpho provides a number of built-in functions.\n\n[showsubtopics]: # (subtopics)\n\n## Random\n[tagrandom]: # (random)\n[tagrand]: # (rand)\n\nThe `random` function generates a random number from a uniform distribution on the interval [0,1].\n\n    print random() \n\nSee also `randomnormal` and `randomint`.\n\n## Randomnormal\n[tagrandomnormal]: # (randomnormal)\n\nThe `randomnormal` function generates a random number from a normal (gaussian) distribution with unit variance and zero offset.\n\n    print randomnormal() \n\nSee also `random` and `randomint`.\n\n## Randomint\n[tagrandomint]: # (randomint)\n\nThe `randomint` function generates a random integer with a specified maximum value.\n\n    print randomint(10) // Generates a random integer [0,10)\n\n## isnil\n[tagisnil]: # (isnil)\n\nReturns `true` if a value is `nil` or `false` otherwise.\n\n## isint\n[tagisint]: # (isint)\n\nReturns `true` if a value is an integer or `false` otherwise.\n\n## isfloat\n[tagisfloat]: # (isfloat)\n\nReturns `true` if a value is a floating point number or `false` otherwise.\n\n## isbool\n[tagisbool]: # (isbool)\n\nReturns `true` if a value is a boolean or `false` otherwise.\n\n## isobject\n[tagisobject]: # (isobject)\n\nReturns `true` if a value is an object or `false` otherwise.\n\n## isstring\n[tagisstring]: # (isstring)\n\nReturns `true` if a value is a string or `false` otherwise.\n\n## isclass\n[tagisclass]: # (isclass)\n\nReturns `true` if a value is a class or `false` otherwise.\n\n## isrange\n[tagisrange]: # (isrange)\n\nReturns `true` if a value is a range or `false` otherwise.\n\n## isdictionary\n[tagisdictionary]: # (isdictionary)\n\nReturns `true` if a value is a dictionary or `false` otherwise.\n\n## islist\n[tagislist]: # (islist)\n\nReturns `true` if a value is a list or `false` otherwise.\n\n## isarray\n[tagisarray]: # (isarray)\n\nReturns `true` if a value is an array or `false` otherwise.\n\n## ismatrix\n[tagismatrix]: # (ismatrix)\n\nReturns `true` if a value is a matrix or `false` otherwise.\n\n## issparse\n[tagissparse]: # (issparse)\n\nReturns `true` if a value is a sparse matrix or `false` otherwise.\n\n## isinf\n[tagisinf]: # (isinf)\n\nReturns `true` if a value is infinite or `false` otherwise.\n\n## isnan\n[tagisnan]: # (isnan)\n\nReturns `true` if a value is a Not a Number or `false` otherwise.\n\n## iscallable\n[tagiscallable]: # (iscallable)\n\nReturns `true` if a value is callable or `false` otherwise.\n\n## isfinite\n[tagisfinite]: # (isfinite)\n\nReturns `true` if a value is finite or `false` otherwise.\n\n    print isfinite(1) // expect: true \n    print isfinite(1/0) // expect: false \n\n## isnumber\n[tagisnumber]: # (isnumber)\n\nReturns `true` if a value is a real number, or `false` otherwise.\n\n    print isnumber(1) // expect: true \n    print isnumber(Object()) // expect: false\n\n## ismesh\n[tagismesh]: # (ismesh)\n\nReturns `true` if a value is a `Mesh`, or `false` otherwise.\n\n## isselection\n[tagisselection]: # (isselection)\n\nReturns `true` if a value is a `Selection`, or `false` otherwise.\n\n## isfield\n[tagisfield]: # (isfield)\n\nReturns `true` if a value is a `Field`, or `false` otherwise.\n\n## Apply\n[tagapply]: # (apply)\n\nApply calls a function with the arguments provided as a list:\n\n    apply(f, [0.5, 0.5]) // calls f(0.5, 0.5) \n    \nIt's often useful where a function or method and/or the number of parameters isn't known ahead of time. The first parameter to apply can be any callable object, including a method invocation or a closure. \n\nYou may also instead omit the list and use apply with multiple arguments: \n\n    apply(f, 0.5, 0.5) // calls f(0.5, 0.5)\n    \nThere is one edge case that occurs when you want to call a function that accepts a single list as a parameter. In this case, enclose the list in another list: \n\n    apply(f, [[1,2]]) // equivalent to f([1,2])\n\n## Abs\n[tagabs]: # (abs)\n\nReturns the absolute value of a number: \n\n    print abs(-10) // prints 10 \n\n## Sign\n[tagsign]: # (sign)\n\nGives the sign of a number: \n\n    print sign(4) // expect: 1\n    print sign(-10.0) // expect: -1\n    print sign(0) // expect: 0\n\n## Arctan\n[tagarctan]: # (arctan)\n\nReturns the arctangent of an input value that lies from `-Inf` to `Inf`. You can use one argument:\n\n    print arctan(0) // expect: 0\n\nor use two arguments to return the angle in the correct quadrant:\n\n    print arctan(x, y)\n\nNote the order `x`, `y` differs from some other languages.\n\n## Exp\n[tagexp]: # (exp)\n\nExponential function `e^x`. Inverse of `log`.\n\n    print exp(0) // expect: 1 \n    print exp(Pi*im) // expect: -1 + 0im\n\n## Log\n[taglog]: # (log)\n\nNatural logarithm function. Inverse of `exp`.\n\n    print log(1) // expect: 0 \n\n## Log10\n[taglog10]: # (log10)\n\nBase 10 logarithm function.\n\n    print log10(10) // expect: 1\n\n## Sin\n[tagsin]: # (sin)\n\nSine trigonometric function.\n\n    print sin(0) // expect: 0 \n\n## Sinh\n[tagsinh]: # (sinh)\n\nHyperbolic sine trigonometric function.\n\n    print sinh(0) // expect: 0 \n\n## Cos\n[tagcos]: # (cos)\n\nCosine trigonometric function.\n\n    print cos(0) // expect: 1\n\n## Cosh\n[tagcosh]: # (cosh)\n\nHyperbolic cosine trigonometric function.\n\n    print cosh(0) // expect: 1\n\n## Tan\n[tagtan]: # (tan)\n\nTangent trigonometric function.\n\n    print tan(0) // expect: 0 \n\n## Tanh\n[tagtanh]: # (tanh)\n\nHyperbolic tangent trigonometric function.\n\n    print tanh(0) // expect: 0 \n\n## Asin\n[tagasin]: # (asin)\n\nInverse sine trigonometric function. Returns a value on the interval    `[-Pi/2,Pi/2]`.\n\n    print asin(0) // expect: 0 \n\n## Acos\n[tagacos]: # (acos)\n\nInverse cosine trigonometric function. Returns a value on the interval  `[-Pi/2,Pi/2]`.\n\n    print acos(1) // expect: 0 \n\n## Sqrt\n[tagsqrt]: # (sqrt)\n\nSquare root function.\n\n    print sqrt(4) // expect: 2\n\n## Min\n[tagmin]: # (min)\n\nFinds the minimum value of its arguments. If any of the arguments are Objects and are enumerable, (e.g. a `List`), `min` will search inside them for a minimum value. Accepts any number of arguments. \n\n    print min(3,2,1) // expect: 1 \n    print min([3,2,1]) // expect: 1 \n    print min([3,2,1],[0,-1,2]) // expect: -2 \n\n## Max\n[tagmax]: # (max)\n\nFinds the maximum value of its arguments. If any of the arguments are Objects and are enumerable, (e.g. a `List`), `max` will search inside them for a maximum value. Accepts any number of arguments. \n\n    print min(3,2,1) // expect: 3 \n    print min([3,2,1]) // expect: 3\n    print min([3,2,1],[0,-1,2]) // expect: 3 \n\n## Bounds\n[tagbounds]: # (bounds)\n\nReturns both the results of `min` and `max` as a list, Providing a set of bounds for its arguments and any enumerable objects within them.\n\n    print bounds(1,2,3) // expect: [1,3]\n    print bounds([3,2,1],[0,-1,2]) // expect: [-1,3]\n"
  },
  {
    "path": "help/classes.md",
    "content": "[comment]: # (Morpho classes help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# Classes\n[tagclass]: # (class)\n[tagmethod]: # (method)\n\nClasses are defined using the `class` keyword followed by the name of the class.\nThe definition includes methods that the class responds to. The special `init` method\nis called whenever an object is created.\n\n    class Cake {\n        init(type) {\n            self.type = type\n        }\n\n        eat() {\n            print \"A delicious \"+self.type+\" cake\"\n        }\n    }\n\nObjects are created by calling the class as if it was a function:\n\n    var c = Cake(\"carrot\")\n\nNote that all objects in Morpho inherit from a base `Object` class, which provides a set of standard methods.\n\nSee also `Object`.\n\n[showsubtopics]: # (subtopics)\n\n## Methods\n[tagmethods]: # (methods)\n\nClasses in morpho can define *methods* to manipulate the objects defined by the class. Like functions, multiple implementations can be defined that accept different parameter types [see also topic: `signature`]:\n\n    class Foo {\n        a(List x) { print \"A list!\" } \n        a(String x) { print \"A string!\" } \n        a(Matrix x) { print \"A matrix!\" } \n    }\n\nHaving created an object with the class,\n\n    var x = Foo() \n\nthe correct implementation is selected at runtime:\n\n    x.a([1,2]) // expect: A list! \n    x.a(\"Hello\") // expect: A string! \n\n## Is\n[tagis]: # (is)\n\nThe `is` keyword is used to specify a class's superclass:\n\n    class A is B {\n\n    }\n\nAll methods defined by the superclass `B` are copied into the new class `A`, *before* any methods specified in the class definition. Hence, you can replace methods from the superclass simply by defining a method with the same name.\n\n## With\n[tagwith]: # (with)\n[tagmixin]: # (mixin)\n\nThe `with` keyword is used together with `is` to insert additional methods into a class definition *without* making them the superclass. These are often called `mixins`. These methods are inserted after the superclass's methods. Multiple classes can be specified after `with`; they are added in the order specified.\n\n    class A is B with C, D {\n\n    }\n\nHere `B` is the superclass of `A`, but methods defined by `C` and `D` are also available to `A`. If `B`, `C` and `D` define methods with the same name, those in `C` take precedence over any in `B` and those in `D` take precedence over `B` and `C`. \n\n## Self\n[tagself]: # (self)\n\nThe `self` keyword is used to access an object's properties and methods from within its definition.\n\n    class Vehicle {\n      init (type) { self.type = type }\n\n      drive () { print \"Driving my ${self.type}.\" }\n    }\n\n## Super\n[tagsuper]: # (super)\n\nThe keyword `super` allows you to access methods provided by an object's superclass rather than its own. This is particularly useful when the programmer wants a class to extend the functionality of a parent class, but needs to make sure the old behavior is still maintained.\n\nFor example, consider the following pair of classes:\n\n    class Lunch {\n        init(type) { self.type=type }\n    }\n\n    class Soup is Lunch {\n        init(type) {\n            print \"Delicious soup!\"\n            super.init(type)\n        }\n    }\n\nThe subclass Soup uses `super` to call the original initializer.\n\n# Objects\n[tagobject]: # (object)\n[tagobjects]: # (objects)\n[tagproperty]: # (property)\n[tagproperties]: # (properties)\n\nObjects in Morpho are created by calling a constructor function, which usually has the same name as the class of the object: \n\n    var a = Color(0.5,0.5,0.5) // 50% gray \n\nYou can store information in an object by assigning to its properties: \n\n    a.prop = \"Foo\" \n\nand you can read from them similarly:\n\n    print a.prop\n\nAn object's `class` determines the methods that can be used on the object. You call them using the . operator:\n\n    print a.clone() \n\nSee also `class`. \n\n[showsubtopics]: # (subtopics)\n\n## Has\n[taghas]: # (has)\n\nThe `has` method is used to test if an object has a particular property:\n\n    print a.has(\"foo\")\n\nIf you call `has` with no parameters, \n\n    print a.has()\n\nit returns a list of all property labels that an object has. \n\n## Respondsto\n[tagrespondsto]: # (respondsto)\n\nThe `respondsto` method is used to test if an object provides a particular method: \n\n    print a.respondsto(\"foo\")\n\nIf you call `respondsto` with no parameters, \n\n    print a.respondsto()\n\nit returns a list of all methods that an object has available. \n\n## Invoke\n[taginvoke]: # (invoke)\n\nThe `invoke` method is used to invoke a method from its label and a list of parameters: \n\n    print a.invoke(\"has\", \"foo\")\n\nis equivalent to:\n\n    print a.has(\"foo\")\n\n## Clss\n[tagclss]: # (clss)\n\nThe `clss` method is used to get the class to which an object belongs. \n\n    print a.clss() \n"
  },
  {
    "path": "help/color.md",
    "content": "[comment]: # (Color module help)\n[version]: # (0.5)\n\n# Color\n[tagcolor]: # (color)\n\nThe `color` module provides support for working with color. Colors are represented in morpho by `Color` objects. The module predefines some colors including `Red`, `Green`, `Blue`, `Black`, `White`.\n\nTo use the module, use import as usual:\n\n    import color\n\nCreate a Color object from an RGB pair:\n\n    var col = Color(0.5,0.5,0.5) // A 50% gray\n\nThe `color` module also provides `ColorMap`s, which are give a sequence of colors as a function of a parameter; these are useful for plotting the values of a `Field` for example.\n\n[showsubtopics]: # (subtopics)\n\n## RGB\n[tagrgb]: # (rgb)\n\nGets the rgb components of a `Color` or `ColorMap` object as a list. Takes a single argument in the range 0 to 1, although the result will only depend on this argument if the object is a `ColorMap`.\n\n    var col = Color(0.1,0.5,0.7)\n    print col.rgb(0)\n\n## Red\n[tagred]: # (red)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Green\n[taggreen]: # (green)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Blue\n[tagblue]: # (blue)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## White\n[tagwhite]: # (white)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Black\n[tagblack]: # (black)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Cyan\n[tagcyan]: # (cyan)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Magenta\n[tagmagenta]: # (magenta)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Yellow\n[tagyellow]: # (yellow)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Brown\n[tagbrown]: # (brown)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Orange\n[tagorange]: # (orange)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Pink\n[tagpink]: # (pink)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Purple\n[tagpurple]: # (purple)\nBuilt in `Color` object for use with the `graphics` and `plot` modules.\n\n## Colormap\n[tagcolormap]: # (colormap)\nThe `color` module provides `ColorMap`s which are subclasses of `Color` that map a single parameter in the range 0 to 1 onto a continuum of colors. `Color`s and `Colormap`s have the same interface.\n\nGet the red, green or blue components of a color or colormap:\n\n    var col = HueMap()\n    print col.red(0.5) // argument can be in range 0 to 1\n\nGet all three components as a list:\n\n    col.rgb(0)\n\nCreate a grayscale:\n\n    var c = Gray(0.2) // 20% gray\n\nAvailable ColorMaps: `GradientMap`,  `GrayMap`, `HueMap`, `ViridisMap`, `MagmaMap`, `InfernoMap` and `PlasmaMap`.\n\n## GradientMap\n[taggradientmap]: # (gradientmap)\n\n`GradientMap` is a `Colormap` that displays a white-green-purple sequence.\n\n## GrayMap\n[taggraymap]: # (graymap)\n\n`GrayMap` is a `Colormap` that displays grayscales.\n\n## HueMap\n[taghuemap]: # (huemap)\n\n`HueMap` is a `Colormap` that displays vivid colors. It is periodic on the interval 0 to 1.\n\n## ViridisMap\n[tagviridismap]: # (viridismap)\n\n`ViridisMap` is a `Colormap` that displays a purple-green-yellow sequence.\nIt is perceptually uniform and intended to be improve the accessibility of visualizations for viewers with color vision deficiency.\n\n## MagmaMap\n[tagmagmamap]: # (magmamap)\n\n`MagmaMap` is a `Colormap` that displays a black-red-yellow sequence.\nIt is perceptually uniform and intended to be improve the accessibility of visualizations for viewers with color vision deficiency.\n\n## InfernoMap\n[taginfernomap]: # (infernomap)\n\n`InfernoMap` is a `Colormap` that displays a black-red-yellow sequence.\nIt is perceptually uniform and intended to be improve the accessibility of visualizations for viewers with color vision deficiency.\n\n## PlasmaMap\n[tagplasmamap]: # (plasmamap)\n\n`InfernoMap` is a `Colormap` that displays a blue-red-yellow sequence. It is perceptually uniform and intended to be improve the accessibility of visualizations for viewers with color vision deficiency.\n"
  },
  {
    "path": "help/complex.md",
    "content": "[comment]: # (Complex help)\n[version]: # (0.5)\n\n# Complex\n[tagcomplex]: # (complex)\n[tagim]: # (im)\n\nMorpho provides complex numbers. The keyword `im` is used to denote the imaginary part of a complex number:\n\n    var a=1+5im \n    print a*a\n\nPrint values on the unit circle in the complex plane:\n\n    import constants \n    for (phi in 0..Pi:Pi/5) print exp(im*phi)\n\nGet the real and imaginary parts of a complex number:\n\n    print real(a) \n    print imag(a) \n\nor alternatively:\n\n    print a.real()\n    print a.imag() \n\n[showsubtopics]: # (subtopics)\n\n## Angle\n[tagangle]: # (angle)\n\nReturns the angle `phi` associated with the polar representation of a complex number `r*exp(im*phi)`:\n\n    print z.angle() \n\n## Conj\n[tagconjugate]: # (conjugate)\n[tagconj]: # (conj)\n\nReturns the complex conjugate of a number:\n\n    print z.conj() \n"
  },
  {
    "path": "help/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n# -- Project information -----------------------------------------------------\n\nproject = 'morpho'\ncopyright = '2020-2024, T J Atherton'\nauthor = 'T J Atherton'\n\n# The full version, including alpha/beta/rc tags\nrelease = '0.6.0'\n\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = ['myst_parser']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# Use recommonmark to parse md files\nfrom recommonmark.parser import CommonMarkParser\nsource_parsers = {'.md': CommonMarkParser}\nsource_suffix = ['.rst', '.md']\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'sphinx_rtd_theme'\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n\nlatex_engine = 'xelatex'\n"
  },
  {
    "path": "help/constants.md",
    "content": "[comment]: # (Constants module help)\n[version]: # (0.5)\n\n# Constants\n[tagconstants]: # (constants)\n\nThe constants module contains a number of useful mathematical constants. Import it like any other module:\n\n    import constants\n\nAvailable constants:\n\n* `E` the base of natural logarithms.\n* `Pi` ratio of the perimeter of a circle to its diameter.\n"
  },
  {
    "path": "help/controlflow.md",
    "content": "[comment]: # (Morpho control flow help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# Control Flow\n[tagcontrol]: # (control)\n\nControl flow statements are used to determine whether and how many times a selected piece of code is executed. These include:\n\n* `if` - Selectively execute a piece of code if a condition is met.\n* `else` - Execute a different block of code if the test in an `if` statement fails.\n* `for` - Repeatedly execute a section of code with a counter\n* `while` - Repeatedly execute a section of code while a condition is true.\n\n## If\n[tagif]: # (if)\n[tagelse]: # (else)\n\n`If` allows you to selectively execute a section of code depending on whether a condition is met. The simplest version looks like this:\n\n    if (x<1) print x\n\nwhere the body of the loop, `print x`, is only executed if x is less than 1. The body can be a code block to accommodate longer sections of code:\n\n    if (x<1) {\n        ... // do something\n    }\n\nIf you want to choose between two alternatives, use `else`:\n\n    if (a==b) {\n        // do something\n    } else {\n        // this code is executed only if the condition is false\n    }\n\nYou can even chain multiple tests together like this:\n\n    if (a==b) {\n        // option 1\n    } else if (a==c) {\n        // option 2\n    } else {\n        // something else\n    }\n\n## While\n[tagwhile]: # (while)\n\nWhile loops repeat a section of code while a condition is true. For example,\n\n    var k=1\n    while (k <= 4) { print k; k+=1 }\n           ^cond   ^body\n\nprints the numbers 1 to 4. The loop has two sections: `cond` is the condition to be executed and `body` is the section of code to be repeated.\n\nSimple loops like the above example, especially those that involve counting out a sequence of numbers, are more conveniently written using a `for` loop,\n\n    for (k in 1..4) print k\n\nWhere `while` loops can be very useful is where the state of an object is being changed in the loop, e.g.\n\n    var a = List(1,2,3,4)\n    while (a.count()>0) print a.pop()\n\nwhich prints 4,3,2,1.\n\n## Do\n[tagdo]: # (do)\n\nA `do`...`while` loop repeats code while a condition is true---similar to a `while` loop---but the test happens at the end:\n\n    var k=1\n    do {\n      print k;\n      k+=1\n    } while (k<5)\n\nwhich prints 1,2,3,4\n\nHence this type of loop executes at least one interation\n\n## For\n[tagfor]: # (for)\n[tagin]: # (in)\n\nFor loops allow you to repeatedly execute a section of code. They come in two versions: the simpler version looks like this,\n\n    for (var i in 1..5) print i\n\nwhich prints the numbers 1 to 5 in turn. The variable `i` is the *loop variable*, which takes on a different value each iteration. `1..5` is a range, which denotes a sequence of numbers. The *body* of the loop,  `print i`, is the code to be repeatedly executed.\n\nMorpho will implicitly insert a `var` before the loop variable if it's missing, so this works too:\n\n    for (i in 1..5) print i\n\nIf you want your loop variable to count in increments other than 1, you can specify a stepsize in the range:\n\n    for (i in 1..5:2) print i\n                   ^step\n\nRanges need not be integer:\n\n    for (i in 0.1..0.5:0.1) print i\n\nYou can also replace the range with other kinds of collection object to loop over their contents:\n\n    var a = Matrix([1,2,3,4])\n    for (x in a) print x\n\nMorpho iterates over the collection object using an integer *counter variable* that's normally hidden. If you want to know the current value of the counter (e.g. to get the index of an element as well as its value), you can use the following:\n\n    var a = [1, 2, 3]\n    for (x, i in a) print \"${i}: ${x}\"\n\nMorpho also provides a second form of `for` loop similar to that in C:\n\n    for (var i=0; i<5; i+=1) { print i }\n         ^start   ^test ^inc.  ^body\n\nwhich is executed as follows:\n  start: the variable `i` is declared and initially set to zero.\n  test: before each iteration, the test is evaluated. If the test is `false`, the loop terminates.\n  body: the body of the loop is executed.\n  inc: the variable `i` is increased by 1.\n\nYou can include any code that you like in each of the sections.\n\n## Break\n[tagbreak]: # (break)\n\n`Break` is used inside loops to finish the loop early. For example\n\n    for (i in 1..5) {\n        if (i>3) break // --.\n        print i        //   | (Once i>3)\n    }                  //   |\n    ...                // <-'\n\nwould only print 1, 2 and 3. Once the condition `i>3` is true, the `break` statement causes execution to continue after the loop body.\n\nBoth `for` and `while` loops support break.\n\n## Continue\n[tagcontinue]: # (continue)\n\n`Continue` is used inside loops to skip over the rest of an iteration. For example\n\n    for (i in 1..5) {     // <-.\n        print \"Hello\"          |\n        if (i>3) continue // --'\n        print i\n    }                     \n\nprints \"Hello\" five times but only prints 1, 2 and 3. Once the condition `i>3` is true, the `continue` statement causes execution to transfer to the start of the loop body.\n\nTraditional `for` loops also support `continue`:\n\n                    // v increment\n    for (var i=0; i<5; i+=1) {\n        if (i==2) continue\n        print i\n    }\n\nSince `continue` causes control to be transferred *to the increment section* in this kind of loop, here the program prints 0..4 but the number 2 is skipped.\n\nUse of `continue` with `while` loops is possible but isn't recommended as it can easily produce an infinite loop!\n\n    var i=0\n    while (i<5) {\n        if (i==2) continue\n        print i\n        i+=1\n    }\n\nIn this example, when the condition `i==2` is `true`, execution skips back to the start, but `i` *isn't* incremented. The loop gets stuck in the iteration `i==2`.\n\n## Try\n[tagtry]: # (try)\n[tagcatch]: # (catch)\n\nA `try` and `catch` statement allow you handle errors. For example\n\n    try {\n      // Do something\n    } catch {\n      \"Tag\" : // Handle the error\n    }\n\nCode within the block after the `try` keyword is executed. If an error is generated then Morpho looks to see if the tag associated with the error matches any of the labels in the `catch` block. If it does, the code after the matching label is executed. If no error occurs, the catch block is skipped entirely.\n"
  },
  {
    "path": "help/delaunay.md",
    "content": "[comment]: # (Delaunay module help)\n[version]: # (0.5)\n\n# Delaunay\n[tagdelaunay]: # (delaunay)\n\nThe `delaunay` module creates Delaunay triangulations from point clouds. It is dimensionally independent, so generates tetrahedra in 3D and higher order simplices beyond.\n\nTo use the module, first import it:\n\n    import delaunay\n\nTo create a Delaunary triangulation from a list of points:\n\n    var pts = []\n    for (i in 0...100) pts.append(Matrix([random(), random()]))\n    var del=Delaunay(pts)\n    print del.triangulate()\n\nThe module also provides `DelaunayMesh` to directly create meshes from Delaunay triangulations.\n\n[showsubtopics]: # (subtopics)\n\n## Triangulate\n[tagtriangulate]: # (triangulate)\n\nThe `triangulate` method performs the delaunay triangulation. To use it, first construct a `Delaunay` object with the point cloud of interest: \n\n    var del=Delaunay(pts)\n\nThen call `triangulate`:\n\n    var tri = del.triangulate()\n\nThis returns a list of triangles `[ [i, j, k], ... ]`.\n\n## Circumsphere\n[tagcircumsphere]: # (circumsphere)\n\nThe `Circumsphere` class calculates the circumsphere of a set of points, i.e. a sphere such that all the points are on the surface of the sphere. It is used internally by the `delaunay` module.\n\nCreate a `Circumsphere` from a list of points and a triangle specified by indices into that list:\n\n    var sph = Circumsphere(pts, [i,j,k]) \n\nTest if an arbitrary point is inside the `Circumsphere` or not: \n\n    print sph.pointinsphere(pt)\n"
  },
  {
    "path": "help/dictionary.md",
    "content": "[comment]: # (Dictionary help)\n[version]: # (0.5)\n\n# Dictionary\n[tag]: # (Dictionary)\n\nDictionaries are collection objects that associate a unique *key* with a particular *value*. Keys can be any kind of morpho value, including numbers, strings and objects.\n\nAn example dictionary mapping states to capitals:\n\n    var dict = { \"Massachusetts\" : \"Boston\",\n                 \"New York\" : \"Albany\",\n                 \"Vermont\" : \"Montpelier\" }\n\nLook up values by a given key with index notation:\n\n    print dict[\"Vermont\"]\n\nYou can change the value associated with a key, or add new elements to the dictionary like this:\n\n    dict[\"Maine\"]=\"Augusta\"\n\nCreate an empty dictionary using the `Dictionary` constructor function:\n\n    var d = Dictionary()\n\nLoop over keys in a dictionary:\n\n    for (k in dict) print k\n\nThe `keys` method returns a Morpho List of the keys.\n\n    var keys = dict.keys() // will return [\"Massachusetts\", \"New York\", \"Vermont\"]\n\nThe `contains` method returns a Bool value for whether the Dictionary\ncontains a given key.\n\n    print dict.contains(\"Vermont\") // true\n    print dict.contains(\"New Hampshire\") // false\n\nThe `remove` method removes a given key from the Dictionary.\n\n    dict.remove(\"Vermont\")\n    print dict // { New York : Albany, Massachusetts : Boston }\n\nThe `clear` method removes all the (key, value) pairs fromt the\ndictionary, resulting in an empty dictionary. \n\n    dict.clear()\n\n    print dict // {  }\n"
  },
  {
    "path": "help/errors.md",
    "content": "[comment]: # (Errors help file)\n[version]: # (0.5)\n\n# Errors\n[tagerror]: # (error)\n[tagerrors]: # (errors)\n[tagerrors]: # (throw)\n[tagerrors]: # (warning)\n\nWhen an error occurs in running a morpho program, an error message is displayed together with an explanation of where in the program that the error happened.\n\nYou can make your own custom errors using the `Error` class: \n\n    var myerr = Error(\"Tag\", \"A message\")\n\nUse the `throw` method to raise the error, interrupting execution unless the error is caught: \n\n    myerr.throw() \n\nor \n\n    myerr.throw(\"A custom message\") \n\nYou can also use the `warning` method to alert the user of a potential issue that doesn't need the program to be interrupted. \n\n    myerr.warning() \n\n[showsubtopics]: # (subtopics)\n\n## Alloc\n[tagalloc]: # (alloc)\n\nThis error may occur when creating new objects or resizing them. It typically indicates that the computer is under memory pressure.\n\n## Intrnl\n[tagintrnl]: # (intrnl)\n\nThis error indicates an internal problem with morpho. Please contact the developers for support.\n\n## InvldOp\n[taginvldop]: # (invldop)\n\nThis error occurs when an operator like `+` or `-` is given operands that it doesn't understand. For example,\n\n    print \"Hello\" * \"Goodbye\" // Causes 'InvldOp'\n\ncauses this error because the multiplication operator doesn't know how to multiply strings.\n\nIf the operands are objects, this means that the objects don't provide a method for the requested operation, e.g. for\n\n    print object1 / object2\n\n`object1` would need to provide a `div()` method that can successfully handle `object2`.\n\n## CnctFld\n[tagcnctfld]: # (cnctfld)\n\nThis error occurs when concatenation of strings or other objects fails, typically because of low memory.\n\n## Uncallable\n[taguncallable]: # (uncallable)\n\nThis error occurs when you try to call something that isn't a method or a function. Here, we initialize a variable with a string and call it:\n\n    var f = \"Not a function\"\n    f() // Causes 'Uncallable'\n\n## GlblRtrn\n[tagglblrtrn]: # (glblrtrn)\n\nThis error occurs when morpho encounters a `return` keyword outside of a function or method definition.\n\n## InstFail\n[taginstfail]: # (instfail)\n\nThis error occurs when morpho tried to create a new object, but something went wrong.\n\n## NotAnObj\n[tagnotanobj]: # (notanobj)\n\nThis error occurs if you try to access a property of something that isn't an object:  \n\n    var a = 1\n    a.size = 5\n\n## ObjLcksPrp\n[tagobjlcksprp]: # (objlcksprp)\n\nThis error occurs if you try to access a property or method that hasn't been defined for an object:\n\n    var a = Object()\n    print a.pifflepaffle\n\nor\n\n    print a.foo()\n\n## NoInit\n[tagnoinit]: # (noinit)\n\nThis error can occur if you try to create a new object from a class that doesn't have an `init` method:\n\n    class Foo { }\n    var a = Foo(0.3)\n\nHere, the argument to `Foo` causes the `NoInit` error because no `init` method is available to process it.\n\n## NotAnInst\n[tagnotaninst]: # (notaninst)\n\nThis error occurs if you try to invoke a method on something that isn't an object:\n\n    var a = 4\n    print a.foo()\n\n## ClssLcksMthd\n[tagclsslcksmthd]: # (clsslcksmthd)\n\nThis error occurs if you try to invoke a method on a class that doesn't exist:\n\n    class Foo { }\n    print Foo.foo()\n\n## InvldArgs\n[taginvldargs]: # (invldargs)\n\nThis error occurs if you call a function with the wrong number of arguments:\n\n    fn f(x) { return x }\n    f(1,2)\n\n## NotIndxbl\n[tagnotindxbl]: # (notindxbl)\n\nThis error occurs if you try to index something that isn't a collection:\n\n    var a = 0.3\n    print a[1]\n\n## IndxBnds\n[tagindxbnds]: # (indxbnds)\n\nThis error can occur when selecting an entry from a collection object (such as a list) if the index supplied is bigger than the number of entries:\n\n    var a = [1,2,3]\n    print a[10]\n\n## NonNmIndx\n[tagnonnmindx]: # (nonnmindx)\n\nThis error occurs if you try to index an array with a non-numerical index:\n\n    var a[2,2]\n    print a[\"foo\",\"bar\"]\n\n## ArrayDim\n[tagarraydim]: # arraydim\n\nThis error occurs if you try to index an array with the wrong number of indices:\n\n    var a[2,2]\n    print a[1]\n\n## DbgQuit\n[tagdbgquit]: # (dbgquit)\n\nThis notification is generated after selecting `Quit` within the debugger. Execution of the program is halted and control returns to the user.    \n\n## SymblUndf\n[tagsymblundf]: # (symblundf)\n\nThis error occurs if you refer to something that has not been previously declared, for example trying to use a variable of call a function that doesn't exist. It's possible that the symbol is spelt incorrectly, or that the capitalization doesn't match the definition (*morpho* symbols are case-sensitive).\n\nA common problem is to try to assign to a variable that hasn't yet been declared:\n\n    a = 5\n\nTo fix this, prefix with `var`:\n\n    var a = 5\n\n\n## MtrxIncmptbl\n[tagmtrxincmptbl]: # (mtrxincmptbl)\n\nThis error occurs when an arithmetic operation is performed on two 'incompatible' matrices. For example, two matrices must have the same dimensions, i.e. the same number of rows and columns, to be added or subtracted,\n\n    var a = Matrix([[1,2],[3,4]])\n    var b = Matrix([[1]])\n    print a+b // generates a `MtrxIncmptbl` error.\n\nOr to be multiplied together, the number of columns of the left hand matrix must equal the number of rows of the right hand matrix.\n\n    var a = Matrix([[1,2],[3,4]])\n    var b = Matrix([1,2])\n    print a*b // ok\n    print b*a // generates a `MtrxIncmptbl` error.\n"
  },
  {
    "path": "help/field.md",
    "content": "[comment]: # (Field class help)\n[version]: # (0.5)\n\n# Field\n[tagfield]: # (Field)\n\nFields are used to store information, including numbers or matrices, associated with the elements of a `Mesh` object.\n\nYou can create a `Field` by applying a function to each of the vertices,\n\n    var f = Field(mesh, fn (x, y, z) x+y+z)\n\nor by supplying a single constant value,\n\n    var f = Field(mesh, Matrix([1,0,0]))\n\nFields can then be added and subtracted using the `+` and `-` operators.\n\nTo access elements of a `Field`, use index notation:\n\n    print f[grade, element, index]\n\nwhere\n* `grade` is the grade to select\n* `element` is the element id\n* `index` is the element index\n\nAs a shorthand, it's possible to omit the grade and index; these are then both assumed to be `0`:\n\n    print f[2]\n\n[showsubtopics]: # (subtopics)\n\n## Mesh\n[tagmesh]: # (mesh)\n\nReturns the Mesh associated with a Field object:\n\n    var f.mesh() \n\n## Grade\n[taggrade]: # (grade)\n\nTo create fields that include grades other than just vertices, use the `grade` option to `Field`. This can be just a grade index,\n\n    var f = Field(mesh, 0, grade=2)\n\nwhich creates an empty field with `0` for each of the facets of the mesh `mesh`.\n\nYou can store more than one item per element by supplying a list to the `grade` option indicating how many items you want to store on each grade. For example,\n\n    var f = Field(mesh, 1.0, grade=[0,2,1])\n\nstores two numbers on the line (grade 1) elements and one number on the facets (grade 2) elements. Each number in the field is initialized to the value `1.0`.\n\n## Shape\n[tagshape]: # (shape)\n\nThe `shape` method returns a list indicating the number of items stored on each element of a particular grade. This has the same format as the list you supply to the `grade` option of the `Field` constructor. For example,\n\n    [1,0,2]\n\nwould indicate one item stored on each vertex and two items stored on each facet.\n\n## Op\n[tagop]: # (op)\n\nThe `op` method applies a function to every item stored in a `Field`, returning the result as elements of a new `Field` object. For example,\n\n    f.op(fn (x) x.norm())\n\ncalls the `norm` method on each element stored in `f`.\n\nAdditional `Field` objects may be supplied as extra arguments to `op`. These must have the same shape (the same number of items stored on each grade). The function supplied to `op` will now be called with the corresponding element from each field as arguments. For example,\n\n    f.op(fn (x,y) x.inner(y), g)\n\ncalculates an elementwise inner product between the elements of Fields `f` and `g`.\n"
  },
  {
    "path": "help/file.md",
    "content": "[comment]: # (File class help)\n[version]: # (0.5)\n\n# File\n[tagfile]: # (File)\n\nThe `File` class provides the capability to read from and write to files, or to obtain the contents of a file in convenient formats.\n\nTo open a file, create a File object with the filename as the argument\n\n    var f = File(\"myfile.txt\")\n\nwhich opens `\"myfile.txt\"` for *reading*. To open a file for writing or appending, you need to provide a mode selector\n\n    var g = File(\"myfile.txt\", \"write\")\n\nor\n\n    var g = File(\"myfile.txt\", \"append\")\n\nOnce the file is open, you can then read or write by calling appropriate methods:\n\n    f.lines()            // reads the contents of the file into an array of lines.\n    f.readline()         // reads a single line\n    f.readchar()         // reads a single character.\n    f.write(string)      // writes the arguments to the file.\n\nAfter you're done with the file, close it with\n\n    f.close()\n\n[show]: # (subtopics)\n\n## lines\n[taglines]: # (lines)\n\nReturns the contents of a file as an array of strings; each element corresponds to a single line.\n\nRead in the contents of a file and print line by line:\n\n    var f = File(\"input.txt\")\n    var s = f.lines()\n    for (i in s) print i\n    f.close()\n\n## readline\n[tagreadline]: # (readline)\n\nReads a single line from a file; returns the result as a string.\n\nRead in the contents of a file and print each line:\n\n    var f = File(\"input.txt\")\n    while (!f.eof()) {\n      print f.readline()\n    }\n    f.close()\n\n## readchar\n[tagreadchar]: # (readchar)\n\nReads a single character from a file; returns the result as a string.\n\n## write\n[tagwrite]: # (write)\n\nWrites to a file.\n\nWrite the contents of a list to a file:\n\n    var f = File(\"output.txt\", \"w\")\n    for (k, i in list) f.write(\"${i}: ${k}\")\n    f.close()\n\n## close\n[tagclose]: # (close)\n\nCloses an open file.\n\n## eof\n[tageof]: # (eof)\n\nReturns true if at the end of the file; false otherwise\n\n# Folder\n[tagfolder]: # (Folder)\n\nThe `Folder` class enables you to find whether a filepath refers to a folder, and find the contents of that folder.\n\nFind whether a path refers to a folder:\n\n    print Folder.isfolder(\"path/folder\")\n    \nGet a list of a folder's contents: \n\n    print Folder.contents(\"path/folder\")\n"
  },
  {
    "path": "help/functionals.md",
    "content": "[comment]: # (Functionals help)\n[version]: # (0.5)\n\n# Functionals\n[tagfunctionals]: # (functionals)\n\nA number of `functionals` are available in Morpho. Each of these represents an integral over some `Mesh` and `Field` objects (on a particular `Selection`) and are used to define energies and constraints in an `OptimizationProblem` provided by the `optimize` module.\n\nMany functionals are built in. Additional functionals are available by importing the `functionals` module:\n\n    import functionals\n\nFunctionals provide a number of standard methods:\n\n* `total`(mesh) - returns the value of the integral with a provided mesh, selection and fields\n* `integrand`(mesh) - returns the contribution to the integral from each element\n* `gradient`(mesh) - returns the gradient of the functional with respect to vertex motions.\n* `fieldgradient`(mesh, field) - returns the gradient of the functional with respect to components of the field\n\nEach of these may be called with a mesh, a field and a selection.\n\n[showsubtopics]: # (subtopics)\n\n## Length\n[taglength]: # (length)\n\nA `Length` functional calculates the length of a line element in a mesh.\n\nEvaluate the length of a circular loop:\n\n    import constants\n    import meshtools\n    var m = LineMesh(fn (t) [cos(t), sin(t), 0], 0...2*Pi:Pi/20, closed=true)\n    var le = Length()\n    print le.total(m)\n\nSee the `Functionals` entry for general information about functionals.\n\n## AreaEnclosed\n[tagareaenclosed]: # (areaenclosed)\n\nAn `AreaEnclosed` functional calculates the area enclosed by a loop of line elements.\n\n    var la = AreaEnclosed()\n\nEvaluate the area enclosed of a circular loop:\n\n    import constants\n    import meshtools\n    var m = LineMesh(fn (t) [cos(t), sin(t), 0], 0...2*Pi:Pi/20, closed=true)\n    var larea = AreaEnclosed()\n    print larea.total(m)\n\nSee the `Functionals` entry for general information about functionals.\n\n## Area\n[tagarea]: # (area)\n\nAn `Area` functional calculates the area of the area elements in a mesh:\n\n    var la = Area()\n    print la.total(mesh)\n\nSee the `Functionals` entry for general information about functionals.\n\n## VolumeEnclosed\n[tagvolumeenclosed]: # (volumeenclosed)\n\nA `VolumeEnclosed` functional is used to calculate the volume enclosed by a surface. Note that this estimate may become inaccurate for highly deformed surfaces.\n\n    var lv = VolumeEnclosed()\n    print lv.total(mesh)\n\nSee the `Functionals` entry for general information about functionals.\n\n## Volume\n[tagvolume]: # (volume)\n\nA `Volume` functional calculates the volume of volume elements.\n\n    var lv = Volume()\n\nSee the `Functionals` entry for general information about functionals.\n\n## ScalarPotential\n[tagscalarpotential]: # (scalarpotential)\n\nThe `ScalarPotential` functional is applied to point elements.\n\n    var ls = ScalarPotential(potential)\n\nYou must supply a function (which may be anonymous) that returns the potential. You may optionally provide a function that returns the gradient as well at initialization:\n\n    var ls = ScalarPotential(potential, gradient)\n\nThis functional is often used to constrain the mesh to the level set of a function. For example, to confine a set of points to a sphere:\n\n    import optimize\n    fn sphere(x,y,z) { return x^2+y^2+z^2-1 }\n    fn grad(x,y,z) { return Matrix([2*x, 2*y, 2*z]) }\n    var lsph = ScalarPotential(sphere, grad)\n    problem.addlocalconstraint(lsph)\n\nSee the thomson example for use of this technique.\n\nSee the `Functionals` entry for general information about functionals.\n\n## LinearElasticity\n[taglinearelasticity]: # (linearelasticity)\n\nThe `LinearElasticity` functional measures the linear elastic energy away from a reference state. \n\nYou must initialize with a reference mesh:\n\n    var le = LinearElasticity(mref)\n\nManually set the poisson's ratio and grade to operate on:\n\n    le.poissonratio = 0.2\n    le.grade = 2\n\nThe energy for each element in the Mesh is computed as follows: First the Gram matrix `S` is computed for the element as well as the Gram matrix `F` for the corresponding element in the reference Mesh. These quantities are used to compute the Cauchy-Green strain tensor:\n\n    C = (F S^-1 - I)/2\n\nThe energy density is then: \n\n    mu*Tr(C^2) + 1/2*lambda*Tr(C)^2\n\nwhere mu and lambda are the Lamé parameters. The total energy is found by multiplying the energy density by the volume or area of the element as appropriate. \n\nSee the `Functionals` entry for general information about functionals.\n\n## EquiElement\n[tagequielement]: # (equielement)\n\nThe `EquiElement` functional measures the discrepency between the size of elements adjacent to each vertex. It can be used to equalize elements for regularization purposes.\n\nSee the `Functionals` entry for general information about functionals.\n\n## LineCurvatureSq\n[taglinecurvaturesq]: # (linecurvaturesq)\n\nThe `LineCurvatureSq` functional measures the integrated curvature squared of a sequence of line elements.\n\nCompute the total squared curvature of a loop:\n\n    import constants\n    import meshtools\n    var m = LineMesh(fn (t) [cos(t), sin(t), 0], 0...2*Pi:Pi/20, closed=true)\n    var larea = LineCurvatureSq()\n    print larea.total(m)\n\nSee the `Functionals` entry for general information about functionals.\n\n## LineTorsionSq\n[taglinetorsionsq]: # (linetorsionsq)\n\nThe `LineTorsionSq` functional measures the integrated torsion squared of a sequence of line elements.\n\nCompute the total squared torsion of a helix:\n\n    import constants\n    import meshtools\n    var m = LineMesh(fn (t) [cos(t), sin(t), t], 0...2*Pi:Pi/20, closed=true)\n    var larea = LineTorsionSq()\n    print larea.total(m)\n\nSee the `Functionals` entry for general information about functionals.\n\n## MeanCurvatureSq\n[tagmeancurvsq]: # (meancurvaturesq)\n\nThe `MeanCurvatureSq` functional computes the integrated mean curvature over a surface.\n\nCompute the integrated mean squared curvature of the unit sphere:\n\n    import implicitmesh\n    var impl = ImplicitMeshBuilder(fn (x,y,z) x^2+y^2+z^2-1)\n    var mesh = impl.build(stepsize=0.25)\n    var lmsq = MeanCurvatureSq() \n    print lmsq.total(mesh) \n\nSee the `Functionals` entry for general information about functionals.\n\n## GaussCurvature\n[taggausscurv]: # (gausscurvature)\n\nThe `GaussCurvature` computes the integrated gaussian curvature over a surface.\n\nNote that for surfaces with a boundary, the integrand is correct only for the interior points. To compute the geodesic curvature of the boundary in that case, you can set the optional flag `geodesic` to `true` and compute the total on the boundary selection.\nHere is an example for a 2D disk mesh.\n\n    var mesh = Mesh(\"disk.mesh\")\n    mesh.addgrade(1)\n\n    var whole = Selection(mesh, fn(x,y,z) true)\n    var bnd = Selection(mesh, boundary=true)\n    var interior = whole.difference(bnd)\n\n    var gauss = GaussCurvature()\n    print gauss.total(mesh, selection=interior) // expect: 0\n    gauss.geodesic = true\n    print gauss.total(mesh, selection=bnd) // expect: 2*Pi\n\nSee the `Functionals` entry for general information about functionals.\n\n## GradSq\n[taggradsq]: # (gradsq)\n\nThe `GradSq` functional measures the integral of the gradient squared of a field. The field can be a scalar, vector or matrix function.\n\nInitialize with the required field:\n\n    var le=GradSq(phi)\n\nCompute the integral of GradSq(phi):\n\n    print le.total(mesh)\n\nSee the `Functionals` entry for general information about functionals.\n\n## Nematic\n[tagnematic]: # (nematic)\n\nThe `Nematic` functional measures the elastic energy of a nematic liquid crystal.\n\n    var lf=Nematic(nn)\n\nThere are a number of optional parameters that can be used to set the splay, twist and bend constants:\n\n    var lf=Nematic(nn, ksplay=1, ktwist=0.5, kbend=1.5, pitch=0.1)\n\nThese are stored as properties of the object and can be retrieved as follows:\n\n    print lf.ksplay\n\nSee the `Functionals` entry for general information about functionals.\n\n## NematicElectric\n[tagnematic]: # (nematic)\n\nThe `NematicElectric` functional measures the integral of a nematic and electric coupling term integral((n.E)^2) where the electric field E may be computed from a scalar potential or supplied as a vector.\n\nInitialize with a director field `nn` and a scalar potential `phi`:\n    var lne = NematicElectric(nn, phi)\n\nSee the `Functionals` entry for general information about functionals.\n\n## NormSq\n[tagnormsq]: # (normsq)\n\nThe `NormSq` functional measures the elementwise L2 norm squared of a field.\n\nSee the `Functionals` entry for general information about functionals.\n\n## LineIntegral\n[taglineintegral]: # (lineintegral)\n\nThe `LineIntegral` functional computes the line integral of a function. You supply an integrand function that takes a position matrix as an argument.\n\nTo compute `integral(x^2+y^2)` over a line element:\n\n    var la=LineIntegral(fn (x) x[0]^2+x[1]^2)\n\nThe function `tangent()` returns a unit vector tangent to the current element:\n\n    var la=LineIntegral(fn (x) x.inner(tangent()))\n\nYou can also integrate functions that involve fields:\n\n    var la=LineIntegral(fn (x, n) n.inner(tangent()), n)\n\nwhere `n` is a vector field. The local interpolated value of this field is passed to your integrand function. More than one field can be used; they are passed as arguments to the integrand function in the order you supply them to `LineIntegral`.\n\nThe gradient of a field is available within an integrand function using the `gradient()` function.\n\nSee the `Functionals` entry for general information about functionals.\n\n## AreaIntegral\n[tagareaintegral]: # (areaintegral)\n\nThe `AreaIntegral` functional computes the area integral of a function. You supply an integrand function that takes a position matrix as an argument.\n\nTo compute integral(x*y) over an area element:\n\n    var la=AreaIntegral(fn (x) x[0]*x[1])\n\nYou can also integrate functions that involve fields:\n\n    var la=AreaIntegral(fn (x, phi) phi^2, phi)\n\nThe local facet normal can be accessed in an integrand using the `normal()` function:\n\n    var la=AreaIntegral(fn (x) x.inner(normal())^2)\n\nMore than one field can be used; they are passed as arguments to the integrand function in the order you supply them to `AreaIntegral`.\n\nThe gradient of a field is available within an integrand function using the `gradient()` function.\n\nSee the `Functionals` entry for general information about functionals.\n\n## VolumeIntegral\n[tagvolumeintegral]: # (volumeintegral)\n\nThe `VolumeIntegral` functional computes the volume integral of a function. You supply an integrand function that takes a position matrix as an argument.\n\nTo compute integral(x*y*z) over an volume element:\n\n    var la=VolumeIntegral(fn (x) x[0]*x[1]*x[2])\n\nYou can also integrate functions that involve fields:\n\n    var la=VolumeIntegral(fn (x, phi) phi^2, phi)\n\nMore than one field can be used; they are passed as arguments to the integrand function in the order you supply them to `VolumeIntegral`.\n\nThe gradient of a field is available within an integrand function using the `gradient()` function.\n\nSee the `Functionals` entry for general information about functionals.\n\n## Hydrogel\n[taghydrogel]: # (hydrogel)\n\nThe `Hydrogel` functional computes the Flory-Rehner energy over an element:\n\n    (a*phi*log(phi) + b*(1-phi)+log(1-phi) + c*phi*(1-phi))*V + \n    d*(log(phiref/phi)/3 - (phiref/phi)^(2/3) + 1)*V0\n\nThe first three terms come from the Flory-Huggins mixing energy, whereas\nthe fourth term proportional to d comes from the Flory-Rehner elastic\nenergy.\n\nThe value of phi is calculated from a reference mesh\nthat you provide on initializing the Functional: \n\n    var lfh = Hydrogel(mref)\n\nHere, a, b, c, d and phiref are parameters you can supply (they are `nil`\nby default), V is the current volume and V0 is the reference volume of a\ngiven element. You also need to supply the initial value of phi, labeled\nas phi0, which is assumed to be the same for all the elements. \nManually set the coefficients and grade to operate on:\n\n    lfh.a = 1; lfh.b = 1; lfh.c = 1; lfh.d = 1;\n    lfh.grade = 2, lfh.phi0 = 0.5, lfh.phiref = 0.1\n\nSee the `Functionals` entry for general information about functionals.\n"
  },
  {
    "path": "help/functions.md",
    "content": "[comment]: # (Morpho functions help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# Functions\n[tagfn]: # (fn)\n[tagfun]: # (fun)\n[tagfunction]: # (function)\n\nA function in morpho is defined with the `fn` keyword, followed by the function's name, a list of parameters enclosed in parentheses, and the body of the function in curly braces. This example computes the square of a number:\n\n    fn sqr(x) {\n      return x*x\n    }\n\nOnce a function has been defined you can evaluate it like any other morpho function.\n\n    print sqr(2)\n\nFunctions can accept optional parameters:\n\n    fn fun(x, quiet=true ) { if (!quiet) print \"Loud!\" }\n\nMorpho functions can also be defined to restrict the type of accepted parameters:\n\n    fn f(List x) { print \"A list!\" }\n\nMultiple implementations can be defined that accept different numbers of parameters and parameter types:\n\n    fn f() { print \"No parameters!\" }\n    fn f(String x) { print \"A string!\" }\n    fn f(Tuple x) { print \"A tuple!\" }\n\nThe correct implementation is then selected at runtime: \n\n    f(\"Hello World!\") // expect: A string! \n\n[show]: # (subtopics) \n\n## Variadic\n[tagvariadic]: # (variadic)\n\nAs well as regular parameters, functions can also be defined with *variadic* parameters: \n\n    fn func(x, ...v) {\n        for (a in v) print a\n    }\n\nThis function can then be called with 1 or more arguments: \n\n    func(1)\n    func(1, 2)\n    func(1, 2, 3) // All valid! \n\nThe variadic parameter `v` captures all the extra arguments supplied. Functions cannot be defined with more than one variadic parameter. \n\nYou can mix regular, variadic and optional parameters. Variadic parameters come before optional parameters:\n\n    fn func(x, ...v, optional=true) {\n        // \n    }\n\n## Optional\n[tagoptional]: # (optional)\n\nFunctions can also be defined with *optional* parameters:\n\n    fn func(a=1) {\n        print a \n    }\n\nEach optional parameter must be defined with a default value (here `1`). The function can then be called either with or without the optional parameter: \n\n    func()    // a == 1 due to default value\n    func(a=2) // a == 2 supplied by the user\n\nNote that optional parameters may not be typed. \n\n## Return\n[tagreturn]: # (return)\n\nThe `return` keyword is used to exit from a function, optionally passing a given value back to the caller. `return` can be used anywhere within a function. The below example calculates the `n` th Fibonacci number,\n\n    fn fib(n) {\n      if (n<2) return n\n      return fib(n-1) + fib(n-2)\n    }\n\nby returning early if `n<2`, otherwise returning the result by recursively calling itself.\n\n## Signature\n[tagsignature]: # (signature)\n\nThe *signature* of a function is a list of the types of arguments in its definition: \n\n    fn f(x) {}         // Accepts one parameter of any type\n    fn f(x,y) {}       // Accepts two parameters of any type\n    fn f(List x, y) {} // The first parameter must be a list\n    fn f(List x, List y) {} // Both parameters must be lists\n\nWhile you can define multiple implementations of a function that accept different parameter types, you can only define one implementation with a unique signature.\n\nNote that optional and variadic parameters are not typed.\n\n# Multiple dispatch\n[tagmultiple]: # (multiple)\n[tagdispatch]: # (dispatch)\n[tagmultipledispatch]: # (multipledispatch)\n\nMorpho supports *multiple dispatch*, whereby you can define several implementations of a function that accept different types:\n\n    fn f(List x) { return \"Accepts a list\" }\n    fn f(String x) { return \"Accepts a string\" }\n\nMorpho chooses the appropriate implementation at runtime: \n\n    f([1,2,3]) // Selects the List implementation\n\nAny classes you define can be used as types:\n\n    class A {} \n    class B is A {} \n    class C is B {} \n\n    fn f(A x) { return \"A\" }\n    fn f(B x) { return \"B\" }\n\nMorpho selects the *closest match*, so that: \n\n    print f(A()) // expect: A\n    print f(B()) // expect: B\n    print f(C()) // expect: B\n\nClass `C` inherits from both `B` and `A`, but because it directly inherits from `B`, that's the closer match. \n\n# Closures\n[tagclosures]: # (closures)\n[tagclosure]: # (closure)\n\nFunctions in morpho can form *closures*, i.e. they can enclose information from their local context. In this example, \n\n    fn foo(a) {\n        fn g() { return a } \n        return g\n    }\n\nthe function `foo` returns a function that captures the value of `a`. If we now try calling `foo` and then calling the returned functions,\n\n    var p=foo(1), q=foo(2) \n    print p() // expect: 1 \n    print q() // expect: 2\n    \nwe can see that `p` and `q` seem to contain different copies of `g` that encapsulate the value that `foo` was called with. \n\nMorpho hints that a returned function is actually a closure by displaying it with double brackets: \n\n    print foo(1) // expect: <<fn g>> \n"
  },
  {
    "path": "help/graphics.md",
    "content": "[comment]: # (Graphics module help)\n[version]: # (0.5)\n\n# Graphics\n[taggraphics]: # (graphics)\n\nThe `graphics` module provides a number of classes to provide simple visualization capabilities. To use it, you first need to import the module:\n\n    import graphics\n\nThe `Graphics` class acts as an abstract container for graphical information; to actually launch the display see the `Show` class. You can create an empty scene like this,\n\n    var g = Graphics()\n\nAdditional elements can be added using the `display` method.\n\n    g.display(element)\n\nMorpho provides the following fundamental Graphical element classes:\n\n    TriangleComplex\n\nYou can also use functions like `Arrow`, `Tube` and `Cylinder` to create these elements conveniently.\n\nTo combine graphics objects, use the add operator:\n\n    var g1 = Graphics(), g2 = Graphics()\n    // ...\n    Show(g1+g2)\n\n[show]: # (subtopics)\n\n## Show\n[tagshow]: # (Show)\n\n`Show` is used to launch an interactive graphical display using the external `morphoview` application. `Show` takes a `Graphics` object as an argument:\n\n    var g = Graphics()\n    Show(g)\n\n## TriangleComplex\n[tagTriangleComplex]: # (TriangleComplex)\n\nA `TriangleComplex` is a graphical element that can be used as part of a graphical display. It consists of a list of vertices and a connectivity matrix that selects which vertices are used in each triangle.\n\nTo create one, call the constructor with the following arguments:\n\n    TriangleComplex(position, normals, colors, connectivity)\n\n* `position` is a `Matrix` containing vertex positions as *columns*.\n* `normals` is a `Matrix` with a normal for each vertex.\n* `colors` is the color of the object.\n* `connectivity` is a `Sparse` matrix where each column represents a triangle and rows correspond to vertices.\n\nYou can also provide optional arguments:\n\n* `transmit` sets the transparency of the object. This parameter is only\nused by the povray module as of now. Default is 0.\n* `filter` sets the transparency of the object using a filter effect.\nThis parameter is only used by the povray module as of now. Default is 0. For the difference between `transmit` and `filter`, checkout the \n[POVRay documentation](http://xahlee.info/3d/povray-glassy.html).\n\n\nAdd to a `Graphics` object using the `display` method.\n\n## Arrow\n[tagArrow]: # (Arrow)\n\nThe `Arrow` function creates an arrow. It takes two arguments:\n\n    arrow(start, end)\n\n* `start` and `end` are the two vertices. The arrow points `start` -> `end`.\n\nYou can also provide optional arguments:\n\n* `aspectratio` controls the width of the arrow relative to its length\n* `n` is an integer that controls the quality of the display. Higher `n` leads to a rounder arrow.\n* `color` is the color of the arrow. This can be a list of RGB values or a `Color` object\n* `transmit` sets the transparency of the arrow. This parameter is only\nused by the povray module as of now. Default is 0.\n* `filter` sets the transparency of the arrow using a filter effect.\nThis parameter is only used by the povray module as of now. Default is 0. For the difference between `transmit` and `filter`, checkout the \n[POVRay documentation](http://xahlee.info/3d/povray-glassy.html).\n\nDisplay an arrow:\n\n    var g = Graphics([])\n    g.display(Arrow([-1/2,-1/2,-1/2], [1/2,1/2,1/2], aspectratio=0.05, n=10))\n    Show(g)\n\n## Cylinder\n[tagCylinder]: # (Cylinder)\n\nThe `Cylinder` function creates a cylinder. It takes two required arguments:\n\n    cylinder(start, end)\n\n* `start` and `end` are the two vertices.\n\nYou can also provide optional arguments:\n\n* `aspectratio` controls the width of the cylinder relative to its length.\n* `n` is an integer that controls the quality of the display. Higher `n` leads to a rounder cylinder.\n* `color` is the color of the cylinder. This can be a list of RGB values or a `Color` object.\n* `transmit` sets the transparency of the cylinder. This parameter is only\nused by the povray module as of now. Default is 0.\n* `filter` sets the transparency of the cylinder using a filter effect.\nThis parameter is only used by the povray module as of now. Default is 0. For the difference between `transmit` and `filter`, checkout the \n[POVRay documentation](http://xahlee.info/3d/povray-glassy.html).\n\nDisplay an cylinder:\n\n    var g = Graphics()\n    g.display(Cylinder([-1/2,-1/2,-1/2], [1/2,1/2,1/2], aspectratio=0.1, n=10))\n    Show(g)\n\n## Tube\n[tagTube]: # (Tube)\n\nThe `Tube` function connects a sequence of points to form a tube.\n\n    Tube(points, radius)\n\n* `points` is a list of points; this can be a list of lists or a `Matrix` with the positions as columns.\n* `radius` is the radius of the tube.\n\nYou can also provide optional arguments:\n\n* `n` is an integer that controls the quality of the display. Higher `n` leads to a rounder tube.\n* `color` is the color of the tube. This can be a list of RGB values or a `Color` object.\n* `closed` is a `bool` that indicates whether the tube should be closed to form a loop.\n* `transmit` sets the transparency of the tube. This parameter is only\nused by the povray module as of now. Default is 0.\n* `filter` sets the transparency of the tube using a filter effect.\nThis parameter is only used by the povray module as of now. Default is 0. For the difference between `transmit` and `filter`, checkout the \n[POVRay documentation](http://xahlee.info/3d/povray-glassy.html).\n\nDraw a square:\n\n    var a = Tube([[-1/2,-1/2,0],[1/2,-1/2,0],[1/2,1/2,0],[-1/2,1/2,0]], 0.1, closed=true)\n    var g = Graphics()\n    g.display(a)\n\n## Sphere\n[tagSphere]: # (Sphere)\n\nThe `Sphere` function creates a sphere.\n\n    Sphere(center, radius)\n\n* `center` is the position of the center of the sphere; this can be a list or column `Matrix`.\n* `radius` is the radius of the sphere\n\nYou can also provide optional arguments:\n\n* `color` is the color of the sphere. This can be a list of RGB values or a `Color` object.\n* `transmit` sets the transparency of the sphere. This parameter is only\nused by the povray module as of now. Default is 0.\n* `filter` sets the transparency of the sphere using a filter effect.\nThis parameter is only used by the povray module as of now. Default is 0. For the difference between `transmit` and `filter`, checkout the \n[POVRay documentation](http://xahlee.info/3d/povray-glassy.html).\n\nDraw some randomly sized spheres:\n\n    var g = Graphics()\n    for (i in 0...10) {\n      g.display(Sphere([random()-1/2, random()-1/2, random()-1/2], 0.1*(1+random()),       color=Gray(random())))\n    }\n    Show(g)\n\n## Text\n[tagText]: # (Text)\n\nA `Text` object is used to display text. \n\n    Text(text, position)\n\n* `text` is the text to display as a string.\n* `position` is the position at which to display the text. \n\nYou can also provide optional arguments:\n\n* `color` is the color of the text. This should be a `Color` object.\n* `dirn` is the direction along which the text is drawn. This should be a `List` or a `Matrix`.\n* `size` is the font size to use\n* `vertical` is the vertical direction for the text\n* `font` is the `Font` object to use.\n\nDraw several pieces of text around the y axis:\n\n    var g = Graphics()\n    for (phi in 0..Pi:Pi/8) {\n      g.display(Text(\"Hello World\", [0,0,0], size=72, dirn=[0,1,0], vertical=[cos(phi),0,sin(phi)]))\n    }\n    Show(g)\n"
  },
  {
    "path": "help/help.md",
    "content": "[comment]: # (Morpho language help file)\n[version]: # (0.5)\n\n# Help\n[tag]: # (help)\n\nMorpho provides an online help system. To get help about a topic called `topicname`, type\n\n    help topicname\n\nA list of available topics is provided below and includes language keywords like `class`, `fn` and `for`, built in classes like `Matrix` and `File` or information about functions like `exp` and `random`.\n\nSome topics have additional subtopics: to access these type\n\n    help topic subtopic\n\nFor example, to get help on a method for a particular class, you could type\n\n    help Classname.methodname\n\nNote that `help` ignores all punctuation.\n\nYou can also use `?` as a shorthand synonym for `help`\n\n    ? topic\n\nA useful feature is that, if an error occurs, simply type `help` to get more information about the error.\n\n[showtopics]: # (topics)\n\n# Quit\n[tagquit]: # (quit)\n\nThe `quit` CLI command quits `morpho` run in interactive mode and returns to the shell.\n"
  },
  {
    "path": "help/implicitmesh.md",
    "content": "[comment]: # (Implicitmesh module help)\n[version]: # (0.5)\n\n# ImplicitMesh\n[tagimplicitmesh]: # (implicitmesh)\n\nThe `implicitmesh` module allows you to build meshes from implicit functions. For example, the unit sphere could be specified using the function `x^2+y^2+z^2-1 == 0`.\n\nTo use the module, first import it:\n\n    import implicitmesh\n\nTo create a sphere, first create an ImplicitMeshBuilder object with the implict function you'd like to use:\n\n    var impl = ImplicitMeshBuilder(fn (x,y,z) x^2+y^2+z^2-1)\n\nYou can use an existing function (or method) as well as an anonymous function as above.\n\nThen build the mesh,\n\n    var mesh = impl.build(stepsize=0.25)\n\nThe `build` method takes a number of optional arguments:\n\n* `start` - the starting point. If not provided, the value Matrix([1,1,1]) is used.\n* `stepsize` - approximate lengthscale to use.\n* `maxiterations` - maximum number of iterations to use. If this limit is exceeded, a partially built mesh will be returned.\n"
  },
  {
    "path": "help/index.rst",
    "content": "Morpho\n======\n\n.. toctree::\n   :caption: Language\n   :maxdepth: 1\n\n   syntax\n   values\n   variables\n   controlflow\n   functions\n   classes\n   modules\n   help\n   builtin\n\n.. toctree::\n   :caption: Data Types\n   :maxdepth: 1\n\n   array\n   complex\n   dictionary\n   list\n   matrix\n   range\n   sparse\n   string\n   tuple\n\n.. toctree::\n   :caption: Computational Geometry\n   :maxdepth: 1\n\n   field\n   functionals\n   mesh\n   selection\n\n.. toctree::\n   :caption: I/O\n   :maxdepth: 1\n\n   file\n   system\n   json\n\n.. toctree::\n   :caption: Modules\n   :maxdepth: 1\n\n   color\n   constants\n   delaunay\n   graphics\n   implicitmesh\n   kdtree\n   meshgen\n   meshslice\n   meshtools\n   optimize\n   plot\n   povray\n   vtk\n\n.. toctree::\n   :caption: Error messages\n   :maxdepth: 1\n\n   errors\n"
  },
  {
    "path": "help/json.md",
    "content": "[comment]: # (Morpho json help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# JSON\n[tagjson]: # (json)\n\nThe `JSON` class provides import and export functionality for the JSON (JavaScript Object Notation) interchange file format as defined by IETF RFC 7159. \n\nTo parse a string that contains JSON, use the `parse` method: \n\n    var a = JSON.parse(\"[1,2,3,4]\")\n    print a // expect: [ 1, 2, 3, 4 ]\n\nElements in the JSON string are converted to equivalent morpho values.\n\nTo convert basic data types to JSON, use the `tostring` method: \n\n    var b = JSON.tostring([1,2,3])\n\nThe exporter supports `nil`, boolean values `true` and `false`, numbers, `String`s as well as `List` and `Dictionary` objects that may contain any of the supported types. \n"
  },
  {
    "path": "help/kdtree.md",
    "content": "[comment]: # (KDTree module help)\n[version]: # (0.5)\n\n# KDTree\n[tagkdtree]: # (kdtree)\n\nThe `kdtree` module implements a k-dimensional tree, a space partitioning data structure that can be used to accelerate computational geometry calculations.\n\nTo use the module, first import it:\n\n    import kdtree\n\nTo create a tree from a list of points:\n\n    var pts = []\n    for (i in 0...100) pts.append(Matrix([random(), random(), random()]))\n    var tree=KDTree(pts)\n\nAdd further points:\n\n    tree.insert(Matrix([0,0,0]))\n\nTest whether a given point is present in the tree:\n\n    tree.ismember(Matrix([1,0,0]))\n\nFind all points within a given bounding box:\n\n    var pts = tree.search([[-1,1], [-1,1], [-1,1]])\n    for (x in pts) print x.location\n\nFind the nearest point to a given point:\n\n    var pt = tree.nearest(Matrix([0.1, 0.1, 0.5]))\n    print pt.location\n\n[showsubtopics]: # (subtopics)\n\n## Insert\n[taginsert]: # (insert)\n\nInserts a new point into a k-d tree. Returns a KDTreeNode object.\n\n    var node = tree.insert(Matrix([0,0,0]))\n\nNote that, for performance reasons, if the set of points is known ahead of time, it is generally better to build the tree using the constructor function KDTree rather than one-by-one with insert.\n\n## Ismember\n[tagismember]: # (ismember)\n\nChecks if a point is a member of a k-d tree. Returns `true` or `false`.\n\n    print tree.ismember(Matrix([0,0,0]))\n\n## Nearest\n[tagnearest]: # (nearest)\n\nFinds the point in a k-d tree nearest to a point of interest. Returns a KDTreeNode object.\n\n    var pt = tree.nearest(Matrix([0.1, 0.1, 0.5]))\n\nTo get the location of this nearest point, access the location property:\n\n    print pt.location\n\n## Search\n[tagsearch]: # (search)\n\nFinds all points in a k-d tree that lie within a cuboidal bounding box. Returns a list of KDTreeNode objects.\n\nFind and display all points that lie in a cuboid 0<=x<=1, 0<=y<=2, 1<=z<=2:\n\n    var result = tree.search([[0,1], [0,2], [1,2]])\n    for (x in result) print x.location\n\n## KDTreeNode\n[tagkdtreenode]: # (kdtreenode)\n\nAn object corresponding to a single node in a k-d tree. To get the location of the node, access the `location` property:\n\n    print node.location\n"
  },
  {
    "path": "help/list.md",
    "content": "[comment]: # (List class help)\n[version]: # (0.5)\n\n# List\n[taglist]: # (List)\n\nLists are collection objects that contain a sequence of values each associated with an integer index.\n\nCreate a list like this:\n\n    var list = [1, 2, 3]\n\nLook up values using index notation:\n\n    list[0]\n\nIndexing can also be done with slices:\n\tlist[0..2]\n\tlist[[0,1,3]]\n\nYou can change list entries like this:\n\n    list[0] = \"Hello\"\n\nCreate an empty list:\n\n    var list = []\n\nLoop over elements of a list:\n\n    for (i in list) print i\n\n[showsubtopics]: # (subtopics)\n\n## Append\n[tagappend]: # (Append)\n\nAdds an element to the end of a list:\n\n    var list = []\n    list.append(\"Foo\")\n\n## Insert\n[taginsert]: # (Insert)\n\nInserts an element into a list at a specified index:\n\n    var list = [1,2,3]\n    list.insert(1, \"Foo\")\n    print list // prints [ 1, Foo, 2, 3 ]\n\n## Pop\n[tagpop]: # (pop)\n\nRemove the last element from a list, returning the element removed:\n\n    print list.pop()\n\nIf an integer argument is supplied, returns and removes that element:\n\n    var a = [1,2,3]\n    print a.pop(1) // prints '2'\n    print a        // prints [ 1, 3 ]\n\n## Sort\n[tagsort]: # (sort)\n\nSorts the contents of a list into ascending order:\n\n    list.sort()\n\nNote that this sorts the list \"in place\" (i.e. it modifies the order of the list on which it is invoked) and hence returns `nil`.\n\nYou can provide your own function to use to compare values in the list\n\n    list.sort(fn (a, b) a-b)\n\nThis function should return a negative value if `a<b`, a positive value if `a>b` and `0` if `a` and `b` are equal.\n\n## Order\n[tagorder]: # (order)\n\nReturns a list of indices that would, if used in order, would sort a list. For example\n\n    var list = [2,3,1]\n    print list.order() // expect: [2,0,1]\n\nwould produce `[2,0,1]`\n\n## Remove\n[tagremove]: # (remove)\n\nRemove any occurrences of a value from a list:\n\n    var list = [1,2,3]\n    list.remove(1)\n\n## ismember\n[tagismember]: # (ismember)\n\nTests if a value is a member of a list:\n\n    var list = [1,2,3]\n    print list.ismember(1) // expect: true\n\n## Add\n[tagadd]: # (add)\n\nJoin two lists together:\n\n    var l1 = [1,2,3], l2 = [4, 5, 6]\n    print l1+l2 // expect: [1,2,3,4,5,6]\n\n## Tuples\n[tagtuples]: # (tuples)\n\nGenerate all possible 2-tuples from a list:\n\n    var t = [ 1, 2, 3].tuples(2)\n    \nproduces `[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ] ... ]`.\n    \n## Sets\n[tagsets]: # (sets)\n\nGenerate all possible sets of order 2 from a list. \n\n    var t = [ 1, 2, 3 ].sets(2)\n    \nproduces `[ [ 1, 2 ], [ 1, 3 ], [ 2, 3 ] ]`.\n\nNote that sets include only distinct elements from the list (no element is repeated) and ordering is unimportant, hence only one of  `[ 1, 2 ]` and `[ 2, 1 ]` is returned. \n"
  },
  {
    "path": "help/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=.\nset BUILDDIR=_build\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\n\n:end\npopd\n"
  },
  {
    "path": "help/matrix.md",
    "content": "[comment]: # (Matrix class help)\n[version]: # (0.5)\n\n# Matrix\n[tagmatrix]: # (Matrix)\n\nThe Matrix class provides support for matrices. A matrix can be initialized with a given size,\n\n    var a = Matrix(nrows,ncols)\n\nwhere all elements are initially set to zero. Alternatively, a matrix can be created from an array,\n\n    var a = Matrix([[1,2], [3,4]])\n\nor a Sparse matrix,\n\n    var a = Sparse([[0,0,1],[1,1,1],[2,2,1]])\n    var b = Matrix(a)\n\nYou can create a column vector like this,\n\n    var v = Matrix([1,2])\n\nFinally, you can create a Matrix by assembling other matrices like this,\n\n    var a = Matrix([[0,1],[1,0]])\n    var b = Matrix([[a,0],[0,a]]) // produces a 4x4 matrix \n\nOnce a matrix is created, you can use all the regular arithmetic operators with matrix operands, e.g.\n\n    a+b\n    a*b\n\nYou can retrieved individual matrix entries with specified indices:\n\n    print a[0,0]\n\nor create a submatrix using slices:\n\n\tprint a[0..1,0..1]\n\nThe division operator is used to solve a linear system, e.g.\n\n    var a = Matrix([[1,2],[3,4]])\n    var b = Matrix([1,2])\n\n    print b/a\n\nyields the solution to the system a*x = b.\n\n[showsubtopics]: # (subtopics)\n\n## Assign\n[tagassign]: # (Assign)\n\nCopies the contents of matrix B into matrix A: \n\n    A.assign(B)\n\nThe two matrices must have the same dimensions.\n\n## Dimensions\n[tagdimensions]: # (Dimensions)\n\nReturns the dimensions of a matrix:\n\n    var A = Matrix([1,2,3]) // Create a column matrix \n    print A.dimensions()    // Expect: [ 3, 1 ]\n\n## Eigenvalues\n[tageigenvalues]: # (Eigenvalues)\n\nReturns a list of eigenvalues of a Matrix:\n\n    var A = Matrix([[0,1],[1,0]])\n    print A.eigenvalues() // Expect: [1,-1]\n\n## Eigensystem\n[tageigensystem]: # (Eigensystem)\n\nReturns the eigenvalues and eigenvectors of a Matrix:\n\n    var A = Matrix([[0,1],[1,0]])\n    print A.eigensystem() \n\nEigensystem returns a two element list: The first element is a List of eigenvalues. The second element is a Matrix containing the corresponding eigenvectors as its columns:\n\n    print A.eigensystem()[0]\n    // [ 1, -1 ]\n    print A.eigensystem()[1]\n    // [ 0.707107 -0.707107 ]\n    // [ 0.707107 0.707107 ]\n\n## Inner\n[taginner]: # (Inner)\n\nComputes the Frobenius inner product between two matrices:\n\n    var prod = A.inner(B)\n\n## Outer\n[tagouter]: # (Outer)\n\nComputes the outer produce between two vectors: \n\n    var prod = A.outer(B)\n\nNote that `outer` always treats both vectors as column vectors. \n\n## Inverse\n[taginverse]: # (Inverse)\n\nReturns the inverse of a matrix if it is invertible. Raises a\n`MtrxSnglr` error if the matrix is singular. E.g.\n\n    var m = Matrix([[1,2],[3,4]])\n    var mi = m.inverse()\n\nyields the inverse of the matrix `m`, such that mi*m is the identity\nmatrix.\n\n## Norm\n[tagnorm]: # (Norm)\n\nReturns a matrix norm. By default the L2 norm is returned:\n\n    var a = Matrix([1,2,3,4])\n    print a.norm() // Expect: sqrt(30) = 5.47723...\n\nYou can select a different norm by supplying an argument:\n\n    import constants\n    print a.norm(1) // Expect: 10 (L1 norm is sum of absolute values) \n    print a.norm(3) // Expect: 4.64159 (An unusual choice of norm)\n    print a.norm(Inf) // Expect: 4 (Inf-norm corresponds to maximum absolute value)\n\n## Reshape\n[tagreshape]: # (Reshape)\n\nChanges the dimensions of a matrix such that the total number of elements remains constant:\n\n    var A = Matrix([[1,3],[2,4]])\n    A.reshape(1,4) // 1 row, 4 columns\n    print A // Expect: [ 1, 2, 3, 4 ]\n\nNote that elements are stored in column major-order.\n\n## Sum\n[tagsum]: # (Sum)\n\nReturns the sum of all entries in a matrix:\n\n    var sum = A.sum() \n\n## Transpose\n[tagtranspose]: # (Transpose)\n\nReturns the transpose of a matrix: \n\n    var At = A.transpose()\n\n## Trace\n[tagtrace]: # (Trace)\n\nComputes the trace (the sum of the diagonal elements) of a square matrix:\n\n    var tr = A.trace()\n\n## Roll\n[tagroll]: # (Roll)\n\nRotates values in a Matrix about a given axis by a given shift:\n\n    var r = A.roll(shift, axis)\n\nElements that roll beyond the last position are re-introduced at the first.\n\n## IdentityMatrix\n[tagidentitymatrix]: # (IdentityMatrix)\n\nConstructs an identity matrix of a specified size:\n\n    var a = IdentityMatrix(size)\n"
  },
  {
    "path": "help/mesh.md",
    "content": "[comment]: # (Mesh class help)\n[version]: # (0.5)\n\n# Mesh\n[tagmesh]: # (Mesh)\n\nThe `Mesh` class provides support for meshes. Meshes may consist of different kinds of element, including vertices, line elements, facets or area elements, tetrahedra or volume elements.\n\nTo create a mesh, you can import it from a file:\n\n    var m = Mesh(\"sphere.mesh\")\n\nor use one of the functions available in `meshtools` or `implicitmesh` packages.\n\nEach type of element is referred to as belonging to a different `grade`. Point-like elements (vertices) are *grade 0*; line-like elements (edges) are *grade 1*; area-like elements (facets; triangles) are *grade 2* etc.\n\nThe `plot` package includes functions to visualize meshes.\n\n[showsubtopics]: # (showsubtopics)\n\n## Save\n[tagsave]: # (Save)\n\nSaves a mesh as a .mesh file.\n\n    m.save(\"new.mesh\")\n\n## Vertexposition\n[tagvertexposition]: # (vertexposition)\n\nRetrieves the position of a vertex given an id:\n\n    print m.vertexposition(id)\n\n## Setvertexposition\n[tagsetvertexposition]: # (setvertexposition)\n\nSets the position of a vertex given an id and a position vector:\n\n    print m.setvertexposition(1, Matrix([0,0,0]))\n\n## Addgrade\n[tagaddgrade]: # (addgrade)\n\nAdds a new grade to a mesh. This is commonly used when, for example, a mesh file includes facets but not edges. To add the missing edges:\n\n    m.addgrade(1)\n\n## Addsymmetry\n[tagaddsymmetry]: # (addsymmetry)\n\nAdds a symmetry to a mesh. Experimental in version 0.5.\n\n## Maxgrade\n[tagmaxgrade]: # (maxgrade)\n\nReturns the highest grade element present:\n\n    print m.maxgrade()\n\n## Count\n[tagcount]: # (count)\n\nCounts the number of elements. If no argument is provided, returns the number of vertices. Otherwise, returns the number of elements present of a given grade:\n\n    print m.count(2) // Returns the number of area-like elements. \n"
  },
  {
    "path": "help/meshgen.md",
    "content": "[comment]: # (Meshgen module help)\n[version]: # (0.5)\n\n# Meshgen\n[tagmeshgen]: # (meshgen)\n\nThe `meshgen` module is used to create `Mesh` objects corresponding to a specified domain. It provides the `MeshGen` class to perform the meshing, which are created with the following arguments:\n\n    MeshGen(domain, boundingbox)\n\nDomains are specified by a scalar function that is positive in the region to be meshed and locally smooth. For example, to mesh the unit disk:\n\n    var dom = fn (x) -(x[0]^2+x[1]^2-1)\n\nA `MeshGen` object is then created and then used to build the `Mesh` like this:\n\n    var mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2])\n    var m = mg.build()\n\nA bounding box for the mesh must be specified as a `List` of `Range` objects, one for each dimension. The increment on each `Range` gives an approximate scale for the size of elements generated.\n\nTo facilitate convenient creation of domains, a `Domain` class is provided that provides set operations `union`, `intersection` and `difference`.\n\n`MeshGen` accepts a number of optional arguments:\n\n* `weight` A scalar weight function that controls mesh density.\n* `quiet` Set to `true` to suppress  `MeshGen` output.\n* `method` a list of options that controls the method used.\n\nSome method choices that are available include:\n\n* `\"FixedStepSize\"` Use a fixed step size in optimization.\n* `\"StartGrid\"` Start from a regular grid of points (the default).\n* `\"StartRandom\"` Start from a randomly generated collection of points.\n\nThere are also a number of properties of a `MeshGen` object that can be set prior to calling `build` to control the operation of the mesh generation:\n\n* `stepsize`, `steplimit` Stepsize used internally by the `Optimizer`\n* `fscale` an internal \"pressure\"\n* `ttol` how far the vertices are allowed to move before retriangulation\n* `etol` energy tolerance for optimization problem\n* `maxiterations` Maximum number of iterations of minimization +\n  retriangulation (default is 100)\n\n`MeshGen` picks default values that cover a reasonable range of uses.\n\n[showsubtopics]: # (subtopics)\n\n## Domain\n[tagdomain]: # (domain)\n\nThe `Domain` class is used to conveniently build a domain by composing simpler elements. \n\nCreate a `Domain` from a scalar function that is positive in the region of interest:\n\n    var dom = Domain(fn (x) -(x[0]^2+x[1]^2-1))\n\nYou can pass it to `MeshGen` to specify the region to mesh: \n\n    var mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2])\n\nYou can combine `Domain` objects using set operations `union`, `intersection` and `difference`: \n\n    var a = CircularDomain(Matrix([-0.5,0]), 1)\n    var b = CircularDomain(Matrix([0.5,0]), 1)\n    var c = CircularDomain(Matrix([0,0]), 0.3)\n    var dom = a.union(b).difference(c)\n\n## CircularDomain\n[tagcirculardomain]: # (circulardomain)\n\nConveniently constructs a `Domain` object correspondiong to a disk. Requires the position of the center and a radius as arguments. \n\nCreate a domain corresponding to the unit disk: \n\n    var c = CircularDomain([0,0], 1)\n\n## RectangularDomain\n[tagrectangulardomain]: # (rectangulardomain)\n\nConveniently constructs a `Domain` object corresponding to a rectangle. Requires a list of ranges as arguments. Works in arbitrary dimensions\n\nCreate a square `Domain`:\n\n    var c = RectangularDomain([-1..1, -1..1])\n\n## HalfSpaceDomain\n[halfspacedomain]: # (halfspacedomain)\n\nConveniently constructs a `Domain` object correspondiong to a half space defined by a plane at `x0` and a normal `n`:\n\n    var hs = HalfSpaceDomain(x0, n)\n\nNote `n` is an \"outward\" normal, so points into the *excluded* region.\n\nHalf space corresponding to the allowed region `x<0`:\n\n    var hs = HalfSpaceDomain(Matrix([0,0,0]), Matrix([1,0,0]))\n\nNote that `HalfSpaceDomain`s cannot be meshed directly as they correspond to an infinite region. They are useful, however, for combining with other domains.\n\nCreate half a disk by cutting a `HalfSpaceDomain` from a `CircularDomain`:\n\n    var c = CircularDomain([0,0], 1)\n    var hs = HalfSpaceDomain(Matrix([0,0]), Matrix([-1,0]))\n    var dom = c.difference(hs) \n    var mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2], quiet=false)\n    var m = mg.build()\n\n## MshGnDim\n[mshgndim]: # (mshgndim)\n\nThe `MeshGen` module currently supports 2 and 3 dimensional meshes. Higher dimensional meshing will be available in a future release; please contact the developer if you are interested in this functionality."
  },
  {
    "path": "help/meshslice.md",
    "content": "[comment]: # (Meshslice module help)\n[version]: # (0.5)\n\n# Meshslice\n[tagmeshslice]: # (meshslice)\n\nThe `meshslice` module is used to slice a `Mesh` object along a given plane, yielding a new `Mesh` object of lower dimensionality. You can also use `meshslice` to project `Field` objects onto the new mesh.\n\nTo use the module, begin by importing it:\n\n    import meshslice \n\nThen construct a `MeshSlicer` object, passing the mesh you want to slice in the constructor:\n\n    var slice = MeshSlicer(mesh)\n\nYou then perform a slice by calling the `slice` method, passing the plane you want to slice through. This method returns a new `Mesh` object comprising the slice. A plane is defined by a point that lies on the plane `pt` and a direction normal to the plan `dirn`:\n\n    var slc = slice.slice(pt, dirn)\n\nHaving performed a slice, you can then project any associated `Field` objects onto the sliced mesh by calling the `slicefield` method:\n\n    var phi = Field(mesh, fn (x,y,z) x+y+z)\n    var sphi = slice.slicefield(phi)\n\nThe new field returned by `slicefield` lives on the sliced mesh. You can slice any number of fields.\n\nYou can perform multiple slices with the same `MeshSlicer` simply by calling `slice` again with a different plane.\n\n## SlcEmpty\n[tagslcempty]: # (slcempty)\n\nThis error occurs if you try to use `slicefield` on a `MeshSlicer` without having performed a slice. For example:\n\n    var slice = MeshSlicer(mesh)\n    slice.slicefield(phi) // Throws SlcEmpty\n    slice.slice([0,0,0],[1,0,0]) \n\nTo fix, call `slice` before `slicefield`:\n\n    var slice = MeshSlicer(mesh)\n    slice.slice([0,0,0],[1,0,0]) \n    slice.slicefield(phi) // Now slices correctly \n"
  },
  {
    "path": "help/meshtools.md",
    "content": "[comment]: # (Morpho meshtools help file)\n[version]: # (0.5)\n\n# Meshtools\n[tagmeshtools]: # (meshtools)\n\nThe Meshtools package contains a number of functions and classes to assist with creating and manipulating meshes.\n\n[showsubtopics]: # (subtopics)\n\n## AreaMesh\n[tagareamesh]: # (areamesh)\n\nThis function creates a mesh composed of triangles from a parametric function. To use it:\n\n    var m = AreaMesh(function, range1, range2, closed=boolean)\n\nwhere\n\n* `function` is a parametric function that has one parameter. It should return a list of coordinates or a column matrix corresponding to this parameter.\n* `range1` is the Range to use for the first parameter of the parametric function.\n* `range2` is the Range to use for the second parameter of the parametric function.\n* `closed` is an optional parameter indicating whether to create a closed loop or not. You can supply a list where each element indicates whether the relevant parameter is closed or not.\n\nTo use `AreaMesh`, import the `meshtools` module:\n\n    import meshtools\n\nCreate a square:\n\n    var m = AreaMesh(fn (u,v) [u, v, 0], 0..1:0.1, 0..1:0.1)\n\nCreate a tube:\n\n    var m = AreaMesh(fn (u, v) [v, cos(u), sin(u)], -Pi...Pi:Pi/4,\n                     -1..1:0.1, closed=[true, false])\n\nCreate a torus:\n\n    var c=0.5, a=0.2\n    var m = AreaMesh(fn (u, v) [(c + a*cos(v))*cos(u),\n                                (c + a*cos(v))*sin(u),\n                                a*sin(v)], 0...2*Pi:Pi/16, 0...2*Pi:Pi/8, closed=true)\n\n## LineMesh\n[taglinemesh]: # (linemesh)\n\nThis function creates a mesh composed of line elements from a parametric function. To use it:\n\n    var m = LineMesh(function, range, closed=boolean)\n\nwhere\n\n* `function` is a parametric function that has one parameter. It should return a list of coordinates or a column matrix corresponding to this parameter.\n* `range` is the Range to use for the parametric function.\n* `closed` is an optional parameter indicating whether to create a closed loop or not.\n\nTo use `LineMesh`, import the `meshtools` module:\n\n    import meshtools\n\nCreate a circle:\n\n    import constants\n    var m = LineMesh(fn (t) [sin(t), cos(t), 0], 0...2*Pi:2*Pi/50, closed=true)\n\n## PolyhedronMesh\n[tagpolyhedronmesh]: # (polyhedron)\n\nThis function creates a mesh corresponding to a polyhedron. \n\n    var m = PolyhedronMesh(vertices, faces)\n\nwhere `vertices` is a list of vertices and `faces` is a list of faces specified as a list of vertex indices.\n\nTo use `PolyhedronMesh`, import the `meshtools` module:\n\n    import meshtools\n\nCreate a cube:\n\n    var m = PolyhedronMesh([ [-0.5, -0.5, -0.5], [ 0.5, -0.5, -0.5],\n                             [-0.5,  0.5, -0.5], [ 0.5,  0.5, -0.5],\n                             [-0.5, -0.5,  0.5], [ 0.5, -0.5,  0.5],\n                             [-0.5,  0.5,  0.5], [ 0.5,  0.5,  0.5]],\n                           [ [0,1,3,2], [4,5,7,6], [0,1,5,4], \n                             [3,2,6,7], [0,2,6,4], [1,3,7,5] ])\n\n*Note* that the vertices in each face list must be specified strictly in cyclic order.\n\n## DelaunayMesh\n[tagdelaunaymesh]: # (delaunaymesh)\n\nThe `DelaunayMesh` constructor function creates a `Mesh` object directly from a point cloud using the Delaunay triangulator.\n\n    var pts = []\n    for (i in 0...100) pts.append(Matrix([random(), random()]))\n    var m=DelaunayMesh(pts)\n    Show(plotmesh(m))\n\nYou can control the output dimension of the mesh (e.g. to create a 2D mesh embedded in 3D space) using the optional `outputdim` property. \n\n    var m = DelaunayMesh(pts, outputdim=3)\n\n## Equiangulate\n[tagequiangulate]: # (equiangulate)\n\nAttempts to equiangulate a mesh, exchanging elements to improve their regularity.\n\n    equiangulate(mesh)\n\nThis function takes optional arguments:\n\n* `quiet`: Set to true to silence messages.\n* `fix`: Supply a `Selection` containing edges that should not be modified by equiangulation.\n\n*Note* this function modifies the mesh in place; it does not create a new mesh.\n\n## ChangeMeshDimension\n[tagchangemeshdimension]: # (changemeshdimension)\n\nChanges the dimension in which a mesh is embedded. For example, you may have created a mesh in 2D that you now wish to use in 3D.\n\nTo use:\n\n    var new = ChangeMeshDimension(mesh, dim)\n\nwhere `mesh` is the mesh you wish to change, and `dim` is the new embedding dimension.\n\n## MeshBuilder\n[tagmeshbuiler]: # (meshbuilder)\n\nThe `MeshBuilder` class simplifies user creation of meshes. To use this class, begin by creating a `MeshBuilder` object:\n\n    var build = MeshBuilder()\n\nYou can then add vertices, edges, etc. one by one using `addvertex`, `addedge`, `addface` and `addelement`. Each of these returns an element id:\n\n    var id1=build.addvertex(Matrix([0,0,0]))\n    var id2=build.addvertex(Matrix([1,1,1]))\n    build.addedge([id1, id2])\n\nOnce the mesh is ready, call the `build` method to construct the `Mesh`:\n\n    var m = build.build()\n\nYou can specify the dimension of the `Mesh` explicitly when initializing the `MeshBuilder`: \n\n    var mb = MeshBuilder(dimension=2)\n\nor implicitly when adding the first vertex:\n\n    var mb = MeshBuilder() \n    mb.addvertex([0,1]) // A 2D mesh\n\n## MshBldDimIncnstnt\n[tagmshblddimincnstnt]: # (mshblddimincnstnt)\n\nThis error is produced if you try to add a vertex that is inconsistent with the mesh dimension, e.g.\n\n    var mb = MeshBuilder(dimension=2) \n    mb.addvertex([1,0,0]) // Throws an error! \n\nTo fix this ensure all vertices have the correct dimension.\n\n## MshBldDimUnknwn\n[tagmshblddimunknwn]: # (mshblddimunknwn)\n\nThis error is produced if you try to add an element to a `MeshBuilder` object but haven't yet specified the dimension (at initialization) or by adding a vertex.\n\n    var mb = MeshBuilder() \n    mb.addedge([0,1]) // No vertices have been added \n\nTo fix this add the vertices first.\n\n## MeshRefiner\n[tagmeshrefiner]: # (meshrefiner)\n\nThe `MeshRefiner` class is used to refine meshes, and to correct associated data structures that depend on the mesh.\n\nTo prepare for refining, first create a `MeshRefiner` object either with a `Mesh`,\n\n    var mr = MeshRefiner(mesh)\n\nor with a list of objects that can include a `Mesh` as well as `Field`s and `Selection`s.\n\n    var mr = MeshRefiner([mesh, field, selection ... ])\n\nTo perform the refinement, call the `refine` method. You can refine all elements,\n\n    var dict = mr.refine()\n\nor refine selected elements using a `Selection`,\n\n    var dict = mr.refine(selection=select)\n\nThe `refine` method returns a `Dictionary` that maps old objects to new, refined objects. Use this to update your data structures.\n\n    var newmesh = dict[oldmesh]\n\n## MeshPruner\n[tagmeshpruner]: # (meshpruner)\n\nThe `MeshPruner` class is used to prune excessive detail from meshes (a process that's sometimes referred to as coarsening), and to correct associated data structures that depend on the mesh.\n\nFirst create a `MeshPruner` object either with a `Mesh`,\n\n    var mp = MeshPruner(mesh)\n\nor with a list of objects that can include a `Mesh` as well as `Field`s and `Selection`s.\n\n    var mp = MeshPruner([mesh, field, selection ... ])\n\nTo perform the coarsening, call the `prune` method with a `Selection`,\n\n    var dict = mp.prune(select)\n\nThe `prune` method returns a `Dictionary` that maps old objects to new, refined objects. Use this to update your data structures.\n\n    var newmesh = dict[oldmesh]\n\n## MeshMerge\n[tagmeshmerge]: # (meshmerge)\n[tagmerge]: # (meshmerge)\n\nThe `MeshMerge` class is used to combine meshes into a single mesh, removing any duplicate elements.\n\nTo use, create a `MeshMerge` object with a list of meshes to merge,\n\n    var mrg = MeshMerge([m1, m2, m3, ... ])\n\nand then call the `merge` method to return a combined mesh:\n\n    var newmesh = mrg.merge()\n"
  },
  {
    "path": "help/modules.md",
    "content": "[comment]: # (Morpho modules help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# Modules\n[tagmodules]: # (modules)\n\nMorpho is extensible and provides a convenient module system that works like standard libraries in other languages. Modules may define useful variables, functions and classes, and can be made available using the `import` keyword. For example,\n\n    import color\n\nloads the `color` module that provides functionality related to color.\n\nYou can create your own modules; they're just regular morpho files that are stored in a standard place. On UNIX platforms, this is `/usr/local/share/morpho/modules`.\n\n[showsubtopics]: # (subtopics)\n\n## Import\n[tagimport]: # (import)\n[tagas]: # (as)\n\nImport provides access to the module system and including code from multiple source files.\n\nTo import code from another file, use import with the filename:\n\n    import \"file.morpho\"\n\nwhich immediately includes all the contents of `\"file.morpho\"`. Any classes, functions or variables defined in that file can now be used, which allows you to divide your program into multiple source files.\n\nMorpho provides a number of built in modules--and you can write your own--which can be loaded like this:\n\n    import color\n\nwhich imports the `color` module.\n\nYou can selectively import symbols from a modules by using the `for` keyword:\n\n    import color for HueMap, Red\n\nwhich imports only the `HueMap` class and the `Red` variable.\n\nYou can also import a module using the 'as' keyword to place the symbols in a specified namespace:\n\n    import color as col \n\nYou can then use refer to specific symbols like this: \n\n    print col.Red \n\n(See the help topic 'namespaces' for more information.)\n\n## Namespaces\n[tagnamespace]: # (namespace)\n[tagnamespaces]: # (namespaces)\n\nA namespace is a collection of symbols that is imported from a module. \nYou identify a namespace using the 'as' keyword when importing the module like this: \n\n    import color as col // 'col' is the namespace\n\nEverything defined by the module with a unique symbol, including classes, functions and global variables, can be identified using the namespace, e.g. \n\n    print col.Red \n\nSince the symbols are only are defined in the namespace you imported them into, you can't refer to them directly: \n\n    print Red \n\nUsing namespaces is recommended, becuase it helps prevent conflicts between modules.\n"
  },
  {
    "path": "help/optimize.md",
    "content": "[comment]: # (Morpho optimize help file)\n[version]: # (0.5)\n\n# Optimize\n[tagoptimize]: # (optimize)\n\nThe `optimize` package contains a number of functions and classes to perform shape optimization.\n\n[showsubtopics]: # (subtopics)\n\n## OptimizationProblem\n[tagoptimizationproblem]: # (optimizationproblem)\n\nAn `OptimizationProblem` object defines an optimization problem, which may include functionals to optimize as well as global and local constraints.\n\nCreate an `OptimizationProblem` with a mesh:\n\n    var problem = OptimizationProblem(mesh)\n\nAdd an energy:\n\n    var la = Area()\n    problem.addenergy(la)\n\nAdd an energy that operates on a selected region, and with an optional prefactor:\n\n    problem.addenergy(la, selection=sel, prefactor=2)\n\nAdd a constraint:\n\n    problem.addconstraint(la)\n\nAdd a local constraint (here a onesided level set constraint):\n\n    var ls = ScalarPotential(fn (x,y,z) z, fn (x,y,z) Matrix([0,0,1]))\n    problem.addlocalconstraint(ls, onesided=true)\n\n## Optimizer\n[tagoptimizer]: # (optimizer)\n\n`Optimizer` objects are used to optimize `Mesh`es and `Field`s. You should use the appropriate subclass: `ShapeOptimizer` or `FieldOptimizer` respectively.\n\n[showsubtopics]: # (subtopics)\n\n## ShapeOptimizer\n[tagshapeoptimizer]: # (shapeoptimizer)\n\nA `ShapeOptimizer` object performs shape optimization: it moves the vertex positions to reduce an overall energy.\n\nCreate a `ShapeOptimizer` object with an `OptimizationProblem` and a `Mesh`:\n\n    var sopt = ShapeOptimizer(problem, m)\n\nTake a step down the gradient with fixed stepsize:\n\n    sopt.relax(5) // Takes five steps\n\nLinesearch down the gradient:\n\n    sopt.linesearch(5) // Performs five linesearches\n\nPerform conjugate gradient (usually gives faster convergence):\n\n    sopt.conjugategradient(5) // Performs five conjugate gradient steps.\n\nControl a number of properties of the optimizer:\n\n    sopt.stepsize=0.1 // The stepsize to take\n    sopt.steplimit=0.5 // Maximum stepsize for optimizing methods\n    sopt.etol = 1e-8 // Energy convergence tolerance\n    sopt.ctol = 1e-9 // Tolerance to which constraints are satisfied\n    sopt.maxconstraintsteps = 20 // Maximum number of constraint steps to use\n\n## FieldOptimizer\n[tagfieldoptimizer]: # (fieldoptimizer)\n\nA `FieldOptimizer` object performs field optimization: it changes elements of a `Field` to reduce an overall energy.\n\nCreate a `FieldOptimizer` object with an `OptimizationProblem` and a `Field`:\n\n    var sopt = FieldOptimizer(problem, fld)\n\nField optimizers provide the same options and methods as Shape optimizers: see the `ShapeOptimizer` documentation for details.\n"
  },
  {
    "path": "help/plot.md",
    "content": "[comment]: # (Plot module help)\n[version]: # (0.5.4)\n\n# Plot\n[tagplot]: # (plot)\n\nThe `plot` module provides visualization capabilities for Meshes, Selections and Fields. These functions produce Graphics objects that can be displayed with `Show`.\n\nTo use the module, first import it:\n\n    import plot\n\n[showsubtopics]: # (subtopics)\n\n## Plotmesh\n[tagplotmesh]: # (plotmesh)\n\nVisualizes a `Mesh` object:\n\n    var g = plotmesh(mesh)\n\nPlotmesh accepts a number of optional arguments to control what is displayed:\n\n* `selection` - Only elements in a provided `Selection` are drawn.\n* `grade` - Only draw the specified grade. This can also be a list of multiple grades to draw.\n* `color` - Draw the mesh in a provided `Color`.\n* `filter` and `transmit` - Used by the `povray` module to indicate transparency.\n\n## Plotmeshlabels\n[tagplotmeshlabels]: # (plotmeshlabels)\n\nDraws the ids for elements in a `Mesh`: \n\n    var g = plotmeshlabels(mesh) \n\nPlotmeshlabels accepts a number of optional arguments to control the output: \n\n* `grade` - Only draw the specified grade. This can also be a list of multiple grades to draw.\n* `selection` - Only labels in a provided `Selection` are drawn.\n* `offset` - Local offset vector for labels. Can be a `List`, a `Matrix` or a function. \n* `dirn` - Text direction for labels. Can be a `List`, a `Matrix` or a function. \n* `vertical` - Text vertical direction. Can be a `List`, a `Matrix` or a function. \n* `color` - Label color. Can be a `Color` object or a `Dictionary` of colors for each grade. \n* `fontsize` - Font size to use. \n\n## Plotselection\n[tagplotselection]: # (plotselection)\n\nVisualizes a `Selection` object:\n\n    var g = plotselection(mesh, sel)\n\nPlotselection accepts a number of optional arguments to control what is displayed:\n\n* `grade` - Only draw the specified grade. This can also be a list of multiple grades to draw.\n* `filter` and `transmit` - Used by the `povray` module to indicate transparency.\n\n## Plotfield\n[tagplotfield]: # (plotfield)\n\nVisualizes a scalar `Field` object:\n\n    var g = plotfield(field)\n\nPlotfield accepts a number of optional arguments to control what is displayed:\n\n* `grade` - Draw the specified grade.\n* `colormap` - A `Colormap` object to use. The field is automatically scaled.\n* `scalebar` - A `Scalebar` object to use. \n* `selection` - Only elements in a provided `Selection` are drawn.\n* `style` - Plot style. See below. \n* `filter` and `transmit` - Used by the `povray` module to indicate transparency.\n* `cmin` and `cmax` - Can be used to define the data range covered.\n  Values beyond these limits will be colored by the lower/upper bound of\n  the colormap accordingly.\n\nSupported plot styles: \n\n* `default` - Color `Mesh` elements by the corresponding value of the `Field`.\n* `interpolate` - Interpolate `Field` quantities onto higher elements.\n\n## ScaleBar\n[tagscalebar]: # (scalebar)\n\nRepresents a scalebar for a plot: \n\n    Show(plotfield(field, scalebar=ScaleBar(posn=[1.2,0,0])))\n\n`ScaleBar`s can be created with many adjustable parameters: \n\n* `nticks` - Maximum number of ticks to show.  \n* `posn` - Position to draw the `ScaleBar`. \n* `length` - Length of `ScaleBar` to draw. \n* `dirn` - Direction to draw the `ScaleBar` in. \n* `tickdirn` - Direction to draw the ticks in. \n* `colormap` - `ColorMap` to use.\n* `textdirn` - Direction to draw labels in. \n* `textvertical` - Label vertical direction. \n* `fontsize` - Fontsize for labels\n* `textcolor` - Color for labels \n\nYou can draw the `ScaleBar` directly by calling the `draw` method: \n\n    sb.draw(min, max)\n\nwhere `min` and `max` are the minimum and maximum values to display on the scalebar. "
  },
  {
    "path": "help/povray.md",
    "content": "[comment]: # (Povray module help)\n[version]: # (0.5)\n\n# POVRay\n[tagpovray]: # (povray)\n\nThe `povray` module provides integration with POVRay, a popular open source ray-tracing package for high quality graphical rendering. To use the module, first import it:\n\n    import povray\n\nTo raytrace a graphic, begin by creating a `POVRaytracer` object:\n\n    var pov = POVRaytracer(graphic)\n\nCreate a .pov file that can be run with POVRay:\n\n    pov.write(\"out.pov\")\n\nCreate, render and display a scene using POVRay:\n\n    pov.render(\"out.pov\")\n\nThis also creates the .png file for the scene.\n\nThe `POVRaytracer` constructor supports an optional `camera` argument:\n\n* `camera` - a `Camera` object (see below / help) containing the\n  settings for the povray camera.\n\nThe `Camera` object can be initialized as follows:\n\n    var camera = Camera()\n\nThis object contains the default settings of the camera, which can be\nchanged using the following optional arguments, or by just setting the\nattributes after instantiation:\n\n* `antialias` - whether to antialias the output or not (`true` by default)\n* `width` - image width (`2048` by default)\n* `height` - image height (`1536` by default)\n* `viewangle` - camera angle (higher means wider view) (`24` by default)\n* `viewpoint` - position of camera (`Matrix([0,0,-5])` by default)\n* `look_at` - coordinate to look at (`Matrix([0,0,0])` by defualt)\n* `sky` - orientation pointing to the sky (`Matrix([0,1,0])` by default)\n\nThe default settings generate a reasonable centered view of the x-y\nplane.\n\nThese attributes can also be set directly for the `POVRaytracer` object:\n\n    pov.look_at = Matrix([0,0,1])\n\nThe `render` method supports a few optional boolean arguments:\n\n* `quiet` - whether to suppress the parser and render statistics from `povray` or not (`false` by default)\n* `display` - whether to turn on the graphic display while rendering or not (`true` by default) \n* `shadowless` - whether to turn off the shadows while rendering (`false` by default)\n* `transparent` - whether to render the graphic with a transparent background in the output PNG (`false` by default)\n\n# Camera\n[tagcamera]: # (camera)\n\nThe `Camera` object can be initialized as follows:\n\n    var camera = Camera()\n\nThis object contains the default settings of the camera, which can be\nchanged using the following optional arguments, or by just setting the\nattributes after instantiation:\n\n* `antialias` - whether to antialias the output or not (`true` by default)\n* `width` - image width (`2048` by default)\n* `height` - image height (`1536` by default)\n* `viewangle` - camera angle (higher means wider view) (`24` by default)\n* `viewpoint` - position of camera (`Matrix([0,0,-5])` by default)\n* `look_at` - coordinate to look at (`Matrix([0,0,0])` by defualt)\n* `sky` - orientation pointing to the sky (`Matrix([0,1,0])` by default)\n\n    camera.sky = Matrix([0,0,1])\n\nThe default settings generate a reasonable centered view of the x-y\nplane.\n"
  },
  {
    "path": "help/range.md",
    "content": "[comment]: # (Morpho range help file)\n[version]: # (0.5)\n\n# Range\n[tagrange]: # (range)\n\nRanges represent a sequence of numerical values. There are two ways to create them depending on whether the upper value is included or not:\n\n    var a = 1..5  // inclusive version, i.e. [1,2,3,4,5]\n    var b = 1...5 // exclusive version, i.e. [1,2,3,4]\n\nBy default, the increment between values is 1, but you can use a different value like this:\n\n    var a = 1..5:0.5 // 1 - 5 with an increment of 0.5.\n\nYou can also create Range objects using the appropriate constructor function:\n\n    var a = Range(1,5,0.5)\n\nRanges are particularly useful in writing loops:\n\n    for (i in 1..5) print i\n\nThey can easily be converted to a list of values:\n\n    var c = List(1..5)\n\nTo find the number of elements in a Range, use the `count` method\n\n    print (1..5).count()\n\n[showmethodsrange]: #\n"
  },
  {
    "path": "help/requirements.txt",
    "content": "# File: docs/requirements.txt\n\n# Defining the exact version will make sure things don't break\nsphinx>=5.0.0\nsphinx_rtd_theme>=0.5.1\nmyst-parser>=0.13.5\nJinja2<3.1\nrecommonmark\n"
  },
  {
    "path": "help/selection.md",
    "content": "[comment]: # (Morpho selection class help file)\n[version]: # (0.5)\n\n# Selection\n[tagselection]: # (selection)\n\nThe Selection class enables you to select components of a mesh for later use. You can supply a function that is applied to the coordinates of every vertex in the mesh, or select components like boundaries.\n\nCreate an empty selection:\n\n    var s = Selection(mesh)\n\nSelect vertices above the z=0 plane using an anonymous function:\n\n    var s = Selection(mesh, fn (x,y,z) z>0)\n\nSelect the boundary of a mesh:\n\n    var s = Selection(mesh, boundary=true)\n\nSelection objects can be composed using set operations:\n\n    var s = s1.union(s2)\n\nor\n    var s = s1.intersection(s2)\n\nTo add additional grades, use the addgrade method. For example, to add areas:\n    s.addgrade(2)\n\n[showsubtopics]: # subtopics\n\n## addgrade\n[tagaddgrade]: # (addgrade)\nAdds elements of the specified grade to a Selection. For example, to add edges to an existing selection, use\n\n    s.addgrade(1)\n\nBy default, this only adds an element if *all* vertices in the element are currently selected. Sometimes, it's useful to be able to add elements for which only some vertices are selected. The optional argument `partials` allows you to do this:\n\n    s.addgrade(1, partials=true)\n\nNote that this method modifies the existing selection, and does not generate a new Selection object.\n\n## removegrade\n[tagremovegrade]: # (removegrade)\nRemoves elements of the specified grade from a Selection. For example, to remove edges from an existing selection, use\n\n    s.removegrade(1)\n\nNote that this method modifies the existing selection, and does not generate a new Selection object.\n\n## idlistforgrade\n[tagidlistforgrade]: # (idlistforgrade)\nReturns a list of element ids included in the selection.\n\nTo find out which edges are selected:\n\n    var edges = s.idlistforgrade(1)\n\n## isselected\n[tagisselected]: # (isselected)\nChecks if an element id is selected, returning `true` or `false` accordingly.\n\nTo check if edge number 5 is selected:\n\n    var f = s.isselected(1, 5))\n"
  },
  {
    "path": "help/sparse.md",
    "content": "[comment]: # (Sparse class help)\n[version]: # (0.5)\n\n# Sparse\n[tagsparse]: # (Sparse)\n\nThe Sparse class provides support for sparse matrices. An empty sparse matrix can be initialized with a given size,\n\n    var a = Sparse(nrows,ncols)\n\nAlternatively, a matrix can be created from an array of triplets,  \n\n    var a = Sparse([[row, col, value] ...])\n\nFor example,\n\n    var a = Sparse([[0,0,2], [1,1,-2]])\n\ncreates the matrix\n\n    [ 2 0 ]\n    [ 0 -2 ]\n\nOnce a sparse matrix is created, you can use all the regular arithmetic operators with matrix operands, e.g.\n\n    a+b\n    a*b\n"
  },
  {
    "path": "help/string.md",
    "content": "[comment]: # (String class help)\n[version]: # (0.5)\n\n# String\n[tagstring]: # (String)\n\nStrings represent textual information. They are written in Morpho like this:\n\n    var a = \"hello world\"\n\nUnicode characters including emoji are supported.\n\nYou can also create strings using the constructor function `String`, which takes any number of parameters:\n\n    var a = String(\"Hello\", \"World\")\n\nA very useful feature, called *string interpolation*, enables the results of any morpho expression can be interpolated into a string. Here, the values of `i` and `func(i)` will be inserted into the string as it is created:\n\n    print \"${i}: ${func(i)}\"\n\nTo get an individual character, use index notatation\n\n    print \"morpho\"[0]\n\nYou can loop over each character like this:\n\n    for (c in \"morpho\") print c\n\nNote that strings are immutable, and hence\n\n    var a = \"morpho\"\n    a[0] = 4\n\nraises an error.\n\n[showsubtopics]: #\n\n## split\n[tagsplit]: # (split)\n\nThe split method splits a String into a list of substrings. It takes one argument, which is a string of characters to use to split the string:\n\n    print \"1,2,3\".split(\",\")\n\ngives\n\n    [ 1, 2, 3 ]\n"
  },
  {
    "path": "help/syntax.md",
    "content": "[comment]: # (Morpho syntax help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# Syntax\n[tagsyntax]: # (syntax)\n\nMorpho provides a flexible object oriented language similar to other languages in the C family (like C++, Java and Javascript) with a simplified syntax.\n\nMorpho programs are stored as plain text with the .morpho file extension. A program can be run from the command line by typing\n\n    morpho5 program.morpho\n\n## Comments\n[tagcomment]: # (comment)\n[tagcomments]: # (comments)\n[tagcommentvar]: # (//)\n[tagcommentvarr]: # (/*)\n[tagcommentvarrr]: # (*/)\nTwo types of comment are available. The first type is called a 'line comment' whereby text after `//` on the same line is ignored by the interpreter.\n\n    a.dosomething() // A comment\n\nLonger 'block' comments can be created by placing text between `/*` and `*/`. Newlines are ignored\n\n    /* This\n       is\n       a longer comment */\n\nIn contrast to C, these comments can be nested\n\n    /* A nested /* comment */ */\n\nenabling the programmer to quickly comment out a section of code.\n\n## Symbols\n[tagsymbols]: # (symbols)\n[tagnames]: # (names)\n\nSymbols are used to refer to named entities, including variables, classes, functions etc. Symbols must begin with a letter or underscore _ as the first character and may include letters or numbers as the remainder. Symbols are case sensitive.\n\n    asymbol\n    _alsoasymbol\n    another_symbol\n    EvenThis123\n    YET_ANOTHER_SYMBOL\n\nClasses are typically given names with an initial capital letter. Variable names are usually all lower case.\n\n## Newlines\n[tagnewlines]: # (newlines)\n[tagnewline]: # (newline)\n\nStrictly, morpho ends statements with semicolons like C, but in practice these are usually optional and you can just start a new line instead. For example, instead of\n\n    var a = 1; // The ; is optional\n\nyou can simply use\n\n    var a = 1\n\nIf you want to put several statements on the same line, you can separate them with semicolons:\n\n    var a = 1; print a\n\nThere are a few edge cases to be aware of: The morpho parser works by accepting a newline anywhere it expects to find a semicolon. To split a statement over multiple lines, signal to morpho that you plan to continue by leaving the statement unfinished. Hence, do this:\n\n    print a +\n          1\n\nrather than this:\n\n    print a   // < Morpho thinks this is a complete statement\n          + 1 // < and so this line will cause a syntax error\n\n\n## Booleans\n[tagtrue]: # (true)\n[tagfalse]: # (false)\n[tagbooleans]: # (true)\n\nComparison operations like `==`, `<` and `>=` return `true` or `false` depending on the result of the comparison. For example,\n\n    print 1==2\n\nprints `false`. The constants `true` or `false` are provided for you to use in your own code:\n\n    return true\n\n## Nil\n[tagnil]: # (nil)\n\nThe keyword `nil` is used to represent the absence of an object or value.\n\nNote that in `if` statements, a value of `nil` is treated like `false`.\n\n    if (nil) {\n        // Never executed.\n    }\n\n## Blocks\n[tagblocks]: # (blocks)\n[tagblock]: # (block)\n\nCode is divided into *blocks*, which are delimited by curly brackets like this:\n\n    {\n      var a = \"Hello\"\n      print a\n    }\n\nThis syntax is used in function declarations, loops and conditional statements.\n\nAny variables declared within a block become *local* to that block, and cannot be seen outside of it. For example,\n\n    var a = \"Foo\"\n    {\n      var a = \"Bar\"\n      print a\n    }\n    print a\n\nwould print \"Bar\" then \"Foo\"; the version of `a` inside the code block is said to *shadow* the outer version.\n\n## Precedence\n[tagprecedence]: # (precedence)\n\nPrecedence refers to the order in which morpho evaluates operations. For example,\n\n    print 1+2*3\n\nprints `7` because `2*3` is evaluated before the addition; the operator `*` is said to have higher precedence than `+`.\n\nYou can always modify the order of evaluation by using parentheses:  \n\n    print (1+2)*3 // prints 9\n\n## Print\n[tagprint]: # (print)\n\nThe `print` keyword is used to print information to the console. It can be followed by any value, e.g.\n\n    print 1\n    print true\n    print a\n    print \"Hello\"\n"
  },
  {
    "path": "help/system.md",
    "content": "[comment]: # (System help)\n[version]: # (0.5)\n\n# System\n[tagsystem]: # (system)\n\nThe `System` class provides information and access to some features of the runtime environment. \n\n[showsubtopics]: # (subtopics)\n\n## Platform\n[tagplatform]: # (platform)\n\nDetect which platform morpho was compiled for: \n\n    print System.platform() \n\nwhich returns `\"macos\"`, `\"linux\"`, `\"unix\"` or `\"windows\"`. \n\n## Version\n[tagversion]: # (version)\n\nFind the current version of morpho: \n\n    print System.version() \n\n## Clock\n[tagclock]: # (clock)\n\nReturns the system time in seconds, with at least millisecond granularity. Primarily intended for timing:\n\n    var start=System.clock() \n    // Do something \n    print System.clock()-start \n\nNote that `System.clock` measures the actual physical time elapsed, not the time spent in a process. \n\n## Sleep\n[tagsleep]: # (sleep)\n\nPauses the program for a specified number of seconds:\n\n    System.sleep(0.5) // Sleep for half a second\n\n## Readline\n[tagreadline]: # (readline)\n\nReads a line of input from the console:\n\n    var in = System.readline() \n\n## Arguments\n[tagargunents]: # (arguments)\n\nReturns a `List` of arguments passed to the current morpho on the command line. \n\n    var args = System.arguments() \n    for (e in args) print e \n\nRun a morpho program with arguments: \n\n    morpho5 program.morpho hello world\n\nNote that, in line with UNIX conventions, command line arguments before the program file name are passed to the `morpho5` runtime; those after are passed to the morpho program via `System.arguments`. \n\n## Exit\n[tagexit]: # (exit)\n\nStop execution of a program:\n\n    System.exit() \n"
  },
  {
    "path": "help/tuple.md",
    "content": "[comment]: # (Tuple class help)\n[version]: # (0.6.0)\n\n# Tuple\n[tagtuple]: # (Tuple)\n\nTuples are collection objects that contain a sequence of values each associated with an integer index. Unlike Lists, they can't be changed after creation.\n\nCreate a tuple like this:\n\n    var tuple = (1, 2, 3)\n\nLook up values using index notation:\n\n    tuple[0]\n\nIndexing can also be done with slices:\n\n\ttuple[0..2]\n\nLoop over elements of a tuple:\n\n    for (i in tuple) print i\n\n[showsubtopics]: # (subtopics)\n\n## ismember\n[tagismember]: # (ismember)\n\nTests if a value is a member of a tuple:\n\n    var tuple = (1,2,3)\n    print tuple.ismember(1) // expect: true\n\n## Join\n[tagjoin]: # (join)\n\nJoin two lists together:\n\n    var t1 = (1,2,3), t2 = (4, 5, 6)\n    print t1.join(t2) // expect: (1,2,3,4,5,6)\n"
  },
  {
    "path": "help/values.md",
    "content": "[comment]: # (Values help)\n[version]: # (0.5)\n\n# Values\n[tagvalues]: # (values)\n\nValues are the basic unit of information in morpho: All functions in morpho accept values as arguments and return values. \n\n[showsubtopics]: # (subtopics)\n\n## Int\n[tagint]: # (int)\n\nMorpho provides integers, which work as you would expect in other languages, although you rarely need to worry about the distinction between floats and integers. \n\nConvert a floating point number to an Integer: \n\n    print Int(1.3) // expect: 1\n\nConvert a string to an integer:\n\n    print Int(\"10\")+1 // expect: 11\n\n## Float\n[tagfloat]: # (float)\n\nMorpho provides double precision floating point numbers. \n\nConvert a string to a floating point number:\n\n    print Float(\"1.2e2\")+1 // expect: 121\n\n## Ceil\n[tagceil]: # (ceil)\n\nReturns the smallest integer larger than or equal to its argument:\n\n    print ceil(1.3) // expect: 2\n\n## Floor\n[tagfloor]: # (floor)\n\nReturns the largest integer smaller than or equal to its argument:\n\n    print floor(1.3) // expect: 1\n\n## Format\n[tagformat]: # (format)\n\nThe format method converts a number to a `String` using a given format specifier: \n\n    print (1/3).format(\"%4.2g\") // Outputs 0.33\n\nThe specifier must begin with '%' and may include: \n\n* A minimum width, given as an integer. \n* Number of decimal places to show, with '.' in front.\n* A formatting option, either 'f' or 'g' where:\n    - 'f' displays the number in decimal form, e.g. 0.01\n    - 'g' uses scientific notation, e.g. 1e-2\n\nThe syntax for the formatting string is similar to that used in C and Python.\n"
  },
  {
    "path": "help/variables.md",
    "content": "[comment]: # (Morpho variables help file)\n[version]: # (0.5)\n\n[toplevel]: #\n\n# Variables\n[tagvariables]: # (variables)\n[tagvar]: # (var)\n\nVariables are defined using the `var` keyword followed by the variable name:\n\n    var a\n\nOptionally, an initial assignment may be given:\n\n    var a = 1\n\nVariables defined in a block of code are visible only within that block, so\n\n    var greeting = \"Hello\"\n    {\n        var greeting = \"Goodbye\"\n        print greeting\n    }\n    print greeting\n\nwill print\n\n*Goodbye*\n*Hello*\n\nMultiple variables can be defined at once by separating them with commas\n\n    var a, b=2, c[2]=[1,2]\n\nwhere each can have its own initializer (or not).\n\n## Indexing\n[taglb]: # ([)\n[tagrb]: # (])\n[tagindex]: # (index)\n[tagsub]: # (subscript)\n\nMorpho provides a number of collection objects, such as `List`, `Range`, `Array`, `Dictionary`, `Matrix` and `Sparse`, that can contain more than one value. Index notation (sometimes called subscript notation) is used to access elements of these objects.\n\nTo retrieve an item from a collection, you use the `[` and `]` brackets like this:\n\n    var a = List(\"Apple\", \"Bag\", \"Cat\")\n    print a[0]\n\nwhich prints *Apple*. Note that the first element is accessed with `0` not `1`.\n\nSimilarly, to set an entry in a collection, use:\n\n    a[0]=\"Adder\"\n\nwhich would replaces the first element in `a` with `\"Adder\"`.\n\nSome collection objects need more than one index,\n\n    var a = Matrix([[1,0],[0,1]])\n    print a[0,0]\n\nand others such as `Dictionary` use non-numerical indices,\n\n    var b = Dictionary()\n    b[\"Massachusetts\"]=\"Boston\"\n    b[\"California\"]=\"Sacramento\"\n\nas in this dictionary of state capitals.\n"
  },
  {
    "path": "help/vtk.md",
    "content": "[comment]: # (Morpho vtk module help file)\n[version]: # (0.5)\n\n# VTK\n[tagvtk]: # (vtk)\n\nThe vtk module contains classes to allow I/O of meshes and fields using\nthe VTK Legacy Format. Note that this currently only supports scalar or 2D/3D vector (column matrix) fields that live on the vertices ( shape `[1,0,0]`). Support for\ntensorial fields and fields on cells coming soon.\n\n[showsubtopics]: # (subtopics)\n\n## VTKExporter\n[tagvtkexporter]: # (VTKExporter)\n\nThis class can be used to export the field(s) and/or a mesh at a given state\nto a single .vtk file. To use it, import the `vtk` module:\n\n    import vtk\n\nInitialize the `VTKExporter`\n\n    var vtkE = VTKExporter(obj)\n\nwhere `obj` can either be\n\n* A `Mesh` object: This prepares the Mesh for exporting.\n* A `Field` object: This prepares both the Field and the Mesh associated\n  with it for exporting.\n\nUse the `export` method to export to a VTK file. \n\n    vtkE.export(\"output.vtk\")\n \nOptionally, use the `addfield` method to add one or more fields before\nexporting:\n\n    vtkE.addfield(f, fieldname=\"f\")\n\nwhere,\n\n* `f` is the field object to be exported\n* `fieldname` is an optional argument that assigns a name to the field\n  in the VTK file. This name is required to be a character\n  string without embedded whitespace. If not provided, the name would be\n  either \"scalars\" or \"vectors\" depending on the field type**. \n\n** Note that this currently only supports scalar or 2D/3D vector (column\nmatrix) fields that live on the vertices ( shape `[1,0,0]`). Support for\ntensorial fields and fields on cells coming soon.\n\nMinimal example:\n\n    import vtk\n    import meshtools\n\n    var m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\n    var vtkE = VTKExporter(m1) // Export just the mesh \n    \n    vtkE.export(\"mesh.vtk\")\n\n    var f1 = Field(m1, fn(x,y,z) x)\n\n    var g1 = Field(m1, fn(x,y,z) Matrix([x,2*x,3*x]))\n\n    vtkE = VTKExporter(f1, fieldname=\"f\") // Export fields\n\n    vtkE.addfield(g1, fieldname=\"g\")\n\n    vtkE.export(\"data.vtk\")\n\n## VTKImporter\n[tagvtkimporter]: # (VTKImporter)\n\nThis class can be used to import the field(s) and/or the  mesh at a\ngiven state from a single .vtk file. To use it, import the `vtk` module:\n\n    import vtk\n\nInitialize the `VTKImporter` with the filename\n\n    var vtkI = VTKImporter(\"output.vtk\")\n\nUse the `mesh` method to get the mesh:\n\n    var mesh = vtkI.mesh()\n\nUse the `field` method to get the field:\n\n    var f = vtkI.field(fieldname)\n\nUse the `fieldlist` method to get the list of the names of the fields contained in the file:\n\n    print vtkI.fieldlist()\n\nUse the `containsfield` method to check whether the file contains a field by a given `fieldname`:\n\n    if (tkI.containsfield(fieldname)) {\n        ... \n    }\n\nwhere `fieldname` is the name assigned to the field in the .vtk file\n\nMinimal example:\n\n    import vtk\n    import meshtools \n\n    var vtkI = VTKImporter(\"data.vtk\")\n\n    var m = vtkI.mesh()\n\n    var f = vtkI.field(\"f\")\n\n    var g = vtkI.field(\"g\")\n\n"
  },
  {
    "path": "modules/color.morpho",
    "content": "/* Color module */\n\nimport constants\n\nclass Color {\n  init(r,g,b) {\n    self.r = r\n    self.g = g\n    self.b = b\n  }\n  red(x) { return self.r }\n  green(x) { return self.g }\n  blue(x) { return self.b }\n  rgb(x) { return [self.r, self.g, self.b] }\n}\n\n// Some predefined colors\nvar Red = Color(1,0,0)\nvar Green = Color(0,1,0)\nvar Blue = Color(0,0,1)\nvar White = Color(1,1,1)\nvar Black = Color(0,0,0)\nvar Cyan = Color(0,1,1)\nvar Magenta = Color(1,0,1)\nvar Yellow = Color(1,1,0)\nvar Brown = Color(0.6,0.4,0.2)\nvar Orange = Color(1,0.5,0)\nvar Pink = Color(1,0.5,0.5) \nvar Purple = Color(0.5,0,0.5)\n\nfn Gray(x) {\n  return Color(x,x,x)\n}\n\n/* Basic color maps */\n\nclass ColorMap {\n  init() { }\n  rgb(x) { return [self.red(x), self.green(x), self.blue(x)] }\n}\n\nclass GradientMap is ColorMap {\n  red(x) { return 0.29+0.04*x+0.66*x^2 }\n  green(x) { return 0.33+0.42*x+0.24*x^2 }\n  blue(x) { return 0.46-0.55*x+x^2 }\n}\n\nclass GrayMap is ColorMap {\n  red(x) { return x }\n  green(x) { return x }\n  blue(x) { return x }\n}\n\nclass HueMap is ColorMap {\n  red(x) { return (sin(2*Pi*(x+1/4))+1)/2 }\n  green(x) { return (sin(2*Pi*x)+1)/2 }\n  blue(x) { return (sin(2*Pi*(x-0.4))+1)/2 }\n}\n\n/* Color maps expanded onto Chebyshev polynomials */\n\nvar _errChebyShevOrderInconsistent = Error(\"ClrChbyshvOrdr\", \"Order of Chebyshev approximation inconsistent.\") \n\nclass ChebyshevMap is Color {\n  init(R,G,B) {\n    self.R = R \n    self.G = G\n    self.B = B\n    self.order = R.count() \n    if (G.count()!=self.order || B.count()!=self.order) _errChebyShevOrderInconsistent.throw()  \n  }\n\n  _chebyshev(t) {\n    var n = self.order\n    var c[n]\n    c[0]=1\n    c[1]=t\n    for (var i=2; i<n; i+=1) c[i]=2*t*c[i-1]-c[i-2]\n    return c \n  } \n\n  _component(x, table) {\n    var r=0\n    var c = self._chebyshev(2*x-1)\n    for (var i=0; i<self.order; i+=1) r+=c[i]*table[i]\n    return r\n  }\n\n  red(x) { return self._component(x, self.R) }\n  green(x) { return self._component(x, self.G) }\n  blue(x) { return self._component(x, self.B) }\n\n  rgb(x) { \n    var r=0,g=0,b=0\n    var c = self._chebyshev(2*x-1)\n  \n    for (var i=0; i<self.order; i+=1) {\n      r+=c[i]*self.R[i]\n      g+=c[i]*self.G[i]\n      b+=c[i]*self.B[i]\n    }\n\n    return [r,g,b]\n  }\n}\n\n/* Colormaps corresponding to the MPL colormaps:\n   http://bids.github.io/colormap/ \n   Here approximated by an expension in Chebyshev polynomials */\n\nclass ViridisMap is ChebyshevMap {\n  init() {\n    super.init(\n      [0.4088232360482251,0.29914951760096953,0.2552612533555949,0.08113280407487648,\n   -0.03283479810992292,-0.018577351334051494,-0.0013733172532640688,\n   0.008117622664142014,0.002682386480442547], \n      [0.5186919867937394,0.4546669273087233,-0.057431691996312643,-0.0048541541888066884,\n   -0.00853841982404364,0.0007461158760058021,0.002057901604696167,0.0008255497724669167,\n   -0.0004372356619719562], \n      [0.3760062927570685,-0.1469374708776625,-0.1704154003941636,0.02090430542528263,\n   0.01510258354582896,0.028525837271586568,0.015009580468626441,0.004431992972452413,\n   0.004574724100591803]) \n  }\n}\n\nclass MagmaMap is ChebyshevMap {\n  init() {\n    super.init(\n      [0.5907866331910908,0.5512722667670491,-0.11869056394186639,-0.06195808758016048,\n   0.016174879132276762,0.007825552776936756,0.007068023322676562,-0.005405791682135656,\n   -0.004289466821398506,0.0022896263057962762],\n      [0.36985521972760765,0.4798725290845796,0.14385739700946584,0.020144429166912035,\n   -0.01966766336743611,-0.0077647799258502605,-0.0036190850643826634,\n   0.007536496657360997,0.0041760071789883136,-0.008101991902176933],\n      [0.4146808525196039,0.24602997042802613,-0.042495725202417074,0.15341588488121402,\n   0.00805152051307142,-0.02717096384766588,0.00206961352997524,-0.007127547734556673,\n   0.010044740905955626,0.005726967451785381])\n  }\n}\n\nclass InfernoMap is ChebyshevMap {\n  init() {\n    super.init(\n      [0.5907686152333599,0.5320419550621178,-0.13225772363892413,-0.05035373817114475,\n   0.020359465701464984,0.008683202802651958,0.013948075520974724,0.0012246769354433988,\n   0.003564440313191328,0.002701158080654896,-0.000037446824389489684],\n      [0.3770580923537784,0.4998367210000978,0.14598165233845511,0.006395013608917966,\n   -0.019063425717928677,-0.006515765659684996,-0.005998826303670322,\n   0.003984370877431411,-0.0008735684997286814,-0.004013362650016195,\n   0.0037589348986688206],\n      [0.28127942085373586,0.10733379064741418,0.007259209326938936,0.23027957313656874,\n   0.05122756917452243,-0.0030648367795109203,-0.009391178685969673,-0.02397409152798162,\n   0.0029394603983608457,0.003505946325128027,0.004817080753431645])\n  }\n}\n\nclass PlasmaMap is ChebyshevMap {\n  init() {\n    super.init(\n      [0.6564978820568728,0.4513005610828885,-0.1474582018543978,-0.012551792337212387,\n   -0.008412202382930323,-0.00023645100725124183,-0.0017694446884859137,\n   0.003097797063542014],\n      [0.37647105842658246,0.49541894108110207,0.119596669297474,-0.02693358423260891,\n   0.010579792365637941,0.007309311031239827,-0.011188674500084088,0.004301686509917261],\n      [0.410228609925941,-0.26056803832126973,-0.07193502017900728,0.0713778851634931,\n   0.0007021465182023984,0.00034857192848406334,0.0088834869199995,-0.00152187260983132]\n    )\n  }\n}"
  },
  {
    "path": "modules/constants.morpho",
    "content": "/*\n * Constants\n * Various mathematical and Physical constants\n */\n\nvar Pi = 3.14159265358979323846\nvar E = exp(1)\n\nvar Inf = 1/0 \n"
  },
  {
    "path": "modules/delaunay.morpho",
    "content": "/* Delaunay - Generates Delaunay simplices of a point set\n * Using a sweepline algorithm */\n\n// Compute the Circumsphere of a set of points\nclass Circumsphere {\n  init (vertices, indx) {\n    var n = indx.count()-1\n    var b = Matrix(n)\n    var M = Matrix(n,n)\n\n    var x0 = vertices[indx[0]] // Make x0 the origin\n    for (var i=0; i<n; i+=1) { // Build the linear system\n      var xi = vertices[indx[i+1]]-x0\n      b[i] = xi.inner(xi)/2\n      M.setcolumn(i,xi)\n    }\n\n    var xc=b/M.transpose() // Solve the linear system\n\n    self.i=indx\n    self.r=xc.norm()\n    self.x=xc+x0\n  }\n\n  pointinsphere(pt) {\n    return (pt-self.x).norm() < self.r\n  }\n}\n\nvar _errDlnyInit = Error(\"DlnyInit\", \"You must initialize Delaunay with a list of points to triangulate.\")\n\n// Generates simplices for a point cloud with the Delaunay property\n// that no simplex's circumsphere contains another point\nclass Delaunay {\n  init (pts) {\n    if (!islist(pts) || pts.count()==0) _errDlnyInit.throw()\n    var dim = pts[0].count()\n    if (pts.count()<dim+1) Error(\"DlnyDim\", \"You must supply at least ${dim+1} points in ${dim} dimensions.\").throw()\n\n    self.dim = dim\n    self.sscale = 1\n    self.pts = pts.clone()\n    self.n = pts.count()\n    self.bbox = self.calcbbox()\n    self.eps = 1e-8 // Tolerance for distance calculations\n  }\n\n  calcbbox() { // Calculate a bounding box for the point cloud\n    var min = self.pts[0].clone(), max = min.clone()\n\n    for (x in self.pts) {\n      for (var i=0; i<self.dim; i+=1) {\n        if (x[i]<min[i]) min[i]=x[i]\n        if (x[i]>max[i]) max[i]=x[i]\n      }\n    }\n\n    return [min, max]\n  }\n\n  picksweep() { // Pick sweep axis as the dimension with greatest extent\n    var extent = (self.bbox[1]-self.bbox[0])\n    var lext = []\n    for (dx in extent) lext.append(dx)\n\n    return lext.order()[-1]\n  }\n\n  supersimplex() { // Create a supersimplex that encloses the point cloud\n    // We create a simplex with vertices x0, x0 + lamda*[1,0,0..], x0 + lambda*[0,1,0..]...\n    // Where x0 and lambda are computed from a sphere that encloses the bounding box\n    var extent = (self.bbox[1]-self.bbox[0])\n    var xc = (self.bbox[0]+self.bbox[1])/2 // Center of the cloud\n    var r = self.sscale*(self.bbox[1]-xc).norm() // Radius of a ball that encloses the cloud\n    var lambda = 2*(self.dim+sqrt(self.dim))*r // Scale factor\n    for (i in 0...self.dim) xc[i]-=r // Shift by r in all coordinate directions\n\n    var pts = [xc] // Vertices\n    for (i in 0...self.dim) {\n      var xi = Matrix(self.dim)\n      xi[i]=lambda // Create a vector that lies in the i'th coordinate axis\n      pts.append(xc+xi) // Add the vertex to the list\n    }\n\n    return pts\n  }\n\n  triangulate() { // Create Delaunay triangulation\n    var sweep=self.picksweep() // Pick sweep dimension\n\n    var pts = self.pts\n    var n = self.n\n    var indices = List(0...n) // Create an array of indices into the vertex array\n\n    // Sort points along sweep axis\n    fn cmp (i, j) {\n      var diff = pts[i][sweep] - pts[j][sweep]\n      if (diff!=0) return diff\n      return i-j\n    }\n    indices.sort(cmp)\n\n    // Create supersimplex\n    var ssmplx = self.supersimplex()\n\n    for (x in ssmplx) pts.append(x) // And add to the point cloud\n\n    self.open = [Circumsphere(pts, List(n..n+self.dim))]// List of open simplices\n    self.closed = [] // List of finalized simplices\n\n    // Incrementally add each point to the mesh\n    self.ext = Dictionary() // Exterior (n-1) simplices\n    for (ix in indices) {\n      self.ext.clear()\n      // For each open simplex, check to see if the current point is\n      // inside its circumsphere. If it is, remove the triangle and add\n      // its edges to an edge list.\n      self.newopen = []\n      var dx, key, e\n      for (smplx in self.open) {\n        dx = pts[ix][sweep] - smplx.x[sweep]\n\n        // If this point is to the right of this simplex's circumcircle,\n        // then this simplex shouldn't get checked again. Add it to the\n        // closed list and don't retain.\n        if (dx > 0.0 && dx > smplx.r) {\n          self.closed.append(smplx)\n          continue\n        }\n\n        // If we're outside the circumsphere, skip this simplex.\n        if ((pts[ix] - smplx.x).norm() - smplx.r > self.eps) {\n          self.newopen.append(smplx)\n          continue\n        }\n\n        // Add the simplex's exterior to the exterior list and don't retain.\n        // sets() generates sets of order dim from the element e.g. triangles use order 2\n        for (e in smplx.i.sets(self.dim)) {\n            key = \"${e}\"\n            // Check if the key already exists. If so, remove the key.\n            if (self.ext.contains(key)) {\n                self.ext.remove(key)\n            }\n            else {\n                self.ext[key] = e\n            }\n        }\n      }\n      self.open = self.newopen\n\n      // Add a new simplex for each exterior (n-1) simplex.\n      for (e in self.ext.keys()) {\n        self.extel = self.ext[e]\n        self.extel.append(ix) // Include the vertex being added*\n        self.open.append(Circumsphere(pts, self.extel))\n      }\n    }\n\n    // Copy any remaining open triangles to the closed list\n    for (smplx in self.open) self.closed.append(smplx)\n\n    // Select all simplices that don't have a vertex from the supersimplex\n    self.open = []\n    var record\n    for (smplx in self.closed) {\n      record = true\n      for (ix in smplx.i) if (ix>=n) record = false\n      if (record) self.open.append(smplx.i)\n    }\n\n    return self.open\n  }\n}\n"
  },
  {
    "path": "modules/functionals.morpho",
    "content": "/* ***************************************************************\n * Functionals\n * ===========\n * This module provides a class that helps implement new\n * functionals in morpho, as well as some less common functionals\n **************************************************************** */\n\nimport kdtree\n\nclass Functional {\n  init(grade) {\n    self.grade = grade\n  }\n\n  integrand(mesh) {\n    var conn\n    if (self.grade>0) conn=mesh.connectivitymatrix(0, self.grade)\n    var vert=mesh.vertexmatrix()\n    var nel = mesh.count(self.grade)\n\n    var out = Matrix(nel)\n\n    for (i in 0...nel) {\n      var el\n      if (conn) el=conn.rowindices(i)\n      out[i]=self.integrandfn(mesh, vert, i, el)\n    }\n\n    return out\n  }\n\n  gradient(mesh) {\n    var conn\n    if (self.grade>0) conn=mesh.connectivitymatrix(0, self.grade)\n    var vert=mesh.vertexmatrix()\n    var nel = mesh.count(self.grade)\n\n    var dim=vert.dimensions()[0]\n    var nv=vert.dimensions()[1]\n    var out = Matrix(dim,nv)\n\n    for (i in 0...nel) {\n      var el\n      if (conn) el=conn.rowindices(i)\n      self.gradientfn(mesh, vert, i, el, out)\n    }\n\n    return out\n  }\n\n  total(mesh) {\n    var a = self.integrand(mesh)\n    return a.sum()\n  }\n}\n\n/** Hookean elasticity:\n  F = 1/2 ((L-L0)/L0)^2\n  Construct with a field of reference lengths */\nclass HookeElasticity < Functional {\n  init(field) {\n    self.ref = field\n    super.init(1)\n  }\n\n  integrandfn(mesh, vert, id, el) {\n    var len = (vert.column(el[0])-vert.column(el[1])).norm()\n    var len0 = self.ref[self.grade, id]\n    return ((len-len0)/len0)^2/2\n  }\n\n  gradientfn(mesh, vert, id, el, grd) {\n    var x0 = vert.column(el[0])\n    var x1 = vert.column(el[1])\n    var dx = x0-x1\n\n    var len = dx.norm()\n    var len0 = self.ref[self.grade, id]\n    var g = dx*(len-len0)/(len0^2*len)\n\n    grd.setcolumn(el[0], g+grd.column(el[0]))\n    grd.setcolumn(el[1], -g+grd.column(el[1]))\n  }\n}\n\n/* Pairwise potential */\nclass PairwisePotential < Functional {\n  init (func, grad, cutoff=nil) {\n    self.func = func\n    self.grad = grad\n    self.cutoff = cutoff\n    super.init(0)\n  }\n\n  buildtree(mesh) {\n    var vert = mesh.vertexmatrix()\n    var nv = mesh.count()\n    var pts = []\n    for (i in 0...nv) {\n      pts.append(vert.column(i))\n    }\n    return KDTree(pts)\n  }\n\n  integrand(mesh) {\n    var nv = mesh.count()\n    var out = Matrix(nv)\n    var vert = mesh.vertexmatrix()\n\n    for (i in 0...nv) {\n      var x = vert.column(i)\n      for (j in i+1...nv) {\n        var r = (x-vert.column(j)).norm()\n        if (self.cutoff && r>self.cutoff) continue\n        var f = self.func(r)\n        out[i]+=0.5*f\n        out[j]+=0.5*f\n      }\n    }\n    return out\n  }\n\n  gradient(mesh) {\n    var nv = mesh.count()\n    var vert = mesh.vertexmatrix()\n    var dim = vert.dimensions()[0]\n    var out = Matrix(dim, nv)\n\n    for (i in 0...nv) {\n      for (j in i+1...nv) {\n        var dx = vert.column(i)-vert.column(j)\n        var r = dx.norm()\n        if (self.cutoff && r>self.cutoff) continue\n        var df = self.grad(r)/r\n        out.setcolumn(i, df*dx+out.column(i))\n        out.setcolumn(j, -df*dx+out.column(j))\n      }\n    }\n    return out\n  }\n\n}\n"
  },
  {
    "path": "modules/graphics.morpho",
    "content": "/* Graphics */\n\nimport constants\nimport meshtools\nimport color\n\nvar _eps = 1e-15\nvar _fntntfnd = Error(\"FntNtFnd\", \"Font not found.\")\nvar _txtdrnvrtprll = Error(\"TxtDrnVrtPrll\", \"Direction and vertical must not be parallel or zero.\")\n\n/* **************************************\n * Utility functions\n **************************************** */\n\n/** Constructs a rotation matrix\n  @param[in] theta - rotation angle\n  @param[in] axis - axis to rotate about\n  @returns the rotation matrix  */\nfn _rotation(theta, axis) {\n  var x = axis[0], y=axis[1], z=axis[2]\n  var ctheta = cos(theta), stheta = sin(theta), cc=1-ctheta\n\n  return Matrix(\n  [[x*x*cc+ctheta,   x*y*cc-z*stheta, x*z*cc+y*stheta],\n   [x*y*cc+z*stheta, y*y*cc+ctheta,   y*z*cc-x*stheta],\n   [z*x*cc-y*stheta, y*z*cc+x*stheta, z*z*cc+ctheta ]]\n  )\n}\n\n/** Generates a vector normal to a given vector but of arbitrary orientation */\nfn _normalvector(vec) {\n  var order = [abs(vec[0]), abs(vec[1]), abs(vec[2])].order()\n  var out=vec.clone()\n  out[order[0]]=0\n  out[order[1]]=vec[order[2]]\n  out[order[2]]=-vec[order[1]]\n  return out\n}\n\n/* 3d cross product */\nfn _cross3d(a, b) {\n  return Matrix([ a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0] ])\n}\n\n/* Gram-Schmitt orthonormalization */\nfn _orthogonalize(m) { \n  var ncols=m.dimensions()[1]\n  for (i in 0...ncols) {\n    var x = m.column(i)\n    for (j in 0...i) {\n      var y = m.column(j)\n      x -= x.inner(y)*y\n    }\n    var nrm = x.norm() \n    if (nrm<_eps) _txtdrnvrtprll.throw() \n    x/=nrm\n    m.setcolumn(i, x)\n  }\n}\n\n/* **************************************\n * Graphics primitives\n **************************************** */\n\n/** TriangleComplexes are a basic graphical unit that draw a set of triangles\n   from a given set of vertices. */\nclass TriangleComplex {\n  init(position, normals, colors, connectivity, filter=nil, transmit=nil) {\n    self.position=position /* Vertex position matrix */\n    self.normals=normals /* Matrix of normal vectors */\n    self.colors=colors /* Color */\n    self.connectivity=connectivity /* Connectivity matrix; columns are triangles */\n    \n    /* Optional arguments to be used by povray*/\n    self.filter = filter \n    self.transmit = transmit\n  }\n\n}\n\n/** Draws a cylinder\n  @param[in] start - start point\n  @param[in] end - end point\n  @param[in] aspectratio - aspect ratio\n  @param[in] n - number of faces to use\n  @param[in] color - Color of the sphere */\nclass Cylinder {\n  init(start, end, aspectratio=0.1, n=10, color=nil, filter=nil, transmit=nil) {\n    self.start = Matrix(start)\n    self.end = Matrix(end)\n    self.aspectratio = aspectratio\n    self.n = n\n    self.color = color\n\n    /* Optional arguments to be used by povray*/\n    self.filter = filter \n    self.transmit = transmit\n  }\n\n  totrianglecomplex() {\n    var aspectratio = self.aspectratio\n    var n = self.n\n\n    var col = self.color\n    if (isnil(col)) col = [0.5,0.5,0.5]\n\n    var np=2*n+2 // 2 points at the end plus 2*n point\n    var nt=4*n\n    var dim=self.start.count()\n    var vertices=Matrix(dim, np);\n    var normals=Matrix(dim, np);\n\n    var x1=self.start, x2=self.end\n    var dx=x2-x1 // Vector from x1 to x2\n    var length = dx.norm()\n    if (abs(length)<1e-8) return nil\n    var ndx=dx/length\n\n    var px=_normalvector(dx)\n    var pxnorm=px.norm()\n    if (abs(pxnorm)<1e-15) return\n    px=0.5*length*aspectratio*px/pxnorm\n\n    // Head and base of the arrow\n    vertices.setcolumn(0, x1)\n    vertices.setcolumn(np-1, x2)\n    normals.setcolumn(0, -1*dx)\n    normals.setcolumn(np-1, dx)\n\n    /* The cylinder is structured like so:\n\n           [n+1...2n]\n                      \\\n    [1..n] A------------B\n           |            |\n           0            1 2n+1\n           <---- dx ----> */\n    for (i in 1..n) {\n        var theta = 2*Pi*(i-1)/n\n        var qx = _rotation(theta, ndx)*px\n        var nn=qx/qx.norm()\n        var pt = qx+x1\n        // Find vertex ring A by rotating px about ndx, to obtain qx, and then adding x1\n        vertices.setcolumn(i, pt)\n        // Vertex ring B is obtained by simply translating the first circle by dx*(1-ar)\n        vertices.setcolumn(n+i, pt+dx)\n\n        // Normals\n        normals.setcolumn(i, nn)\n        normals.setcolumn(n+i, nn)\n    }\n\n    var conn=Sparse(np,nt)\n    for (i in 0...n) {\n      // Layer 0-A\n      conn[0,i]=1\n      conn[i+1,i]=1\n      if (i<n-1) conn[i+2,i]=1 else conn[1,i]=1\n\n      // C-1\n      conn[np-1,n+i]=1\n      conn[n+i+1,n+i]=1\n      if (i<n-1) conn[n+i+2,n+i]=1 else conn[n+1,n+i]=1\n\n      // Layer A-B\n      conn[i+1,2*n+i]=1\n      conn[n+i+1,2*n+i]=1\n      if (i<n-1) conn[i+2,2*n+i]=1 else conn[1,2*n+i]=1\n\n      if (i<n-1) conn[i+2,3*n+i]=1 else conn[1,3*n+i]=1\n      if (i<n-1) conn[n+i+2,3*n+i]=1 else conn[n+1,3*n+i]=1\n      conn[n+i+1,3*n+i]=1\n    }\n\n      return TriangleComplex(vertices, normals, col, conn)\n  }\n\n}\n\nvar _sphere = [ PolyhedronMesh(\n  [[-0.982247, 0.0, 0.187592], [0.982247,\n    0.0, -0.187592], [-0.303531, -0.934172, 0.187592], [-0.303531,\n    0.934172, 0.187592], [0.794654, -0.57735, 0.187592], [0.794654,\n    0.57735, 0.187592], [-0.187592, -0.57735, 0.794654], [-0.187592,\n    0.57735, 0.794654], [-0.491123, -0.356822, -0.794654], [-0.491123,\n    0.356822, -0.794654], [0.491123, -0.356822, 0.794654], [0.491123,\n    0.356822, 0.794654], [0.607062,\n    0.0, -0.794654], [-0.794654, -0.57735, -0.187592], [-0.794654,\n    0.57735, -0.187592], [-0.607062, 0.0,\n    0.794654], [0.187592, -0.57735, -0.794654], [0.187592,\n    0.57735, -0.794654], [0.303531, -0.934172, -0.187592], [0.303531,\n    0.934172, -0.187592]]\n  ,\n  [[14, 9, 8, 13, 0], [1, 5, 11, 10, 4], [4, 10, 6, 2, 18], [10, 11, 7,\n    15, 6], [11, 5, 19, 3, 7], [5, 1, 12, 17, 19], [1, 4, 18, 16,\n    12], [3, 19, 17, 9, 14], [17, 12, 16, 8, 9], [16, 18, 2, 13, 8], [2,\n     6, 15, 0, 13], [15, 7, 3, 14, 0]]\n) ]\n\n/** Draws a sphere\n  @param[in] center - the center of the sphere\n  @param[in] r - radius\n  @param[in] color - Color of the sphere */\nclass Sphere {\n  init(center, r, color=nil, maxrefine = 3, filter=nil, transmit=nil) {\n    self.center = center\n    self.r = r\n    self.color = color\n    self.maxrefine = maxrefine\n\n    /* Optional arguments to be used by povray */\n    self.filter = filter \n    self.transmit = transmit\n  }\n\n  refinementlevel() {\n      var n = 0 \n      while (self.r/(2^n)>0.1 && n<self.maxrefine) {\n      if (n>=_sphere.count()-1) {\n        _sphere.append(refinemesh(_sphere[-1]))\n      }\n      n += 1;\n    }\n    return n\n  }\n\n  totrianglecomplex(scale=true) {\n    var center = self.center, r = self.r\n    var col = self.color\n    if (isnil(col)) col = [0.5,0.5,0.5]\n    var n = self.refinementlevel() \n\n    var v = _sphere[n].vertexmatrix().clone()\n    var normals = _sphere[n].vertexmatrix().clone()\n    var conn = _sphere[n].connectivitymatrix(0,2) // Should clone!\n    var nv = v.dimensions()[1]\n\n    var x0=center\n    if (islist(center)) x0=Matrix(center)\n\n    // Ignores the center and size and just produces the unit ball\n    if (!scale) { x0*=0; r=1; col = [0.5,0.5,0.5] } \n\n    for (i in 0...nv) { // Normalize, scale and translate\n      var x = v.column(i)\n      var nn = x/x.norm()\n      normals.setcolumn(i, nn)\n      v.setcolumn(i, x0 + r*nn)\n    }\n\n    return TriangleComplex(v, normals, col, conn)\n  }\n\n}\n\n/** Draws an arrow\n  @param[in] start - start point\n  @param[in] end - end point\n  @param[in] aspectratio - aspect ratio of the arrow\n  @param[in] n - number of faces to use\n  @param[in] color - Color of the sphere */\nclass Arrow {\n  init(start, end, aspectratio=0.1, n=10, color=nil, filter=nil, transmit=nil) {\n    self.start = Matrix(start)\n    self.end = Matrix(end)\n    self.aspectratio = aspectratio\n    self.n = n\n    self.color = color\n\n    /* Optional arguments to be used by povray*/\n    self.filter = filter \n    self.transmit = transmit\n  }\n\n  totrianglecomplex () {\n    var aspectratio = self.aspectratio\n    var n = self.n\n\n    var col = self.color\n    if (isnil(col)) col = [0.5,0.5,0.5]\n\n    var np=3*n+2 // 2 points at the end plus 3*n point\n    var nt=6*n\n    var dim=self.start.count()\n    var vertices=Matrix(dim, np)\n    var normals=Matrix(dim, np)\n\n    var x1=self.start, x2=self.end\n    var dx=x2-x1 // Vector from x1 to x2\n    var length = dx.norm()\n    var ndx=dx/length\n\n    var px=_normalvector(dx)\n    var pxnorm=px.norm()\n    if (abs(pxnorm)<1e-15) return\n    px=0.5*length*aspectratio*px/pxnorm\n\n    // Head and base of the arrow\n    vertices.setcolumn(0, x1)\n    vertices.setcolumn(np-1, x2)\n    normals.setcolumn(0, -1*dx)\n    normals.setcolumn(np-1, dx)\n\n    /* The arrow is structured like so:\n\n           [n+1...2n]   C--[2n+1...3n]\n                      \\ |\\\n    [1..n] A------------B \\\n           |               \\\n           0<----- u --->   1 3n+1\n           <------ dx ------> */\n    var u=(1-aspectratio)*dx\n    for (i in 1..n) {\n        var theta = 2*Pi*(i-1)/n\n        var qx = _rotation(theta, ndx)*px\n        var nn=qx/qx.norm()\n        var pt = qx+x1\n        // Find vertex ring A by rotating px about ndx, to obtain qx, and then adding x1\n        vertices.setcolumn(i, pt)\n        // Vertex ring B is obtained by simply translating the first circle by dx*(1-ar)\n        vertices.setcolumn(n+i, pt+u)\n        // Vertex ring C is obtained by simply adding qx to the point in vertex ring B\n        vertices.setcolumn(2*n+i, pt+u+qx)\n\n        // Normals\n        normals.setcolumn(i, nn)\n        normals.setcolumn(n+i, nn)\n        normals.setcolumn(2*n+i, nn)\n    }\n\n    var conn=Sparse(np,nt)\n    for (i in 0...n) {\n      // Layer 0-A\n      conn[0,i]=1\n      conn[i+1,i]=1\n      if (i<n-1) conn[i+2,i]=1 else conn[1,i]=1\n\n      // C-1\n      conn[np-1,n+i]=1\n      conn[2*n+i+1,n+i]=1\n      if (i<n-1) conn[2*n+i+2,n+i]=1 else conn[2*n+1,n+i]=1\n\n      // Layer A-B\n      conn[i+1,2*n+i]=1\n      conn[n+i+1,2*n+i]=1\n      if (i<n-1) conn[i+2,2*n+i]=1 else conn[1,2*n+i]=1\n\n      if (i<n-1) conn[i+2,3*n+i]=1 else conn[1,3*n+i]=1\n      if (i<n-1) conn[n+i+2,3*n+i]=1 else conn[n+1,3*n+i]=1\n      conn[n+i+1,3*n+i]=1\n\n      // Layer B-C\n      conn[n+i+1,4*n+i]=1\n      if (i<n-1) conn[n+i+2,4*n+i]=1 else conn[n+1,4*n+i]=1\n      conn[2*n+i+1,4*n+i]=1\n\n      if (i<n-1) conn[n+i+2,5*n+i]=1 else conn[n+1,5*n+i]=1\n      if (i<n-1) conn[2*n+i+2,5*n+i]=1 else conn[2*n+1,5*n+i]=1\n      conn[2*n+i+1,5*n+i]=1\n    }\n\n    return TriangleComplex(vertices, normals, col, conn)\n  }\n\n}\n\n/** Draws a tube\n  @param[in] pts - list of points or a matrix with points as the columns\n  @param[in] rad - radius\n  @param[in] n - number of faces to use per point\n  @param[in] closed - whether to treat the point list as closed or not\n  @param[in] color - Color of the sphere */\nclass Tube {\n  normalvector(vec) { // Musn't take the abs for Tube\n    var order = [vec[0], vec[1], vec[2]].order()\n    var out=vec.clone()\n    out[order[0]]=0\n    out[order[1]]=vec[order[2]]\n    out[order[2]]=-vec[order[1]]\n    return out\n  }\n\n  init(pts, rad, n=10, color=nil, closed=false, filter=nil, transmit=nil) {\n    self.pts = pts\n    self.radius = rad\n    self.n = n\n    self.closed = closed\n    self.color = color\n\n    /* Optional arguments to be used by povray*/\n    self.filter = filter\n    self.transmit = transmit\n  }\n\n  totrianglecomplex() {\n    var n = self.n\n    var col = self.color\n    if (isnil(col)) col = [0.5,0.5,0.5]\n\n    var mat = self.pts\n    if (islist(self.pts)) mat = Matrix(self.pts).transpose();\n\n    var np=mat.dimensions()[1]\n  \tvar dim=mat.dimensions()[0]\n    var nv=n*np\n  \tvar nt=n*(2*(np-1))\n    if (!self.closed) {\n  \t\tnv+=2 // Two points for the caps\n  \t\tnt+=2*n // n triangles for each cap\n  \t}\n\n    var vertices=Matrix(dim, nv);\n    var normals=Matrix(dim, nv);\n\n  \tvar sep[np], ndx[np]\n\n  \t// Loop over line segments to calculate separation vectors\n  \tfor (k in 0..np-2) {\n  \t\tvar dx=mat.column(k+1)-mat.column(k)\n  \t\tsep[k]=dx/dx.norm()\n  \t\tndx[k]+=sep[k]\n  \t\tndx[k+1]+=sep[k]\n  \t}\n\n  \t// Correct ends if closed\n  \tif (self.closed) {\n  \t\tvar dx=mat.column(0)-mat.column(np-1)\n  \t\tsep[np-1]=dx/dx.norm()\n  \t\tndx[np-1]+=sep[np-1]\n  \t\tndx[0]+=sep[np-1]\n  \t}\n\n  \t// Now generate vertices\n   \tfor (k in 0...np) {\n  \t\tvar px // Vertices are generated from a point plus this displacement\n   \t\tif (k==0) {\n  \t\t\tpx=self.normalvector(ndx[k])\n  \t\t} else { // This aligns vertices generated from successive points\n  \t\t\tvar dx=vertices.column((k-1)*n)-mat.column(k-1)\n  \t\t\tpx=dx-dx.inner(ndx[k])*ndx[k]/ndx[k].norm()\n  \t\t}\n  \t\tpx=self.radius*px/px.norm()\n\n  \t\tvar x1 = mat.column(k)\n  \t\tfor (i in 0...n) {\n  \t\t\t/* Find vertex ring at point k by rotating px about ndx, to obtain qx, and then adding x1 */\n        var theta = i*2*Pi/n\n  \t\t\tvar qx = _rotation(theta, ndx[k])*px\n  \t\t\tvertices.setcolumn(k*n+i, x1+qx)\n\n  \t\t\tnormals.setcolumn(k*n+i, qx/qx.norm())\n  \t\t}\n   \t}\n\n  \t/* Loop over segments again to create the triangles */\n  \tvar conn=Sparse(nv,nt)\n\n  \tvar m=0\n  \tfor (k in 0..np-2) {\n  \t\tfor (i in 0...n) {\n  \t\t\tconn[k*n+i,m]=1\n  \t\t\tif (i==n-1) conn[k*n,m]=1 else conn[k*n+i+1,m]=1\n  \t\t\tconn[((k+1)*n+i),m]=1\n  \t\t\tm+=1\n  \t\t\tif (i==n-1) conn[k*n,m]=1 else conn[k*n+i+1,m]=1\n  \t\t\tif (i==n-1) conn[(k+1)*n,m]=1 else conn[((k+1)*n+i)+1,m]=1\n  \t\t\tconn[((k+1)*n+i),m]=1\n  \t\t\tm+=1\n  \t\t}\n  \t}\n\n  \tif (self.closed) {\n  \t\t// Connect first and last points\n  \t\tvar k=np-1\n  \t\tfor (i in 0...n) {\n  \t\t\tconn[k*n+i,m]=1\n  \t\t\tif (i==n-1) conn[k*n,m]=1 else conn[k*n+i+1,m]=1\n  \t\t\tconn[i,m]=1\n  \t\t\tm+=1\n  \t\t\tif (i==n-1) conn[k*n,m]=1 else conn[k*n+i+1,m]=1\n  \t\t\tif (i==n-1) conn[0,m]=1 else conn[i+1,m]=1\n  \t\t\tconn[i,m]=1\n  \t\t\tm+=1\n  \t\t}\n  \t} else {\n  \t\t// End caps\n  \t\tvertices.setcolumn(nv-2, mat.column(0))\n  \t\tvertices.setcolumn(nv-1, mat.column(np-1))\n\n  \t\tnormals.setcolumn(nv-2, -sep[0]/sep[0].norm())\n  \t\tnormals.setcolumn(nv-1, sep[np-2]/sep[np-2].norm())\n\n  \t\tfor (i in 0...n) {\n  \t\t\tconn[i,m]=1\n  \t\t\tif (i==n-1) conn[0,m]=1 else conn[i+1,m]=1\n  \t\t\tconn[nv-2,m]=1\n  \t\t\tm+=1\n  \t\t\tconn[((np-1)*n+i),m]=1\n  \t\t\tif (i==n-1) conn[(np-1)*n,m]=1 else conn[((np-1)*n+i)+1,m]=1\n  \t\t\tconn[nv-1,m]=1\n  \t\t\tm+=1\n  \t\t}\n  \t}\n\n      return TriangleComplex(vertices, normals, col, conn, filter=self.filter, transmit=self.transmit)\n  }\n\n}\n\n/* **************************************\n * Text\n **************************************** */\n\nclass Text {\n  init(string, posn, dirn=nil, vertical=nil, size=10, font=nil, color=nil, filter=nil, transmit=nil) {\n    self.posn = posn\n    if (islist(self.posn)) self.posn = Matrix(self.posn)\n\n    self.dirn = dirn // Direction of flow for the text \n    if (islist(self.dirn)) self.dirn = Matrix(self.dirn)\n\n    self.vertical = vertical // Vertical direction\n    if (islist(self.vertical)) self.vertical = Matrix(self.vertical)\n    \n    self.string = string \n    self.font = font\n    self.color = color \n    self.size = size \n    \n    /* Optional arguments to be used by povray*/\n    self.filter = filter \n    self.transmit = transmit\n  }\n\n  transformationmatrix() {\n    if (!(ismatrix(self.dirn) || ismatrix(self.vertical))) return nil \n    var m = Matrix(4,4) \n    for (i in 0..3) m[i,i]=1\n\n    if (self.dirn) for (i in 0..2) m[i,0]=self.dirn[i]\n    if (self.vertical) for (i in 0..2) m[i,1]=self.vertical[i]\n\n    // Now set the final direction to the cross product of the two \n    var zp = _cross3d(m.column(1),m.column(0)) \n    for (i in 0..2) m[i,2]=zp[i]\n\n    _orthogonalize(m)\n\n    return m.transpose() \n  }\n\n}\n\nclass Font {\n  init (name, size, id=nil) {\n    self.name = name \n    self.size = size \n    self.id = id\n  }\n}\n\n/* **************************************\n * Graphics class\n **************************************** */\n\nclass Graphics {\n  init(title = nil, background = nil) {\n    self.displaylist = [] // A collection of primitives to display\n    self.title = title\n    if (!self.title) self.title=\"Morpho\" // Title to show\n    self.background = background\n    if (!self.background) self.background = Black\n  }\n\n  display(item) {\n    if (isobject(item)) self.displaylist.append(item)\n  }\n\n  add(right) { // Create a union of two graphics objects\n    var g = Graphics()\n    for (el in self.displaylist) g.displaylist.append(el)\n    for (el in right.displaylist) g.displaylist.append(el)\n    return g\n  }\n}\n\n/* **************************************\n * Show a graphics object\n **************************************** */\n\nclass Show {\n\n  init(g) {\n    self.uidinit()\n    self.spheres = Dictionary() \n    self.fonts = []\n    self.colors = Dictionary() \n\n    var tempfolder = \"/tmp\"\n    var fname = self._randomalphanumstring(10)\n    var out = File(\"${tempfolder}/morpho${fname}.draw\", \"w\")\n\n    self.write(g, out)\n\n    out.close()\n    // 1/7/25 Redirect error output to /dev/null to suppress annoying errors on macOS\n    system(\"morphoview -t ${tempfolder}/morpho${fname}.draw > /dev/null 2>&1 &\")\n  }\n\n  /** Generate a random name */\n  _randomalphanumstring(n) {\n    var alpha=\"abcdefghijklmnopqrstuvwxyz0123456789\"\n    var name=\"\"\n    for (i in 1..n) name+=alpha[Int(random()*(alpha.count()-1))]\n    return name\n  }\n\n  uidinit() {\n    self.uid = 0\n  }\n\n  uid() { // Generate unique identifiers \n    self.uid+=1\n    return self.uid\n  }\n\n  preamble(graphic, out) {\n    out.write(\"S 1 3\")\n    out.write(\"W \\\"${graphic.title}\\\"\")\n  }\n\n  /* The visit method for a generic `item`. \n\n  Here, we define this separate `visitgeneric` method that performs the intended generic task, and have the actual generic `visit` method call `visitgeneric`. This enables calls to the generic fallback from *within a specific implementation*. \n  For example, the `visit` method for a `Sphere` object can call `self.visitgeneric(item, out)` to visit the `TriangleComplex` representation of the sphere, but calling `self.visit(item, out)` would result in an infinite loop.\n  */\n  visitgeneric(item, out) {\n    self.visit(item.totrianglecomplex(), out) \n  }\n\n  // Will be used by Cylinder, Arrow and Tube\n  visit(item, out) {\n    self.visitgeneric(item, out)\n  }\n\n  visit(Sphere item, out) {\n    var n = item.refinementlevel()\n    \n    if (item.color) {\n      self.visitgeneric(item, out)\n      return \n    } else if (!self.spheres.contains(n)) { // Check if we already have a sphere object at this refinement level\n      var tri=item.totrianglecomplex(scale=false)\n      tri.id = self.uid() \n      out.write(\"o ${tri.id}\")\n      self.trianglecomplexobjectdata(tri,out) \n      self.spheres[n]=tri\n    } \n\n    out.write(\"i\")\n    out.write(\"s ${item.r}\")\n    out.write(\"t ${item.center[0]} ${item.center[1]} ${item.center[2]}\")\n    out.write(\"d ${self.spheres[n].id}\")\n  }\n\n  trianglecomplexobjectdata(item, out) {\n    var x = item.position\n    var n = item.normals\n    var col = item.colors\n    var localcolor = false\n    var dimensions=x.dimensions()\n    var dim = dimensions[0]\n    var nv = dimensions[1]\n\n    if (ismatrix(col)) {\n      if (col.dimensions()[1]>1) localcolor = true\n    } else if (isobject(col) && !islist(col)) col = col.rgb(0)\n\n    out.write(\"v \\\"xnc\\\"\")\n    for (i in 0...nv) {\n      var line = \"\"\n      for (j in 0...dim) {\n        line+=\"${x[j,i]} \"\n      }\n      for (j in 0...dim) {\n        line+=\"${n[j,i]} \"\n      }\n      if (localcolor) {\n        for (j in 0...3) line+=\"${col[j,i]} \"\n      } else {\n        for (j in 0...item.colors.count()) line+=\"${col[j]} \"\n      }\n      out.write(line)\n    }\n\n    out.write(\"f\")\n    var f=item.connectivity\n    nv=f.dimensions()[0]\n    var nf=f.dimensions()[1]\n\n    for (i in 0...nf) {\n      var line = \"\"\n      for (j in 0...nv) {\n        if (f[j,i]!=0) line+=\"${j} \"\n      }\n      out.write(line)\n    }\n  }\n\n  visit(TriangleComplex item, out) {\n    var id = self.uid() \n    out.write(\"o ${id}\")\n    self.trianglecomplexobjectdata(item, out)\n    out.write(\"i\")\n    out.write(\"d ${id}\")\n  }\n\n  fontpath() {\n    var platform = System.platform()\n    if (platform==\"macos\") {\n      return [\"/System/Library/Fonts\"]\n    } else return [\"/usr/share/fonts\",\"/usr/local/share/fonts\"]\n  }\n\n  matchfontfile(file, font) {\n    var fname = file.split(\".\")\n    if (fname[0]==font && \n        (fname[1]==\"ttc\" || fname[1]==\"ttf\")) return true \n    return false \n  } \n\n  searchfontfolder(folder, font) {\n    for (f in Folder.contents(folder)) {\n      var path = folder + \"/\" + f\n      if (self.matchfontfile(f, font)) return path\n\n      if (Folder.isfolder(path)) { // Recurse\n        var result = self.searchfontfolder(path, font)\n        if (result) return result \n      }\n    }\n    return nil\n  }\n\n  findfont(font) {\n    var base = self.fontpath() \n    for (f in base) {\n      var found = self.searchfontfolder(f, font)\n      if (found) return found\n    }\n    return nil \n  }\n\n  defaultfontname() {\n    var platform = System.platform()\n    if (platform==\"macos\") {\n      return \"Helvetica\"\n    } else return \"FreeSans\"\n  }\n\n  addcolor(color, out) {\n    if (!self.colors.contains(color)) {\n      var id = self.colors[color] = self.colors.count() \n      out.write(\"c ${id} ${color.r} ${color.g} ${color.b}\")\n    }\n    return self.colors[color] \n  }\n\n  addfont(font, size, out) {\n    var path = self.findfont(font)\n    if (!path) _fntntfnd.throw() \n    var id = self.uid()\n    var fnt = Font(font, size, id=id) \n    self.fonts.append(fnt)\n\n    out.write(\"F ${id} \\\"${path}\\\" ${size}\")\n    return id \n  }\n\n  findfontid(font, size, out) {\n    for (f in self.fonts) {\n      if (f.name == font && f.size == size) return f.id\n    }\n    return self.addfont(font, size, out) \n  }\n\n  visit(Text item, out) {\n    var font = item.font\n    if (!font) font = self.defaultfontname()\n    var id = self.findfontid(font, item.size, out)\n    \n    if (item.color) {\n      var id = self.addcolor(item.color, out) \n      out.write(\"C ${id}\")\n    }\n\n    out.write(\"i\")\n    var m = item.transformationmatrix() \n    if (m) {\n      var str = \"m\"\n      for (i in 0..3) for (j in 0..3) str+=\" \"+String(m[i,j])\n      out.write(str)\n    }\n    out.write(\"t ${item.posn[0]} ${item.posn[1]} ${item.posn[2]}\")\n    out.write(\"T ${id} \\\"${item.string}\\\"\")\n  }\n\n  write(graphic, out) {\n    self.preamble(graphic, out)\n\n    for (item in graphic.displaylist) self.visit(item, out)\n  }\n}\n"
  },
  {
    "path": "modules/histogram.morpho",
    "content": "/*\n * Histogram\n * Simple command line histograms.\n */\n\n// Find the minimum of an enumerable object\nfn hmin(x) {\n  var mn=x[0]\n  for (i in x) {\n    if (i<mn) mn=i\n  }\n  return mn\n}\n\n// Find the maximum of an enumerable object\nfn hmax(x) {\n  var mx=x[0]\n  for (i in x) {\n    if (i>mx) mx=i\n  }\n  return mx\n}\n\n// Display a histogram\nfn histogram(lst, nbins) {\n  var cnt[nbins]\n  var bins[nbins+1]\n  // Calculate the bin bounds\n  var mx = hmax(lst), mn = hmin(lst)\n  for (i in 0..nbins) {\n    bins[i]=mn+i*(mx-mn)/(nbins)\n  }\n\n  // Assign each element of lst to a bin\n  for (x in lst) {\n    var k=0\n    while (x>bins[k+1]) k+=1\n    cnt[k]+=1\n  }\n\n  // Show histogram\n  for (i in 0..nbins-1) {\n    print \"${bins[i]}-${bins[i+1]}: ${cnt[i]}\"\n  }\n}\n"
  },
  {
    "path": "modules/implicitmesh.morpho",
    "content": "/* Create meshes that are level sets of an implicit function\n  Marching method for implicit surfaces\n  E. Hartmann, The Visual Computer (1998) 14:95-108 */\n\nimport constants\nimport meshtools\n\nclass ImplicitPoint {\n  init (pt, normal) {\n    self.location = pt\n    var n = normal/normal.norm()\n    self.normal = n\n    if (n[0]>0.5 || n[1]>0.5) self.t1 = Matrix([n[1], -n[0], 0])\n    else self.t1 = Matrix([-n[2], 0, n[0]])\n\n    self.t1 /= self.t1.norm()\n    self.t2 = self.cross(n, self.t1)\n    self.t2 /= self.t2.norm()\n\n    self.frontangle = 0.0\n    self.border_point = false\n    self.exclude = false\n    self.angle_changed = false\n  }\n\n  cross(a, b) {\n    return Matrix([ a[1]*b[2]-a[2]*b[1],\n                    a[2]*b[0]-a[0]*b[2],\n                    a[0]*b[1]-a[1]*b[0] ])\n  }\n}\n\nclass ImplicitMeshBuilder < MeshBuilder {\n  init(f, gradient=nil) {\n    self.func = f\n    self.grad = gradient\n    self.tol = 1e-10\n    self.eps = 1e-8\n    self.maxiter = 100\n    self.points = []\n    super.init()\n  }\n\n  // Evaluate the gradient of the function\n  gradient(x) {\n    if (self.grad) {\n      return self.grad(x[0], x[1], x[2])\n    } else {\n      return Matrix([ (self.func(x[0]+self.eps, x[1], x[2])-self.func(x[0]-self.eps, x[1], x[2]))/2/self.eps,\n                     (self.func(x[0], x[1]+self.eps, x[2])-self.func(x[0], x[1]-self.eps, x[2]))/2/self.eps,\n                     (self.func(x[0], x[1], x[2]+self.eps)-self.func(x[0], x[1], x[2]-self.eps))/2/self.eps ])\n    }\n  }\n\n  // Finds a nearby point on the level set by gradient descent\n  surfacepoint(x0) {\n    var x=x0, grad\n    for (iter in 1..self.maxiter) {\n      var f = self.func(x[0], x[1], x[2])\n      grad = self.gradient(x)\n      var df = grad.norm()^2\n      if (df<self.tol) { print \"Zero gradient detected.\"; return nil }\n      var dx=f/df*grad\n      x-=dx\n      if (dx.norm()<self.tol) return ImplicitPoint(x, grad)\n    }\n    print \"Too many iterations in surfacepoint: Try a different starting point.\"\n    return ImplicitPoint(x, grad)\n  }\n\n  addpoint(x) {\n    self.points.append(x)\n    return self.addvertex(x.location)\n  }\n\n  initialhexagon(start, stepsize) {\n    var x0 = self.surfacepoint(start)\n    self.addpoint(x0)\n\n    for (theta in 0...2*Pi:Pi/3) {\n      var x = x0.location + stepsize*cos(theta)*x0.t1 + stepsize*sin(theta)*x0.t2\n      var xi = self.surfacepoint(x)\n      self.addpoint(xi)\n    }\n\n    var tri = [[0,1,2], [0,2,3], [0,3,4], [0,4,5], [0,5,6], [0,6,1]]\n    for (t in tri) self.addface(t)\n\n    self.front = [[1,2,3,4,5,6]] // First front polygon\n    for (id in self.front[0]) self.points[id].angle_changed=true\n  }\n\n  calculatefrontangle(il, ii, ir) {\n    // shift into local coordinates\n    var x1 = self.points[il].location - self.points[ii].location\n    // Project this position into the (t1, t2, n) frame at point i\n    var omega1 = arctan( x1.inner(self.points[ii].t1), x1.inner(self.points[ii].t2) )\n\n    // And again\n    var x2 = self.points[ir].location - self.points[ii].location\n    var omega2 = arctan( x2.inner(self.points[ii].t1), x2.inner(self.points[ii].t2) )\n\n    if (omega2>=omega1) return omega2-omega1\n    return omega2-omega1+2*Pi\n  }\n\n  recalculatefrontangles(poly) {\n    var minfa = 2*Pi\n    var np=poly.count()\n    for (i in 0...np) {\n      var pt = self.points[poly[i]]\n      if (pt.angle_changed && !pt.border_point) {\n        var ir = i+1\n        if (i==np-1) ir = 0\n        var fa =self.calculatefrontangle(poly[i-1], poly[i], poly[ir])\n        self.points[poly[i]].frontangle = fa\n        if (fa<minfa) minfa = fa\n        pt.angle_changed=false\n      }\n    }\n    return minfa\n  }\n\n  generatetriangles(poly, stepsize, force=nil) {\n    var omega = nil\n    var k, n=poly.count()\n\n    for (i in 0...n) {\n      if (isnil(omega) || self.points[poly[i]].frontangle < omega || poly[i]==force) {\n        omega = self.points[poly[i]].frontangle\n        k = i\n        if (poly[i]==force) break\n      }\n    }\n\n    // 1. Determine the neighbors of the selected vertex\n    var vk = poly[k]\n    var v1 = poly[k-1], v2\n    if (k==n-1) v2=poly[0] else v2=poly[k+1]\n\n    // 2. Determine number of triangles to be generated\n    var nnewt = floor(3*omega/Pi)+1\n    var deltaomega=omega/nnewt\n\n    // Correct for extreme cases\n    if (deltaomega<0.8 && nnewt>1) {\n        nnewt-=1; deltaomega=omega/nnewt\n    } else if (nnewt==1 && deltaomega>0.8) {\n        var xl = self.points[v1].location - self.points[v2].location\n        if (xl.norm()>1.2*stepsize) {\n          nnewt=2; deltaomega=deltaomega/2\n        }\n    } else if (omega<3) {\n        var xl = self.points[v1].location - self.points[vk].location\n        var s1 = xl.norm()\n        xl = self.points[v2].location - self.points[vk].location\n        var s2 = xl.norm()\n        if (s1<=0.5*stepsize || s2 <= 0.5*stepsize) nnewt=1\n    }\n\n    // 3. Generate the triangles\n    var vi[nnewt+1] // Store indices of new vertices\n    vi[0] = v1; vi[nnewt] = v2\n    if (nnewt==1) {\n      self.addface([v1,vk,v2])\n    } else {\n      var pt = self.points[vk]\n      // Project (v1-vk) onto tangent plane\n      var xl = self.points[v1].location - pt.location\n      var q0 = Matrix([xl.inner(pt.t1), xl.inner(pt.t2)])\n      q0 /= q0.norm()\n\n      for (i in 1...nnewt) {\n        var theta = i*deltaomega\n        var qi[2]\n        qi[0]=q0[0]*cos(theta) - q0[1]*sin(theta)\n        qi[1]=q0[0]*sin(theta) + q0[1]*cos(theta)\n\n        var x = pt.location+stepsize*(qi[0]*pt.t1+qi[1]*pt.t2)\n        var xi = self.surfacepoint(x)\n        vi[i]=self.addpoint(xi)\n      }\n\n      // Generate triangles\n      for (i in 0...nnewt) self.addface([vi[i], vi[i+1], vk])\n    }\n\n    // Correct the front polygon\n    for (i in 0..nnewt) self.points[vi[i]].angle_changed=true\n    poly.pop(k)\n    for (i in 1...nnewt) poly.insert(k, vi[nnewt-i])\n\n  }\n\n  splitpolygon(poly, i, j) {\n    self.points[poly[i]].exclude = true\n    self.points[poly[j]].exclude = true\n    self.points[poly[i]].angle_changed = true\n    self.points[poly[j]].angle_changed = true\n\n    var new = []\n    for (k in i..j) new.append(poly[k])\n    for (k in i+1...j) poly.pop(i+1)\n    return new\n  }\n\n  mergepolygons(poly, i, poly2, j) {\n    self.points[poly[i]].exclude = true\n    self.points[poly2[j]].exclude = true\n    self.points[poly[i]].angle_changed = true\n    self.points[poly2[j]].angle_changed = true\n\n    var cut = []\n    for (k in j...poly2.count()) cut.append(poly2[k])\n    for (k in 0..j) cut.append(poly2[k])\n    cut.append(poly[i])\n    for (p, k in cut) poly.insert(i+k+1, p)\n\n    self.front.remove(poly2)\n  }\n\n  pointcheck(poly1, i, poly2, j, stepsize) {\n    var pt1 = self.points[poly1[i]], pt2 = self.points[poly2[j]]\n    // Are either of the pts excluded from distance checks\n    if (pt1.exclude || pt2.exclude) return false\n\n    var sep = self.points[poly1[i]].location - self.points[poly2[j]].location\n    if (sep.norm() > stepsize) return false\n\n    // Todo: Check for \"bad\" near points by testing front angles\n\n    return true\n  }\n\n  proximitycheck(poly, stepsize) {\n    var split\n    var np = poly.count()\n    // Ensure there are no front angles < ~ 60deg\n    for (id in poly) if (self.points[id].frontangle<Pi/3) return\n\n    // Distance check within polygon\n    for (i in 0...np) {\n      // Check all points that are not neigbors or neighbors of neighbors\n      var npmax = np\n      if (i<2) npmax-=(2-i) // Deals with wraparound\n      for (j in i+3...npmax) {\n        if (self.pointcheck(poly, i, poly, j, stepsize)) {\n          split = self.splitpolygon(poly, i, j)\n          self.front.insert(0, split)\n          return\n        }\n      }\n    }\n\n    // Distance check between polygons\n    for (p in self.front) {\n      if (p==poly || p==split) continue\n\n      for (i in 0...np) for (j in 0...p.count()) {\n        if (self.pointcheck(poly, i, p, j, stepsize)) {\n          self.mergepolygons(poly, i, p, j)\n          //\n          return\n        }\n      }\n    }\n  }\n\n  build(start=nil, stepsize=0.5, maxiterations=nil) {\n    var iter = 0\n    var x = start\n    if (!start) x = Matrix([1,1,1])\n    self.initialhexagon(x, stepsize)\n\n    do {\n      var poly = self.front[-1]\n\n      // 1: Calculate front angles\n      self.recalculatefrontangles(poly)\n\n      // 2: Proximity check between polygons\n      self.proximitycheck(poly, stepsize)\n\n      // 3: Create new triangles\n      self.generatetriangles(poly, stepsize)\n\n      // Check if front polygon is now a triangle\n      if (poly.count()==3) {\n        self.addface(poly)\n        self.front.pop()\n      }\n      iter+=1\n      if (maxiterations) if (iter>maxiterations) break\n    } while (self.front.count()>0)\n\n    return super.build()\n  }\n}\n"
  },
  {
    "path": "modules/kdtree.morpho",
    "content": "/* ***************************************************************\n * KDTree\n * ======\n * This module implements the KDTree class for spatial search\n **************************************************************** */\n\nclass KDTreeNode {\n  init (location, left, right) {\n    self.location = location\n    self.left = left\n    self.right = right\n  }\n}\n\nclass KDTree {\n  init(pts) {\n    self.dimension = pts[0].dimensions()[0]\n    self.head=self.build(pts.clone(), 0)\n    self.tol=1e-15\n  }\n\n  build(pts, depth) { // Build a tree or subtree from a list of points\n    var np = pts.count()\n    if (np==0) return nil\n\n    var axis = mod(depth, self.dimension)\n\n    // Sort points to find median along current axis\n    pts.sort(fn (a, b) a[axis]-b[axis])\n    var ipivot=Int((np-1)/2)\n\n    // Identify points on the left and right of this plane\n    var left = [], right = []\n    for (i in 0...ipivot) left.append(pts[i])\n    for (i in ipivot+1...np) right.append(pts[i])\n\n    var node = KDTreeNode(pts[ipivot], self.build(left, depth+1), self.build(right, depth+1))\n\n    return node\n  }\n\n  donearest(pt, start, cdepth, bst, bdst) {\n    var depth = cdepth\n    var list = []\n\n    // Search the tree and track the nodes selected\n    for (var node = start; !isnil(node); ) {\n      list.append(node)\n      var pivot = mod(depth, self.dimension)\n      if (abs(pt[pivot] - node.location[pivot])<self.tol &&\n                (pt-node.location).norm()<self.tol) {\n        return node\n      } if (pt[pivot] < node.location[pivot]) {\n        node = node.left;\n      } else {\n        node = node.right;\n      }\n      depth+=1\n    }\n\n    // Unwind search and check whether a closer neighbor could exist\n    var best=bst, bdist=bdst\n    while (list.count()>0) {\n      depth-=1\n      var node = list.pop()\n      var dist = (pt-node.location).norm()\n      // If this point is closer, it becomes the best point\n      if (dist<bdist) { best = node; bdist = dist }\n\n      // Decide if we need to check the subtree on the other side of the split\n      var pivot = mod(depth, self.dimension)\n      var splitdist=pt[pivot]-node.location[pivot]\n\n      if (abs(splitdist)<bdist) { // We do\n        var alt\n        if (splitdist<0 && node.right) alt=self.donearest(pt, node.right, depth+1, best, bdist)\n        else if (node.left) alt=self.donearest(pt, node.left, depth+1, best, bdist)\n        if (alt) { // Check if this new point is even better\n          var adist = (pt-alt.location).norm()\n          if (adist<bdist) { best = alt; bdist = adist; }\n        }\n      }\n    }\n\n    return best\n  }\n\n  domaxdepth(node, startdepth) {\n    if (!node) return startdepth\n    return max([ self.domaxdepth(node.left, startdepth+1), self.domaxdepth(node.right, startdepth+1) ])\n  }\n\n  doprint(node, depth) {\n    var s = \"${depth}:\"\n    for (i in 0...depth) s+=\" \"\n    s+=\" [${node.location[0]}, ${node.location[1]}, ${node.location[2]}]\"\n    print s\n    if (node.left) self.doprint(node.left, depth+1)\n    if (node.right) self.doprint(node.right, depth+1)\n  }\n\n  dosearch(query, node, depth, result) {\n    // Check if this point satisfies the query\n    var include = true\n    for (i in 0...self.dimension) {\n      include = include && (node.location[i]>=query[i][0] && node.location[i]<=query[i][1])\n    }\n    if (include) result.append(node)\n\n    // Now check the subnodes\n    var pivot = mod(depth, self.dimension)\n    var aq = query[pivot]\n    var x = node.location[pivot]\n\n    if (x>=aq[0] && node.left) self.dosearch(query, node.left, depth+1, result)\n    if (x<=aq[1] && node.right) self.dosearch(query, node.right, depth+1, result)\n  }\n\n  insert(pt) { // Inserts a point into the tree\n    var depth = 0\n\n    for (var node = self.head, next; node!=nil; node=next) {\n      var pivot = mod(depth, self.dimension)\n\n      if (abs(pt[pivot] - node.location[pivot])<self.tol &&\n                (pt-node.location).norm()<self.tol) {\n        print \"Warning: duplicate node\"\n        return node\n      } else if (pt[pivot] < node.location[pivot]) {\n        next = node.left\n        if (!next) {\n          node.left = KDTreeNode(pt, nil, nil)\n          return node.left\n        }\n      } else {\n        next = node.right\n        if (!next) {\n          node.right = KDTreeNode(pt, nil, nil)\n          return node.right\n        }\n      }\n      depth+=1\n    }\n  }\n\n  ismember(pt) { // Tests if a point is in the tree.\n    var depth = 0\n\n    for (var node = self.head, next; !isnil(node); ) {\n      var pivot = mod(depth, self.dimension)\n      if (abs(pt[pivot] - node.location[pivot])<self.tol &&\n                (pt-node.location).norm()<self.tol) {\n        return node\n      } else if (pt[pivot] < node.location[pivot]) {\n        node = node.left\n      } else {\n        node = node.right\n      }\n      depth+=1\n    }\n    return false\n  }\n\n  maxdepth() { // Finds the maximum depth of the tree\n    return self.domaxdepth(self.head, 0)\n  }\n\n  nearest(pt) {\n    return self.donearest(pt, self.head, 0, nil, 1e255)\n  }\n\n  search(query) {\n    var result = []\n    self.dosearch(query, self.head, 0, result)\n    return result\n  }\n\n  prnt() {\n    self.doprint(self.head, 0)\n  }\n\n}\n"
  },
  {
    "path": "modules/meshgen.morpho",
    "content": "/* Meshgen - a simple morpho mesh generator\n *\n * Inspired by the distmesh algorithm presented in:\n *   A Simple Mesh Generator in MATLAB, Per-Olof Persson and Gilbert Strang, SIAM Rev., 46(2), 329–345 (2004)\n * Reimplemented using Morpho's optimization capabilities */\n\nimport functionals\nimport optimize\nimport meshtools\nimport delaunay\n\n/* ********************************************************\n * The Domain of interest is specified by a generalization\n * of a signed distance function: wherever the function is\n * positive will be meshed. Domains are constructed with\n * the function of interest and can be composed by set\n * operations intersection, union, difference.\n * ******************************************************** */\n\nclass Domain {\n  init (func) {\n    self.func = func\n  }\n\n  intersection(dom) { //\n    fn fint (x) {\n      return min(self.func(x), dom.func(x))\n    }\n    return Domain(fint)\n  }\n\n  difference(dom) {\n    fn fdiff (x) {\n      return min(self.func(x), -dom.func(x))\n    }\n    return Domain(fdiff)\n  }\n\n  union(dom) {\n    fn funion (x) {\n      return max(self.func(x), dom.func(x))\n    }\n    return Domain(funion)\n  }\n}\n\nfn CircularDomain(x0, r) {\n  var xx = x0 \n  if (islist(x0)) xx = Matrix(x0)\n\n  fn circle (x) {\n    var dx = x - xx\n    return (r^2-dx.inner(dx))\n  }\n\n  return Domain(circle)\n}\n\n// A Half Space defined by a plane at x0 and a normal n. \n// n is an \"outward\" normal, so points into the excluded region\nfn HalfSpaceDomain(x0, n) {\n  var xx0 = x0 \n  if (islist(x0)) xx0=Matrix(x0)\n  var nn = n\n  if (islist(nn)) nn=Matrix(n)\n\n  return Domain(fn (x) -(x-xx0).inner(nn))\n}\n\n// A rectangular domain defined by a list of ranges. \n// Works in arbitrary dimensions\nfn RectangularDomain(ranges) {\n  var dim = ranges.count() \n  var x0 = []\n  var w = []\n  for (r in ranges) {\n    var bnds = bounds(r)\n    w.append((bnds[1]-bnds[0])/2)\n    x0.append((bnds[1]+bnds[0])/2)\n  }\n\n  fn rectsdf (x) {\n    var sdf\n    for (xoff, k in x0) {\n      var f = 1-abs((x[k]-xoff)/w[k])\n      if (!sdf || f<sdf) sdf = f \n    }\n    return sdf \n  }\n\n  return Domain(rectsdf)\n}\n\n/* ********************************************************\n * Functionals to control mesh quality\n * ******************************************************** */\n\n/* -------------------------------------------------------\n * One sided hookean elasticity:\n * Energy F = 1/2 ((L-L0)/L0)^2 for L<L0, 0 otherwise\n * Construct with a Field of reference lengths L0\n * ------------------------------------------------------- */\nclass OneSidedHookeElasticity is Functional {\n  init(l0) {\n    self.ref = l0\n    super.init(1)\n  }\n\n  integrandfn(mesh, vert, id, el) {\n    var len = (vert.column(el[0])-vert.column(el[1])).norm()\n    var len0 = self.ref[self.grade, id]\n    if (len>len0) return 0\n    return ((len-len0)/len0)^2/2\n  }\n\n  gradientfn(mesh, vert, id, el, grd) {\n    var x0 = vert.column(el[0])\n    var x1 = vert.column(el[1])\n    var dx = x0-x1\n\n    var len = dx.norm()\n    var len0 = self.ref[self.grade, id]\n    if (len>len0) return\n    var g = dx*(len-len0)/(len0^2*len)\n\n    grd.setcolumn(el[0], g+grd.column(el[0]))\n    grd.setcolumn(el[1], -g+grd.column(el[1]))\n  }\n}\n\n/* -------------------------------------------------------\n * Evaluates a scalar potential at the midpoint of\n * an element. Will be used to detect elements inside\n * forbidden areas.\n * ------------------------------------------------------- */\nclass ScalarPotentialMidpoint is Functional {\n  init(hbar) {\n    self.func = hbar\n    super.init(1)\n  }\n\n  integrandfn(mesh, vert, id, el) {\n    var dim = el.count()\n    var x = vert.column(el[0])\n    for (var i=1; i<dim; i+=1) x+=vert.column(el[i])\n    x/=dim\n    return self.func(x)\n  }\n}\n\n/* ********************************************************\n * Shape optimizer\n * We subclass ShapeOptimizer to add a special convergence\n * criterion, which additionally tests if the mesh has\n * moved too far from its starting point\n * ******************************************************** */\n\nclass MGShapeOptimizer is ShapeOptimizer {\n  init (problem, mesh, ttol=0.1, localcheck=false) {\n    super.init(problem, mesh)\n    self.x0 = mesh.vertexmatrix().clone()\n    self.nv = self.x0.dimensions()[1]\n    self.ttol = ttol\n    self.localcheck = localcheck\n  }\n\n  hasconverged() { // Replacement method stops if mesh moved too far\n    var dx = self.target.vertexmatrix()-self.x0\n    \n    if (self.localcheck) {\n      // Did any vertex move too far? \n      for (id in 0...self.nv) {\n        if (dx.column(id).norm()>self.ttol) return true\n      }\n    }\n\n    if (dx.norm()>self.ttol) return true // old criterion\n    return super.hasconverged()\n  }\n\n  didconverge() { // Checks if we converged according to superclass\n    return super.hasconverged()\n  }\n}\n\n/* ********************************************************\n * Mesh generator\n * ******************************************************** */\n\nvar MshGnDim = Error(\"MshGnDim\", \"MeshGen only supports 2 or 3 dimensions\")\n\nclass MeshGen {\n  init (f, bbox, weight=nil, quiet=false, method=nil) {\n    self.func = f            // The function to call\n    if (!iscallable(f) && isobject(f) && f.has(\"func\")) self.func = f.func // You can give a Domain object directly\n\n    self.bbox = bbox         // Bounding box\n    self.weight = weight     // Weight function\n\n    fn defaultweight (x) { return 1 } // Provide a default weight function\n    if (isnil(weight)) self.weight = defaultweight\n\n    self.dim = bbox.count()  // Dimensionality of problem\n\n    self.quiet = quiet       // Whether to report output\n\n    var sep = []\n    for (range in bbox) sep.append(range[1]-range[0]) // Get stepsize\n    self.h0 = min(sep)       // Element separation\n\n    self.stepsize = self.h0/5    // Stepsize for optimizer \n    self.steplimit = self.h0/2   // Steplm for optimizer \n\n    self.mesh = nil          // Mesh\n    self.problem = nil       // Optimization problem\n\n    self.fscale = 1.2        // Internal pressure\n    if (self.dim>2) self.fscale = 1.1\n\n    self.etol = 1e-3 // Energy tolerance for optimization problem (it's deliberately loose)\n    self.ttol = 0.5 // How much motion of the vertices before retriangulation\n    self.xtol = 1e-12 // Tolerance for point comparisons\n\n    self.method = []         // Method selection\n    if (isstring(method)) self.method.append(method)\n    if (islist(method)) for (m in method) self.method.append(m)\n\n    self.maxiterations = 100\n  }\n\n  initialgridmesh() { // Create the initial mesh\n    if (!self.quiet) print \"Creating initial mesh on regular grid\"\n    var f = self.func\n    var vert = []\n    if (self.dim==2) {\n      for (v in self.bbox[1]) {\n        for (u in self.bbox[0]) {\n          if (f(Matrix([u,v]))>0) vert.append([u,v])\n        }\n      }\n    } else if (self.dim==3) {\n      for (w in self.bbox[2]) {\n        for (v in self.bbox[1]) {\n          for (u in self.bbox[0]) {\n            if (f(Matrix([u,v,w]))>0) vert.append([u,v,w])\n          }\n        }\n      }\n    } else {\n      MshGnDim.throw()\n    }\n    self.retriangulate(vert)\n  }\n\n  initialrandommesh() { // Create an initial mesh from random points\n    var vert = []\n    var bnds = []\n    var vol = 1 // compute the generalized volume of the element \n    for (b in self.bbox) {\n      var sep = b[b.enumerate(-1)-1]-b[0] // range of this dimension\n      bnds.append([b[0], sep])\n      vol *= sep\n    }\n\n    var n = ceil(vol/(self.h0)^(self.dim))\n    if (!self.quiet) print \"Creating initial mesh with ${n} random points\"\n\n    for (var nsuccess=0; nsuccess<n; ) {\n      var pt = []\n\n      for (i in 0...self.dim) pt.append(bnds[i][0]+bnds[i][1]*random())\n      \n      if (self.func(Matrix(pt))>0) {\n        vert.append(pt)\n        nsuccess+=1\n      }\n    }\n\n    self.retriangulate(vert)\n  }\n\n  selectbboxpts() {\n    var bnds = [], xtol = self.xtol\n    for (range,k in self.bbox) {\n        bnds.append(bounds(range))\n    }\n\n    fn selectOnBbox2D(x,y) {\n        return ( abs(x-bnds[0][0])<xtol || abs(x-bnds[0][1])<xtol ||\n                abs(y-bnds[1][0])<xtol || abs(y-bnds[1][1])<xtol )\n    }\n\n    fn selectOnBbox3D(x,y,z) {\n        return ( abs(x-bnds[0][0])<xtol || abs(x-bnds[0][1])<xtol ||\n                abs(y-bnds[1][0])<xtol || abs(y-bnds[1][1])<xtol ||\n                abs(z-bnds[2][0])<xtol || abs(z-bnds[2][1])<xtol )\n    }\n\n    var selfn = selectOnBbox2D\n    if (self.dim==3) selfn = selectOnBbox3D\n\n    return Selection(self.mesh, selfn) \n  }\n\n  retriangulate(vert) { // Retriangulate the mesh\n    if (!self.quiet) print \"Retriangulating.\"\n    var mb = MeshBuilder(dimension=self.dim)\n\n    for (x in vert) mb.addvertex(x)\n\n    var del = Delaunay(mb.vertices) // Triangulate\n    var tri = del.triangulate()\n\n    for (t in tri) { // Filter out any triangles whose midpoints are outside the feasible region\n      var mp = mb.vertices[t[0]]\n      for (i in 1...t.count()) mp+=mb.vertices[t[i]]\n      mp/=t.count()\n\n      if (self.func(mp)>0) {\n        mb.addelement(self.dim, t)\n      }\n    }\n\n    self.mesh=mb.build()\n    self.mesh.addgrade(1)\n  }\n\n  vertices() { // Extract a list of vertices from the current mesh\n    var v = self.mesh.vertexmatrix()\n    var vlist = []\n    for (id in 0...v.dimensions()[1]) vlist.append(v.column(id))\n    return vlist\n  }\n\n  setupproblem() { // Setup optimization problem\n    self.problem = OptimizationProblem(self.mesh)\n    // Build reference lengths\n    var lengths = Length().integrand(self.mesh)\n\n    // Evaluate the weight function at the midpoint\n    var hbar = ScalarPotentialMidpoint(self.weight).integrand(self.mesh)\n\n    // Calculate preferred lengths\n    var len0 = Field(self.mesh, grade=1)\n\n    fn lnorm(l, n) { // n-norm of a matrix\n      var sum = 0 \n      for (a in l) sum+=a^n\n      return sum^(1/n)\n    }\n\n    var lscale=self.fscale*(lnorm(lengths, self.dim)/lnorm(hbar, self.dim))\n    for (id in 0...self.mesh.count(1)) len0[id]=lscale*hbar[id]\n\n    // Impose elasticity\n    var lel = OneSidedHookeElasticity(len0)\n    self.problem.addenergy(lel)\n\n    // Use a one sided level set constraint to keep things in the domain\n    var ls\n    if (self.dim==2) { // [TODO]: Rewrite this with varargs when available\n      ls = ScalarPotential(fn (x,y) self.func(Matrix([x,y])))\n    } else if (self.dim==3) {\n      ls = ScalarPotential(fn (x,y,z) self.func(Matrix([x,y,z])))\n    }\n    self.problem.addlocalconstraint(ls, onesided=true)\n  }\n\n  optimize() { // Perform optimization\n    var opt = MGShapeOptimizer(self.problem, self.mesh, ttol=self.ttol*self.h0, localcheck=self.method.ismember(\"LocalCheck\"))\n    opt.fix(self.selectbboxpts())\n    // set steplimit such that the first step doesn't take vertices\n    // further than ttol\n\n    // Calculate the initial force\n    var frc = opt.totalforcewithconstraints()\n    var normfrc = frc.norm()\n    // set initial force to be the minimum of the provided steplimit and\n    // the stepsize required to move a distance of ttol\n    opt.steplimit = min(self.steplimit, self.ttol/normfrc)\n    // Similar for stepsize\n    opt.stepsize = min(self.stepsize, self.ttol/normfrc/5)\n    opt.etol = self.etol // Use a fairly loose convergence criterion\n    opt.quiet = self.quiet\n    if (self.method.ismember(\"FixedStepSize\")) {\n      opt.relax(100)\n    } else {\n      opt.linesearch(1)\n      opt.conjugategradient(100)\n    }\n\n    return opt.didconverge() // Returns true if we converged\n  }\n\n  build(outputdim=3) { // Build the mesh\n    if (self.method.ismember(\"StartRandom\")) {\n      self.initialrandommesh()\n    } else self.initialgridmesh()\n\n    for (iter in 0...self.maxiterations) {\n      if (!self.quiet) print \"Mesh generator iteration ${iter}.\"\n      self.setupproblem()\n\n      if (self.optimize()) break // Converged\n\n      self.retriangulate(self.vertices())\n    }\n\n    return self.mesh\n  }\n}\n"
  },
  {
    "path": "modules/meshslice.morpho",
    "content": "/* Meshslice - Slices a Mesh along a plane */\n\nimport meshtools\nimport delaunay\n\nvar _errSlcEmpty = Error(\"SlcEmpty\", \"No slice has yet been taken.\")\nvar _errSlcPrmType = Error(\"SlcPrmType\", \"Slice point and direction must be a List or Matrix.\")\n\nclass Intersection {\n  init(u, x) {\n    self.u = u\n    self.x = x\n  }\n}\n\nclass MeshSlicer {\n  init(mesh, ptol=1e-8) {\n    self.mesh = mesh\n    self.newmesh = nil \n    self.pt = nil\n    self.dirn = nil\n    self.basis = nil \n    self.ptol = ptol\n    self.pairs = nil\n    self.map = nil\n    self.vert = nil\n    self.mb = nil\n  }\n\n  basis() { // Creates a basis perpendicular to the dirn \n    var d = []\n    for (x in self.dirn) d.append(abs(x))\n    var order = d.order()\n\n    var e = [self.dirn]\n    for (i in 0..1) {\n      var ei = Matrix(self.dirn.count()) // Build a vector in that direction \n      ei[order[i]]=1\n      e.append(ei)\n    }\n\n    var f = self.orthogonalize(e) \n    self.basis = [ f[1], f[2] ]\n  }\n\n  orthogonalize(v) {  \n    var u = []\n    for (i in 0...v.count()) {\n      var ui = v[i]\n      for (uj in u) ui -= uj*uj.inner(v[i])/uj.inner(uj) \n      u.append(ui/ui.norm())\n    }\n    return u\n  } \n\n  setpt(Matrix pt, Matrix dirn) {\n    self.pt = pt\n    self.dirn = dirn\n    self.basis()\n  }\n\n  setpt(List pt, List dirn) {\n    self.pt = Matrix(pt)\n    self.dirn = Matrix(dirn)\n    self.basis()\n  }\n\n  setpt(pt, dirn) { _errSlcPrmType.throw() }\n\n  testintersection(indices) { // Finds if an element with indices intersects with the plane\n    var minus=false, plus=false\n    for (i in indices) {\n      var x = self.vert.column(i)\n      var t = sign((x - self.pt).inner(self.dirn))\n      if (t>0) plus=true\n      if (t<0) minus=true\n    }\n    return (plus && minus)\n  }\n\n  findintersection(i,j) { // returns the point between i,j that lies on the intersection plane\n    var x0 = self.vert.column(i)\n    var x1 = self.vert.column(j)\n\n    var t0 = (x0 - self.pt).inner(self.dirn)\n    var t1 = (x1 - self.pt).inner(self.dirn)\n    if (t0*t1>0) return nil // check if the points lie on the same side of the plane\n\n    var u = t0/(t0-t1)\n\n    return Intersection(u, (1-u)*x0 + u*x1)\n  }\n\n  vertexpairs(el) { // return all possible pairs of vertices from a list of vertices\n    var out = []\n    var n = el.count()\n    for (i in 0...n-1) for (j in i+1...n) {\n      var a=el[i], b=el[j]\n      if (a<b) out.append([a,b]) else out.append([b,a])\n    }\n    return out\n  }\n\n  checkpair(i,j) {\n    var intersection = self.pairs[i,j]\n    return (!intersection || isobject(intersection))\n  }\n\n  triangulate(newel) { // Triangulates a set of points \n    var pts = []\n    for (i in newel) {\n      var x = self.mb.vertices[i]-self.pt\n      // Project onto 2D plane\n      var xp = Matrix([x.inner(self.basis[0]), \n                       x.inner(self.basis[1])])\n      pts.append(xp)\n    }\n\n    // Do the triangulation \n    var del = Delaunay(pts)\n    del.sscale = 100 // Use a large outer simplex\n    var tri = del.triangulate() \n\n    var el = []\n\n    for (t in tri) { \n      var ntri = []\n      for (ix in t) { // Replace with the correct indices\n        ntri.append(newel[ix])\n      }\n      el.append(self.mb.addelement(2, ntri)) // and add the element\n    }\n\n    return el \n  }\n\n  project(g, el) { // project element of grade g onto next lower grade\n    var pairs = self.vertexpairs(el)\n    var ret // Return the projected element(s)\n\n    // Find the intersections of all the boundaries\n    for (pair in pairs) {\n      var i=pair[0], j=pair[1]\n      if (self.checkpair(i,j)) continue\n\n      var intersection = self.findintersection(i,j)\n      if (intersection) {\n        ret = self.mb.addvertex(intersection.x)\n        intersection.id = ret\n        self.pairs[i,j] = intersection // record the pair\n      }\n    }\n\n    if (g<2) return ret // Skip if a line element\n\n    // Create a new element\n    var newel = [] // First create a list of the vertices on the plane\n    for (pair in pairs) {\n      var intersection = self.pairs[pair[0],pair[1]]\n      if (isobject(intersection)) newel.append(intersection.id)\n    }\n\n    if (newel.count()==g) {\n      ret=self.mb.addelement(newel.count()-1, newel)\n    } else {\n      ret=self.triangulate(newel)\n    }\n\n    return ret\n  }\n\n  select(pt, dirn) { // Returns a selection of all elements intersecting with a plane\n    self.setpt(pt, dirn)\n    var sel = Selection(self.mesh)\n    for (g in 1..self.mesh.maxgrade()) { // Loop over grades\n      var conn = self.mesh.connectivitymatrix(0, g) // Get the connectivity matrix\n      if (!conn) continue\n\n      for (id in 0...self.mesh.count(g)) { // Loop over elements\n        var el = conn.rowindices(id)\n        if (self.testintersection(el)) { // Check if the element intersects\n          sel[g, id]=true\n        }\n      }\n    }\n    return sel\n  }\n\n  slice(pt, dirn) {\n    self.setpt(pt, dirn)\n    self.pairs = Sparse([]) // Used to keep track vertices created from pairs of vertices\n    self.map = List(0..self.mesh.maxgrade())\n    self.vert = self.mesh.vertexmatrix()\n    self.mb = MeshBuilder()\n\n    for (g in 1..self.mesh.maxgrade()) { // Loop over grades\n      var conn = self.mesh.connectivitymatrix(0, g) // Get the connectivity matrix\n      if (!conn) continue\n      var dict = Dictionary() \n\n      for (id in 0...self.mesh.count(g)) { // Loop over elements\n        var el = conn.rowindices(id)\n        if (self.testintersection(el)) { // Check if the element intersects\n          var nid = self.project(g, el)\n          dict[id] = nid\n        }\n      }\n\n      self.map[g] = dict \n    }\n\n    self.newmesh = self.mb.build()\n    return self.newmesh\n  }\n\n  slicefield(fld, interpolate=true) {\n    if (!self.newmesh) _errSlcEmpty.throw() \n\n    // Determine the new shape of the field \n    var shape = fld.shape() \n    var newshape = []\n    for (i in 1...shape.count()) newshape.append(shape[i])\n    if (shape[0]>0) newshape[0]=max(shape[0], shape[1])\n\n    var f = Field(self.newmesh, fld[0], grade=newshape) \n\n    // Interpolate vertex-based fields first \n    if (interpolate) for (k in 0...shape[0]) { \n      for (j in 0...self.pairs.dimensions()[1]) {\n        var ri=self.pairs.rowindices(j)\n        for (i in ri) {\n          var fi = fld[0,i,k], fj = fld[0,j,k]\n          var u = self.pairs[i,j].u\n          var id = self.pairs[i,j].id\n          f[0,id,k]=(1-u)*fi + u*fj\n        }\n      }\n    }\n\n    // Then copy information from higher elements\n    for (g in 1..self.mesh.maxgrade()) {\n      var nvar = shape[g]\n      var dict = self.map[g]\n      if (nvar==0) continue\n      if (!isdictionary(dict)) continue\n\n      for (id in dict) { // Copy the contents down into the next grade \n        var fid = dict[id]\n        if (isint(fid)) for (k in 0...nvar) f[g-1,fid,k]=fld[g,id,k]\n        else for (l in fid) for (k in 0...nvar) f[g-1,l,k]=fld[g,id,k]\n      }\n    }\n\n    return f \n  }\n}\n"
  },
  {
    "path": "modules/meshtools.morpho",
    "content": "/* *********************************************************\n * Meshtools \n * A module that provides various mesh creation, refinement\n * and visualization tools \n * ********************************************************* */\n\n// Dependencies \nimport constants\nimport kdtree\nimport delaunay\n\n// Error messages  \nvar _errMshBldDimIncnstnt = Error(\"MshBldDimIncnstnt\", \"Vertex dimension inconsistent with mesh dimension.\")\nvar _errMshBldDimUnknwn = Error(\"MshBldDimUnknwn\", \"Cannot add elements until a vertex has been added or MeshBuilder initialized with a specified dimension.\")\nvar _errMltVClstrFxd = Error(\"MltVClstrFxd\", \"More than one vertex in a selected cluster is fixed.\")\nvar _errMshMaxVrt = Error(\"MltMaxVrt\", \"Maximum number of vertices in Mesh exceeded.\")\n\nvar _MAX_VERTICES = 2147483647\n\n/* **************************\n * Manual mesh creation\n * ************************** */\n\n// Grades\nvar vertexGrade = 0\nvar lineGrade = 1\nvar areaGrade = 2\nvar volumeGrade = 3\n\nclass MeshBuilder {\n  init(dimension=nil) {\n    self.vertices = []\n    self.dimension = dimension\n    self.elements = nil \n    if (isnumber(dimension)) self.initdim(dimension)\n  }\n\n  initdim(dimension) {\n    self.dimension = dimension\n    self.elements = Array(dimension)\n    for (i in 0...dimension) {\n      self.elements[i]=[]\n    }\n  }\n\n  addvertex(v) {\n    if (self.vertices.count()==_MAX_VERTICES) _errMshMaxVrt.throw() \n    var x = v\n    if (islist(v)) x = Matrix(v)\n    if (self.dimension==nil) self.initdim(x.count())\n\n    if (x.count()!=self.dimension) _errMshBldDimIncnstnt.throw()\n    self.vertices.append(x)\n    return self.vertices.count()-1\n  }\n\n  addelement(grade, el) {\n    if (!self.elements) _errMshBldDimUnknwn.throw()\n    self.elements[grade-1].append(el)\n    return self.elements[grade-1].count()-1\n  }\n\n  addedge(el) { return self.addelement(lineGrade, el) }\n\n  addface(el) { return self.addelement(areaGrade, el) }\n  \n  addvolume(el) { return self.addelement(volumeGrade, el) }\n\n  makeconnectivity(el) {\n    if (el.count()==0) return nil\n    var s = Sparse(0,0)\n    for (lst, id in el) for (v in lst) s[v,id]=1\n    return s\n  }\n\n  build() {\n    var m = Mesh();\n\n    var nv = self.vertices.count()\n\n    var vert = Matrix(self.dimension, nv)\n    for (x, i in self.vertices) vert.setcolumn(i, x)\n\n    m.setvertexmatrix(vert)\n\n    for (el, i in self.elements) {\n        var s = self.makeconnectivity(el)\n        if (s) m.addgrade(i+1, s)\n    }\n\n    return m\n  }\n}\n\n/* ******************************************\n * A few constructors to create simple meshes\n * ****************************************** */\n\n/** Creates a mesh from a parametric function.\n * @param[in] f - a function that returns the vertex position as a list\n * @param[in] range - parameter range to use\n * @param[in] closed - whether to close the mesh  */\nfn LineMesh(f, range, closed=false) {\n  var dim = f(range[0]).count()\n  var mb = MeshBuilder(dimension=dim)\n\n  for (t in range) mb.addvertex(f(t))\n\n  var nlines = range.count()-1\n\n  for (i in 0...nlines) mb.addedge([i,i+1])\n  if (closed) mb.addedge([0,nlines])\n\n  return mb.build()\n}\n\n/** Creates a mesh from a parametric function.\n * @param[in] f - a function that returns the vertex position as a list\n * @param[in] range - parameter range to use\n * @param[in] range - parameter range to use\n * @param[in] closed - whether to close the mesh  */\nfn AreaMesh(f, r1, r2, closed=false) {\n  var dim = f(r1[0],r2[0]).count()\n  var mb = MeshBuilder(dimension=dim)\n\n  var nu = r1.count(), nv = r2.count() // How many points\n  if (Float(nu)*Float(nv)>_MAX_VERTICES) _errMshMaxVrt.throw() \n\n  for (v in r2) for (u in r1) mb.addvertex(f(u,v))\n\n  var np = nu*nv\n  var nfaces = (nu-1)*(nv-1)\n\n  for (j in 0...nv-1) {\n    for (i in 0...nu-1) {\n      mb.addface([i+j*nu, i+1+j*nu, i+nu+j*nu])\n      mb.addface([i+1+j*nu, i+nu+j*nu, i+1+nu+j*nu])\n    }\n  }\n\n  // Close boundaries if selected\n  var cu = closed, cv = closed\n  if (islist(closed)) { cu=closed[0]; cv=closed[1] }\n\n  if (cu) {\n    var n=nu-1 // Connect right column to left column\n    for (j in 0...nv-1) {\n      mb.addface([n+j*nu, n+1+j*nu, 0+j*nu])\n      mb.addface([n+j*nu, 0+nu+j*nu, n+nu+j*nu])\n    }\n  }\n  if (cv) {\n    var n=nv-1 // Connect top row to bottom row\n    for (i in 0...nu-1) {\n      mb.addface([i+n*nu, i+1+n*nu, i])\n      mb.addface([i+1+n*nu, i, i+1])\n    }\n  }\n  if (cu && cv) {  // For toroidal geometry, connect top right to bottom left\n    mb.addface([nu*nv-1, (nv-1)*nu, nu-1])\n    mb.addface([0, (nv-1)*nu, nu-1])\n  }\n\n  return mb.build()\n}\n\n/** Creates a mesh from a polyhedron specified as\n  @param[in] list - a list of vertices\n  @param[in] faces - list of lists of vertices corresponding to each face\n  @returns a mesh */\nfn PolyhedronMesh(list, faces) {\n  var dim=list[0].count()\n  var mb=MeshBuilder(dimension=dim)\n  var nv=list.count() // Number of vertices in polyhedron\n\n  // Vertices\n  for (x in list) mb.addvertex(x)\n  for (f in faces) { // Add midpoints\n    var pt = Matrix(3)\n    for (i in f) pt.acc(1, Matrix(list[i]))\n    mb.addvertex(pt/f.count())\n  }\n\n  // Faces\n  for (face, k in faces) { // Loop over faces\n    var fv = face.count()\n    for (i in 0...fv) { // Loop over the vertex indices and create a triangle for every pair\n      var v1 = face[0]\n      if (i<fv-1) v1=face[i+1]\n      mb.addface([face[i], v1, k+nv])\n    }\n  }\n\n  return mb.build()\n}\n\n// Constructs a mesh from a point cloud\nfn DelaunayMesh(pts, outputdim=3) {\n  var del = Delaunay(pts)\n  var mb = MeshBuilder()\n\n  for (x in del.pts) {\n    var p = Matrix(outputdim)\n    for (xi,i in x) p[i]=xi\n    mb.addvertex(p)\n  }\n\n  for (tri in del.triangulate()) {\n    mb.addelement(del.dim, tri)\n  }\n\n  return mb.build()\n}\n\n/* **************************************************\n * Change the dimension in which the mesh is embedded \n * ************************************************** */\n\nfn ChangeMeshDimension(mesh, dim) {\n    var vert = mesh.vertexmatrix()\n    var odim = vert.dimensions()[0]\n    var nv = vert.dimensions()[1]\n    var copy = 0...min(odim, dim)\n    var mb = MeshBuilder()\n\n    for (i in 0...nv) {\n        var pt = Matrix(dim)\n        var v = vert.column(i)\n        for (j in copy) pt[j]=v[j]\n        mb.addvertex(pt)\n    }\n\n    if (dim<odim) odim = dim \n\n    for (grade in 1..odim) {\n        var conn = mesh.connectivitymatrix(0,grade)\n\n        if (!conn) continue\n        for (i in 0...conn.dimensions()[1]) {\n            mb.addelement(grade, conn.rowindices(i))\n        }\n    }\n\n    return mb.build() \n}\n\n/* **************************\n * Equiangulation\n * ************************** */\n\n/* Tests whether to flip an edge\n   @param[in] v - vertex matrix of edge\n   @param[in] ev - vertex indices of the edge\n   @param[in] cv - vertex indices of the vertices in the two triangles not\n                   present in the edge\n   @returns true if the edge should be flipped */\nfn _equiangulatetest (v, ev, cv) {\n  var a = (v.column(ev[0])-v.column(ev[1])).norm(), // Length of common edge\n      b = (v.column(ev[0])-v.column(cv[0])).norm(), // Edges of face 1\n      c = (v.column(ev[1])-v.column(cv[0])).norm(),\n      d = (v.column(ev[0])-v.column(cv[1])).norm(), // Edges of face 2\n      e = (v.column(ev[1])-v.column(cv[1])).norm()\n   return ((b*b + c*c - a*a)*d*e + (d*d + e*e - a*a)*b*c) < 0\n}\n\n/* Finds vertices in a pair of triangles not in a common edge\n   @param[in] ev - list of two vertex ids\n   @param[in] faces - faces definition matrix\n   @param[in] ef - ids of two adjacent faces\n   @returns a list of vertices present in triangles ef but not part of ev */\nfn _cvert(ev, faces, ef) {\n  var cv = []\n  for (i in 0..1) for (v in faces.rowindices(ef[i])) if (!ev.ismember(v)) cv.append(v)\n  return cv\n}\n\n/* Flip the edge */\nfn _edgeflip(m, iedge, edges, faces, ev, cv, ef) {\n  edges.setrowindices(iedge, cv)\n  faces.setrowindices(ef[0], [ev[0], cv[0], cv[1]])\n  faces.setrowindices(ef[1], [ev[1], cv[0], cv[1]])\n  m.resetconnectivity()\n}\n\nfn equiangulate(m, quiet=false, fix=nil) {\n  var vert = m.vertexmatrix()\n  var edges = m.connectivitymatrix(0,1)\n  var faces = m.connectivitymatrix(0,2)\n  var edgetoface = m.connectivitymatrix(2,1)\n  var verttoedge = m.connectivitymatrix(1,0)\n  var ne = edges.dimensions()[1]\n  var nflip = 0\n\n  for (iedge in 0...ne) {\n    if (fix && fix[1,iedge]) continue\n    var ev=edges.rowindices(iedge) // vertices for this edge\n\n    if (verttoedge.rowindices(ev[0]).count()<4 ||\n        verttoedge.rowindices(ev[1]).count()<4) continue // skip if connectivity deficient\n\n    var ef=edgetoface.rowindices(iedge) // faces attached to this edge\n    if (ef.count()!=2) continue  // Only edges that are connected to two triangles\n\n    var cv=_cvert(ev, faces, ef)\n    if (_equiangulatetest(vert, ev, cv)) {\n      _edgeflip(m, iedge, edges, faces, ev, cv, ef)\n      edgetoface = m.connectivitymatrix(2,1) // Recalculate connectivity\n      verttoedge = m.connectivitymatrix(1,0) // Recalculate connectivity\n      nflip+=1\n    }\n  }\n\n  if (!quiet) print \"Equiangulate: ${nflip} edges flipped.\"\n\n  return nil\n}\n\n\n//////////////////////\n// Utility functions\n//////////////////////\n\n// Compare two lists \nfn _cmpel(a,b) {\n  if (a.count()!=b.count()) return false \n  for (x in a) if (!b.ismember(x)) return false \n  return true \n}\n\n// Determine if list a is already in a list of lists \nfn _elinlist(ellist, a) {\n  for (el in ellist) {\n    if (_cmpel(el, a)) return true \n  }\n  return false \n}\n\n/* **********************************\n * ElementList\n * ********************************** */\n\nvar _errellstgrd = Error(\"ElLstGrd\", \"Element doesn't match grade of ElementList.\")\n\n/* A data structure to (relatively) efficiently find duplicate elements. \n   Presently uses a Sparse to do this; a Dictionary would be better in future */\nclass ElementList {\n  init(mesh, grade, sort=false) {\n    self.grade = grade \n    self.sort = sort \n    var nv = mesh \n    if (ismesh(mesh)) nv = mesh.count() \n    self._store = Sparse(nv, nv)\n    self._count = 0 \n  }\n\n  _add(el) {\n    if (self._match(el)) return \n\n    if (self.grade==1) {\n      self._store[el[0],el[1]] = true \n    } else {\n      var st = el[2..self.grade] // Use the remaining indices \n      var lst = self._store[el[0],el[1]]\n      if (islist(lst)) lst.append(st) // If there's a list already, store it \n      else self._store[el[0],el[1]]=[st] // Otherwise, just return it \n    }\n    self._count+=1 \n  }\n\n  _match(el) {\n    var entry = self._store[el[0],el[1]]\n    if (isbool(entry)) return true // If the entry is a boolean (grade 1) then we have a match\n    if (islist(entry)) { \n      return _elinlist(entry, el[2..self.grade]) // If not, we have to match the remaining indices in the list \n    }\n    return false \n  }\n\n  _checksrt(el) { // Checks to see if we need to sort the element\n    if (!self.sort) return el \n    var ell = el.clone()\n    ell.sort()\n    return ell\n  }\n\n  addelement(el) { // Adds an element to the structure \n    self._add(self._checksrt(el))\n  }\n\n  ismember(el) { // Checks if an element is already a member \n    if (el.count()!=self.grade+1) _errellstgrd.throw() \n    return self._match(self._checksrt(el))\n  }\n\n  count() {\n    return self._count\n  }\n}\n\n/* **************************\n * Merging meshes\n * ************************** */\n\nclass MeshMerge is MeshBuilder {\n  init (meshes, tol=1e-12) {\n    self.meshes = meshes\n    self.tree = nil\n    self.tol = tol // Tolerance below which vertices will be considered identical\n    self.nv = 0\n    self.elists = nil \n    super.init()\n  }\n\n  maxgrade() {\n    var lst = []\n    for (m in self.meshes) lst.append(m.maxgrade())\n    return max(lst)\n  }\n\n  addmesh(msh) {\n    if (isnil(self.meshes)) self.meshes = []\n    if (!islist(self.meshes)) self.meshes = [self.meshes]\n    if (islist(msh)) for (el in msh) self.meshes.append(el)\n    else self.meshes.append(msh)\n  }\n\n  addvertex(x) {\n    if (self.tree) {\n      // Does this vertex already exist?\n      var nrst = self.tree.nearest(x)\n      if ((nrst.location - x).norm() > self.tol) {\n        // No, so create a new one\n        var new = self.tree.insert(x)\n        new.indx = super.addvertex(x)\n        return new.indx\n      } else { // Yes, so just return the index\n        return nrst.indx\n      }\n    } else { // If the tree wasn't created yet, create it and add the vertex\n      self.tree = KDTree([x])\n      self.tree.head.indx = super.addvertex(x)\n      return self.tree.head.indx\n    }\n  }\n\n  mergevertices(m) { // Merges vertices into the mesh\n    var vert = m.vertexmatrix()\n    var vdict = Dictionary()\n    for (vid in 0...m.count()) {\n      vdict[vid] = self.addvertex(vert.column(vid))\n    }\n    return vdict\n  }\n\n  _initelists() {\n    var mg = self.maxgrade()\n    self.elists = Array(mg+1)\n    var nv = self.vertices.count()\n    for (g in 1..mg) self.elists[g] = ElementList(nv, g, sort=true) \n  }\n\n  _clearelists() {\n    self.elists = nil \n  }\n\n  addelement(g, el) {\n    var elist = self.elists[g]\n    if (elist.ismember(el)) return \n    elist.addelement(el)\n    super.addelement(g, el)\n  }\n\n  mergegrade(m, g, dict) { // Merges elements of grade g into the mesh\n    var conn = m.connectivitymatrix(0, g)\n    for (id in 0...m.count(g)) {\n        var newel = [] // Convert the indices into new vertex indices\n        var duplicate = false // Check if the merged element contains duplicate vertices\n        \n        for (elid in conn.rowindices(id)) {\n          if (newel.ismember(dict[elid])) duplicate=true \n          newel.append(dict[elid])\n        }\n        \n        if (!duplicate) self.addelement(g, newel)\n    }\n  }\n\n  merge() {\n    var vdict = [] \n    for (m in self.meshes) { // First merge the vertices\n      vdict.append(self.mergevertices(m))\n    } \n\n    self._initelists() // Prepare lists to keep track of elements\n\n    for (m, k in self.meshes) { // Then merge all higher grades\n      for (grade in 1..m.maxgrade()) { \n        self.mergegrade(m, grade, vdict[k])\n      }\n    }\n\n    self._clearelists() \n\n    return self.build() // Build the mesh\n  }\n}\n\n/* ********************************\n * Simple refinement \n ******************************** */\n\n// Calculate the midpoint of an edge\nfn _midpoint(vertices, edges, i) {\n  var edge = edges.rowindices(i)\n  return 0.5*(vertices.column(edge[0])+vertices.column(edge[1]))\n}\n\n// Swaps elements of an indexable object\nfn _swap(e, i, j) {\n  var swp = e[i]; e[i] = e[j]; e[j]=swp\n}\n\n/** Refines a mesh */\nfn refinemesh(m) {\n  var vertices = m.vertexmatrix()\n  var edges = m.connectivitymatrix(0, 1)\n  if (isnil(edges)) { // If no edges are present, we should try to add them\n    m.addgrade(1)\n    edges = m.connectivitymatrix(0, 1)\n    if (isnil(edges)) { print \"Failed to add edges\"; return }\n  }\n  var faces = m.connectivitymatrix(0, 2)\n\n  // Identify number of vertices, edges and faces in the old mesh\n  var dim = vertices.dimensions()[0]\n  var nv = vertices.dimensions()[1]\n  var nl = edges.dimensions()[1]\n  var nf = faces.dimensions()[1]\n\n  var new = Mesh()\n\n  // Each refined edge contributes a new vertex\n  var newvertices = Matrix(dim, nv+nl)\n  for (i in 0...nv) newvertices.setcolumn(i, vertices.column(i))\n  for (i in 0...nl) {\n    newvertices.setcolumn(nv+i, _midpoint(vertices, edges, i))\n  }\n  new.setvertexmatrix(newvertices)\n\n  // Each edge becomes two edges\n  var newedges = Sparse(nv+nl, nl) // Size is automatically updated\n  var iedge = nl\n  for (i in 0...nl) {\n    var edge = edges.rowindices(i)\n    newedges[edge[0], i]=1   // ] Edge 0\n    newedges[nv+i, i]=1      // ]\n\n    newedges[nv+i, iedge]=1     // ] Edge 1\n    newedges[edge[1], iedge]=1  // ]\n    iedge+=1\n  }\n\n  // Refine faces if present. Creates a canonical order for the face\n  /*         a (e0[0])\n           /   \\\n    e0   x ---  y  e1\n        /  \\ /  \\\ne0[1] b --- z -- c\n            e2\n  */\n  var newfaces = Sparse(nv+nl, 4*nf)\n  var iface = nf // Count over new triangles\n  if (!isnil(faces)) {\n    var faceedge = m.connectivitymatrix(1,2)\n\n    for (i in 0...nf) {\n      var fvert = faces.rowindices(i) // Vertices in this face\n      var fedge = faceedge.rowindices(i) // Edges in this face\n\n      var evert[3] // Vertices for each edge\n      for (f, i in fedge) evert[i]=edges.rowindices(f)\n      // evert 0 defines vertices a and b\n      var va=evert[0][0], vb=evert[0][1], vc=evert[1][0]\n      if (evert[0].ismember(vc)) vc = evert[1][1]\n      // The vertices are now in canonical order\n\n      // does edge 1 connect with a? if not swap\n      if (!evert[1].ismember(va)) _swap(fedge, 1, 2)\n      // The edges are now in a canonical order, so that evert[1] connects to the first element of evert[0]\n      var vx = nv+fedge[0], vy = nv+fedge[1], vz = nv+fedge[2]\n\n      // Triangle a-x-y\n      newfaces[va,i]=1; newfaces[vx,i]=1; newfaces[vy,i]=1\n      // Triangle b-x-z\n      newfaces[vb,iface]=1; newfaces[vx,iface]=1; newfaces[vz,iface]=1; iface+=1\n      // Triangle c-y-z\n      newfaces[vc,iface]=1; newfaces[vy,iface]=1; newfaces[vz,iface]=1; iface+=1\n      // Triangle x-y-z\n      newfaces[vx,iface]=1; newfaces[vy,iface]=1; newfaces[vz,iface]=1; iface+=1\n\n      // Edge x-y\n      newedges[vx, iedge]=1; newedges[vy, iedge]=1; iedge+=1\n      // Edge x-z\n      newedges[vx, iedge]=1; newedges[vz, iedge]=1; iedge+=1\n      // Edge y-z\n      newedges[vy, iedge]=1; newedges[vz, iedge]=1; iedge+=1\n    }\n  }\n\n  new.addgrade(1, newedges)\n  new.addgrade(2, newfaces)\n\n  return new\n}\n\n/* ********************************\n * Advanced refinement \n ******************************** */\n\n/* **********************************\n * Base class for adaptive refinement \n * ********************************** */\n\n/* Subclasses must implement their own version of adaptmesh, \n   which returns a list of dictionaries, one for each grade of element, \n   Each dictionary has: \n    * keys corresponding to the new element ids; \n    * values can be either \n      - a single elementid in the old mesh OR\n      - a list of elements which will be averaged over \n*/ \n\nclass MeshAdaptiveRefiner { \n  init (target) { // The target can either be a single mesh or a collection of objects\n    self.target = target\n    self.refinemap = nil \n    self.new = nil\n  }\n\n  mesh() { // Returns the mesh being refined \n    if (ismesh(self.target)) return self.target\n    else if (islist(self.target)) {\n      for (m in self.target) if (ismesh(m)) return m\n    }\n    return nil\n  }\n\n  // Subclasses should replace this method with their own version\n  adaptmesh(selection) { }\n\n  adaptfield(field) { // Maps a field onto a new mesh\n    var mesh = self.mesh()\n    var prototype = field.enumerate(0)\n    if (isobject(prototype)) prototype=prototype.clone()\n    var shape = field.shape()\n    var fespace = field.finiteelementspace() \n    var result\n\n    if (self.refinemap && prototype) {\n      result = Field(self.new, prototype, grade=shape, finiteelementspace=fespace)\n\n      for (g in 0...shape.count()) {\n        if (shape[g]==0) continue\n        var dict = self.refinemap[g]\n\n        for (newid in dict) { // Loop over the new ids \n          var oldid = dict[newid]\n\n          if (islist(oldid)) { // If this oldid is a list, average over these values\n            var sum, nn = oldid.count()\n            if (isfloat(prototype)) sum = 0\n            if (nn==0) continue\n            for (i in 0...nn) sum+=field[g, oldid[i]]\n            sum/=oldid.count()\n            result[g, newid] = sum\n          } else {\n            result[g, newid] = field[g, oldid]\n          }\n        }\n      }\n    }\n\n    return result\n  }\n\n  selectdown(el, oldsel, newsel) {\n    var nv=self.mesh().count() // Max vertexid in old mesh \n    var vmap = self.refinemap[0]\n    var vselected=false \n\n    for (vid in el) { // Check at least one vertex is selected \n      if (vid<nv && oldsel[0,vmap[vid]]) vselected=true \n    }\n\n    if (vselected) for (vid in el) {\n      if (vid<nv && !oldsel[0,vmap[vid]]) continue \n      newsel[0,vid]=true \n    }\n  }\n\n  adaptselection(sel) { // Maps a selection onto a new mesh\n    var result\n\n    if (self.refinemap) {\n      result = Selection(self.new)\n\n      for (g in 0..self.new.maxgrade()) {\n        var dict = self.refinemap[g]\n        if (sel.count(g)==0) continue\n        var conn = self.new.connectivitymatrix(0,g)\n\n        for (id in dict.keys()) {\n          if (isint(dict[id]) && sel[g, dict[id]]) {\n            result[g, id]=true\n            if (g>0) self.selectdown(conn.rowindices(id), sel, result) \n          }\n        }\n      }\n    }\n\n    return result\n  }\n\n  adapt(selection=nil) { // Base adaptive refinement sequence\n    var newmesh = self.adaptmesh(selection)\n\n    // Returns a refinement dictionary that maps old objects to new\n    var dict = { self.mesh() : self.new } \n\n    if (islist(self.target)) { \n      for (el in self.target) {\n        if (isfield(el)) dict[el] = self.adaptfield(el)\n        if (isselection(el)) dict[el] = self.adaptselection(el)\n      }\n    }\n\n    return dict\n  }\n}\n\n/* **************************\n * Refinement\n * ************************** */\n\nclass MeshRefiner is MeshAdaptiveRefiner {\n  init (target) {\n    self.target = target\n    self.refinemap = nil\n    self._clearelists()\n    self.new = nil\n  }\n\n  mesh() {\n    if (ismesh(self.target)) return self.target\n    else if (islist(self.target)) {\n      for (m in self.target) if (ismesh(m)) return m\n    }\n    return nil\n  }\n\n  _initelists(maxgrade, nv) {\n    self.elists = Array(maxgrade+1)\n    for (g in 1..maxgrade) self.elists[g] = ElementList(nv, g, sort=true) \n  }\n\n  _clearelists() {\n    self.elists = nil \n  }\n\n  split(id, el, vert, dict) { // split elements, generating new vertices\n    var nv = el.count()\n    for (i in 0...nv) {     // Loop over distinct pairs \n      for (j in i+1...nv) { // \n        var midpoint = (vert.column(el[i])+vert.column(el[j]))/2\n        if (!self.tree.ismember(midpoint)) { // Check if already exists \n          var id = self.mb.addvertex(midpoint) // Add vertex \n          self.tree.insert(midpoint).id = id\n          dict[id]=[el[i],el[j]] // dict[new vertex] -> [oldvertices]\n        }\n      }\n    }\n  }\n\n  addelement(srcid, grade, el, dict) {\n    if (self.elists[grade].ismember(el)) return nil\n    var id = self.mb.addelement(grade, el)\n    self.elists[grade].addelement(el)\n    if (isdictionary(dict)) dict[id] = srcid \n    return id \n  }\n\n  refine1(id, el, vert, nel, dict) { // refine edges\n    var midpoint = (vert.column(el[0])+vert.column(el[1]))/2\n    var mp = self.tree.ismember(midpoint) \n    if (mp) {\n      self.addelement(id, lineGrade, [el[0], mp.id], dict) \n      self.addelement(id, lineGrade, [mp.id, el[1]], dict) \n    } // refinement dictionary key: new edge id -> old edge id \n    return isobject(mp)\n  }\n \n  refine2(id, el, vert, nel, dict) { // refine faces\n    var nv = el.count()\n    var refedge = []\n\n    for (i in 0...nv) { // Generate midpoints\n      for (j in i+1...nv) {\n        var midpoint = (vert.column(el[i])+vert.column(el[j]))/2\n        var node = self.tree.ismember(midpoint)\n        if (node) {\n          refedge.append([el[i], el[j], node.id])\n        }\n      }\n    }\n\n    /* Three possible outcomes of refinement:\n            .            .            .\n          / | \\        / | \\        /   \\\n         /  |  \\      /  |  *      * --- *\n        /   |   \\    /   | / \\    /  \\  /  \\\n      . --- * -- . . --- * -- . . --- * -- . */\n\n    var nref = refedge.count()\n    if (nref==0) {\n      return false \n    } else if (nref==1) {\n      var tid\n      for (id in el) if (!refedge[0].ismember(id)) tid = id\n\n      self.addelement(id, areaGrade, [refedge[0][0], refedge[0][2], tid], dict)\n      self.addelement(id, areaGrade, [refedge[0][2], refedge[0][1], tid], dict)\n\n      if (nel[1]>0) {\n        self.addelement(id, lineGrade, [refedge[0][2], tid], nil)\n      }\n    } else if (nref==2) {\n      var shareid, xid, yid\n      for (id in el) {\n        if (refedge[0].ismember(id) && refedge[1].ismember(id)) shareid = id\n        if (refedge[0].ismember(id) && !refedge[1].ismember(id)) xid = id\n        if (!refedge[0].ismember(id) && refedge[1].ismember(id)) yid = id\n      }\n\n      self.addelement(id, areaGrade, [shareid, refedge[0][2], refedge[1][2]], dict)\n      self.addelement(id, areaGrade, [xid, refedge[0][2], refedge[1][2]], dict)\n      self.addelement(id, areaGrade, [xid, yid, refedge[1][2]], dict)\n\n      if (nel[1]>0) {\n        self.addelement(id, lineGrade, [refedge[1][2], xid], nil)\n        self.addelement(id, lineGrade, [refedge[0][2], refedge[1][2]], nil)\n      }\n    } else if (nref==3) {\n      self.addelement(id, areaGrade, [el[0], refedge[0][2], refedge[1][2]], dict)\n      self.addelement(id, areaGrade, [el[1], refedge[0][2], refedge[2][2]], dict)\n      self.addelement(id, areaGrade, [el[2], refedge[1][2], refedge[2][2]], dict)\n      self.addelement(id, areaGrade, [refedge[0][2], refedge[1][2], refedge[2][2]], dict)\n\n      if (nel[1]>0) {\n        self.addelement(id, lineGrade, [refedge[0][2], refedge[1][2]], nil)\n        self.addelement(id, lineGrade, [refedge[1][2], refedge[2][2]], nil)\n        self.addelement(id, lineGrade, [refedge[2][2], refedge[0][2]], nil)\n      }\n    }\n    return true \n  }\n\n  refine3(id, el, vert, nel, dict) { // refine volumes \n    var nv = el.count() // Number of vertices in the element\n    var pts = [] // Points defining the element\n    var localmap = Dictionary() // Maps local ids in pts to correct ids \n\n    for (i in 0...nv) { // Find vertex positions and ids\n      pts.append(vert.column(el[i])) \n      localmap[i]=el[i]\n    }\n\n    for (i in 0...nv) {      // Loop over all unique pairs \n      for (j in i+1...nv) {  // and find midpoints \n        var midpoint = (vert.column(el[i])+vert.column(el[j]))/2\n        var node = self.tree.ismember(midpoint) // Find whether this midpoint actually exists\n        if (node) {\n          localmap[pts.count()]=node.id\n          pts.append(midpoint)\n        } \n      }\n    }\n\n    var tri = Delaunay(pts).triangulate() // Now perform the Delaunday triangulation\n\n    fn _mapel(el) { // Map local ids for this element onto the correct ids \n      var ntri = [] \n      for (id in el) ntri.append(localmap[id]) \n      return ntri \n    } \n\n    fn _checkel(el) { // Check if this element contains any old vertices \n      for (id in el) if (id<nv) return false \n      return true \n    }\n\n    for (t in tri) { // Loop over the generated tetrahedra from the triangulation\n      self.addelement(id, volumeGrade, _mapel(t), dict)\n\n      for (g in 1..2) { // Create \"missing\" elements of lower grade \n        if (nel[g]==0) continue // Skip empty grades \n        var elements = t.sets(g+1) // Generate all possible elements \n        // Generate new elements only from possibilites that use only new vertices\n        for (e in elements) { \n          if (_checkel(e)) {\n            self.addelement(id, g, _mapel(e), nil)\n          }\n        }\n      }\n    }\n    return true \n  }\n\n  // vdict keys: old vertex ids -> values: new vertex ids\n  adaptmesh(selection) {\n    var m = self.mesh()\n    self.mb = MeshBuilder()\n    var vertices = m.vertexmatrix()\n    var dim = vertices.dimensions()[0]\n    var nv = vertices.dimensions()[1]\n    var refmap = [ ] // Dictionary describing how element ids are mapped over\n    var nel[dim+1] // Number of elements in each grade\n\n    for (g in 0..dim) nel[g]=m.count(g)\n    var vlist = []\n    var vdict = Dictionary() // Vertex map dictionary \n    for (vid in 0...nv) { // Loop over vertexids in old mesh \n      var x = vertices.column(vid) // Get the vertex position\n      vlist.append(x) \n      var newid = self.mb.addvertex(x)\n      vdict[newid] = vid // Add the vertex to the new mesh\n    }\n\n    self.tree=KDTree(vlist)\n\n    // Create new vertices\n    for (g in 1..dim) {\n      var el = m.connectivitymatrix(0, g)\n\n      for (i in 0...nel[g]) { // Loop over elements\n        if (selection && !selection.isselected(g, i)) continue\n        //print \"Split grade ${g} element ${i}\"\n        self.split(i, el.rowindices(i), vertices, vdict)\n      }\n    }\n    refmap.append(vdict)\n\n    // Now loop over all elements and either copy or refine them\n    self._initelists(dim, self.mb.vertices.count())\n    for (g in 1..dim) {\n      var refinemethod = \"refine${g}\"\n      var el = m.connectivitymatrix(0, g)\n      var dict = Dictionary()\n\n      for (i in 0...nel[g]) { // Loop over elements\n        //print \"Refine grade ${g} element ${i}\"\n        var eind = el.rowindices(i)\n        if (!self.invoke(refinemethod, i, eind, vertices, nel, dict)) {\n          self.addelement(i, g, eind, dict) // Just copy the element across\n        }\n      }\n\n      refmap.append(dict)\n    }\n    self._clearelists()\n\n    self.refinemap = refmap\n\n    return (self.new=self.mb.build())\n  }\n\n  refine(selection=nil) {\n    return self.adapt(selection=selection)\n  } \n}\n\n/* **************************\n * Pruning\n * ************************** */\n\nclass MeshPruner is MeshAdaptiveRefiner {\n  init (target, fix=nil) {\n    self.fix = fix // Fixed vertices \n    self.clusters = nil // A list of clusters \n    self.vmap = nil // A dictionary that maps elements to clusters\n\n    super.init(target) \n  }\n\n  findclusterfromvertex(vid) { // Finds the cluster containing a vertex, if any\n    if (self.vmap.contains(vid)) return self.vmap[vid]\n    return nil \n  }\n\n  countvertices(vlist) { // Counts the number of vertices in vlist that belong to any cluster\n    var count = 0\n    for (vid in vlist) {\n      if (self.vmap.contains(vid)) count+=1\n    }\n    return count\n  } \n\n  countmaxverticesincluster(vlist) { // Counts the number of maximum number of vertices in vlist that belong to a single cluster\n    var count = Dictionary() \n    var max = 0\n    for (vid in vlist) {\n      if (self.vmap.contains(vid)) {\n        var cluster = self.vmap[vid]\n        if (count.contains(cluster)) count[cluster]+=1\n        else count[cluster]=1\n      }\n    }\n    for (c in count) if (count[c]>max) max=count[c]\n    return max\n  } \n\n  addcluster(vlist) { // Creates a new cluster from a list of vertices\n    var newcluster = []\n    self.clusters.append(newcluster)\n    self.addverticestocluster(newcluster, vlist)\n  }\n\n  addverticestocluster(cluster, vlist) { // Adds vertices to a cluster\n    for (v in vlist) {\n      if (!cluster.ismember(v)) cluster.append(v)\n      self.vmap[v]=cluster\n    }\n  }\n\n  mergeclusters(clusters) { // Merges clusters into one\n    for (k in 1...clusters.count()) {\n      self.addverticestocluster(clusters[0], clusters[k])\n      self.clusters.remove(clusters[k])\n    }\n  }\n\n  collapseelement(vlist) { // Collapses the vertices in element \n    var clusters = []\n    for (v in vlist) {\n      var c = self.findclusterfromvertex(v)\n      // Make sure we don't find duplicate clusters\n      if (c && !_elinlist(clusters, c)) clusters.append(c)\n    }\n\n    var nc = clusters.count() // Did any of these vertices belong to a cluster?\n    if (nc==0) { // No, so they form a new cluster\n      self.addcluster(vlist)\n    } else { // Yes, so we add them to an existing cluster\n      self.addverticestocluster(clusters[0],vlist)\n      if (nc>1) self.mergeclusters(clusters) // Merge if they combine more than one\n    }\n  }\n\n  updateelement(vdict, vlist) { // Maps elements of vlist using vdict\n    var nvlist = []\n    for (v in vlist) nvlist.append(vdict[v])\n    return nvlist\n  }\n  \n  isfixed(vid) { // Test if a vertex is fixed \n    if (self.fix) return self.fix[0, vid]\n    return false\n  } \n\n  clustercenter(vlist) { // Find the center of a cluster \n    var m = self.mesh()\n    var fix\n    var x \n    for (vid in vlist) {\n      if (self.isfixed(vid)) {\n        if (fix) _errMltVClstrFxd.throw()\n        fix=m.vertexposition(vid)\n        break\n      } \n      x+=m.vertexposition(vid)\n    }\n\n    if (fix) x=fix\n    else if (x) x/=vlist.count() \n    \n    return x\n  }\n\n  adaptmesh(selection) {\n    var m = self.mesh()\n    self.clusters = []\n    self.vmap = Dictionary() \n\n    // Identify clusters of points to collapse\n    for (g in 1..m.maxgrade()) {\n      var conn = m.connectivitymatrix(0, g)\n      var ids = selection.idlistforgrade(g)\n      for (elid in ids) {\n        var el=conn.rowindices(elid) \n        self.collapseelement(el) \n      }\n    }\n\n    // Now build the new mesh \n    var mb = MeshBuilder() \n    var refmap = [ ] // Dictionary describing how element ids are mapped over\n    var vdict = Dictionary(), // Map newids to oldids\n        vmap = Dictionary() // Map old vertex ids to new ids\n    for (oldid in 0...m.count()) { // Add vertices that aren't being collapsed\n      if (self.findclusterfromvertex(oldid)) continue\n      var newid = mb.addvertex(m.vertexposition(oldid))\n      vdict[newid]=oldid\n      vmap[oldid]=newid\n    }\n    for (cluster in self.clusters) { // Collapse each cluster to a new vertex\n      var x = self.clustercenter(cluster)\n      var newid = mb.addvertex(x)\n      vdict[newid]=cluster\n      for (oldid in cluster) vmap[oldid]=newid\n    }\n    refmap.append(vdict)\n\n    // Now add appropriate elements \n    for (g in 1..m.maxgrade()) {\n      var conn = m.connectivitymatrix(0, g)\n      var dict = Dictionary() \n      var dup = Dictionary()\n\n      for (id in 0...m.count(g)) {\n        var el = conn.rowindices(id)\n        var nvcl = self.countvertices(el) // Number of vertices in a cluster\n        if (nvcl<2 || self.countmaxverticesincluster(el)==1) {\n          var newel = self.updateelement(vmap, el)\n\n          var indices = newel.clone()\n          indices.sort()\n          indices = apply(Tuple, indices)\n\n          if (dup.contains(indices)) continue \n          dup[indices]=true\n\n          var newid = mb.addelement(g, newel)\n          dict[newid] = id\n        }\n      }\n      refmap.append(dict)\n    }\n\n    self.refinemap = refmap\n\n    self.new = mb.build() \n    return self.new\n  }\n\n  prune(selection) {\n    return self.adapt(selection=selection)\n  } \n}\n"
  },
  {
    "path": "modules/optimize.morpho",
    "content": "/* ************************************\n * Optimization\n ************************************** */\n\n// Minimize a 1-d function by Brent's algorithm\nfn brent(bracket, func, tol, itermax) {\n var zeps = 1e-10, cgold = 0.3819660\n\n fn sign(x, y) { // Returns sign(y)*|x|\n   if (y<0) return -abs(x)\n   if (y>0) return abs(x)\n   return 0.0\n }\n\n var iter\n var a, b // Minimum lies between a and b\n if (bracket[0]<bracket[2]) a=bracket[0] else a=bracket[2]\n if (bracket[0]>bracket[2]) b=bracket[0] else b=bracket[2]\n var d=0.0, e=0.0\n var xm\n\n var v=bracket[1], w=v, x=v // Initialize\n var fv=func(x), fw=fv, fx=fv\n\n for (iter in 0...itermax) {\n   xm=0.5*(a+b)\n   var tol1=tol*abs(x)+zeps, tol2=2*tol1\n\n   // Check if converged\n   if (abs(x-xm) <= (tol2-0.5*(b-a))) return [x, fx]\n\n   if (abs(e) > tol1) { // Model the function by a parabola\n     var r = (x-w)*(fx-fv),\n         q = (x-v)*(fx-fw),\n         p = (x-v)*q-(x-w)*r\n     q=2*(q-r)\n     if (q>0) p = -p\n     q=abs(q)\n     var etemp = e\n     e=d\n\n     // Check if the parabolic fit is acceptable\n     if (abs(p) >= abs(0.5*q*etemp) || p<= q*(a-x) ||\n         p>= q*(b-x)) { // Bad: Take golden section step\n       if (x>=xm) e=a-x else e=b-x\n       d = cgold*e\n     } else { // Good: Use parabolic step\n       d=p/q\n       var u=x+d\n       if (u-a < tol2 || b-u < tol2) d = sign(tol1, xm-x)\n     }\n   } else {\n     if (x>=xm) e=a-x else e=b-x\n     d = cgold*e\n   }\n\n   var u\n   if (abs(d)>=tol1) u=x+d else u=x+sign(tol1, d)\n   var fu = func(u) // Evaluate function\n\n   // Update bracket\n   if (fu<=fx) {\n     if (u>=x) a=x else b=x\n     v=w; w=x; x=u\n     fv=fw; fw=fx; fx=fu\n   } else {\n     if (u<x) a=u else b=u\n     if (fu <= fw || w==x) {\n       v=w; w=u; fv=fw; fw=fu\n     } else if (fu<=fv || v==x || v==w) {\n       v=u; fv=fu\n     }\n   }\n }\n return [x, fx]\n}\n\nclass Constraint {\n  init (func, target) {\n    self.functional = func\n    self.target = target\n    self.field = nil\n    self.selection = nil\n    self.onesided = false\n  }\n}\n\nclass Energy {\n  init (func) {\n    self.functional = func\n    self.selection = nil\n    self.prefactor = nil\n  }\n}\n\n/* Defines an optimization problem */\nclass OptimizationProblem {\n  init (m) {\n    self.mesh = m\n    self.fields = []\n    self.energies = []\n    self.constraints = []\n    self.localconstraints = []\n    self.fixed = nil\n  }\n\n  /* Adds an energy to the problem */\n  addenergy(functional, selection=nil, prefactor=nil) {\n    var en = Energy(functional)\n    en.selection = selection\n    en.prefactor = prefactor\n    self.energies.append(en)\n    return en\n  }\n\n  /* Adds a field to the problem */\n  addfield(field) {\n    self.fields.append(field)\n  }\n\n  /* Adds a constraint to the problem */\n  addconstraint(functional, selection=nil, field=nil) {\n    var target\n    if (selection) {\n      target=functional.total(self.mesh, selection)\n    } else {\n      target=functional.total(self.mesh)\n    }\n    var cons = Constraint(functional, target)\n    cons.selection = selection\n    cons.field = field\n    cons.prefactor = nil\n    self.constraints.append(cons)\n    return cons\n  }\n\n  /* Adds a local constraint to the problem */\n  addlocalconstraint(functional, selection=nil, field=nil, onesided=false, target=0) {\n    var cons = Constraint(functional, target)\n    cons.selection = selection\n    cons.onesided = onesided\n    cons.field = field\n    cons.prefactor = nil\n    self.localconstraints.append(cons)\n    return cons\n  }\n\n  updatelist(lst, dict) {\n    if (islist(lst)) for (en in lst) {\n      if (en.has(\"selection\") && dict.contains(en.selection)) en.selection = dict[en.selection]\n      if (en.has(\"field\") && dict.contains(en.field)) en.field = dict[en.field]\n      //if (en.has(\"target\") && dict.contains(en.target)) en.target = dict[en.target]\n\n      if (en.functional.has(\"field\")) {\n        var f = en.functional.field\n\n        if (isfield(f) && dict.contains(f)) {\n          en.functional.field = dict[f]\n        } else if (islist(f)) {\n          var newlist = []\n          for (fel in f) {\n            if (dict.contains(fel)) newlist.append(dict[fel])\n            else newlist.append(fel)\n          }\n          en.functional.field = newlist\n        }\n      }\n    }\n  }\n\n  update(dict) {\n    if (dict.contains(self.mesh)) self.mesh = dict[self.mesh]\n\n    var newfields = []\n    for (f in self.fields) {\n      if (dict.contains(f)) newfields.append(dict[f])\n      else newfields.append(f)\n    }\n    self.fields = newfields\n\n    self.updatelist(self.energies, dict)\n    self.updatelist(self.constraints, dict)\n    self.updatelist(self.localconstraints, dict)\n  }\n}\n\n\nclass Optimizer {\n  init (problem, target) {\n    self.problem = problem\n    self.target = target\n    self.fixed = nil\n    self.stepsize = 0.1\n    self.steplimit = 0.5\n    self.energy = [] // History of energies\n    self.etol = 1e-8 // Energy convergence criterion\n    self.ctol = 1e-10 // Constraint satisfaction criterion\n    self.linmintol = 0.001 // Convergence tolerance for line minimizations\n    self.linminmax = 10 // Maximum number of iterations for line minimizations\n    self.maxconstraintsteps = 20 // Maximum number of constraint steps\n    self.maxbracketsteps = 20 // Maximum number of bracketing steps \n    self.quiet = false // Whether to report\n  }\n\n  /* Calculate the total energy from a functional */\n  total(func) {\n    var prefactor = 1\n    if (func.prefactor) prefactor = func.prefactor\n    if (func.selection) {\n      return prefactor*func.functional.total(self.problem.mesh, func.selection)\n    } else {\n      return prefactor*func.functional.total(self.problem.mesh)\n    }\n  }\n\n  /* Calculate the integrand for a functional */\n  integrand(func) {\n    var val\n    if (func.selection) {\n      val=func.functional.integrand(self.target, func.selection)\n    } else {\n      val=func.functional.integrand(self.target)\n    }\n    if (func.prefactor) val*=func.prefactor\n    return val\n  }\n\n  /* Calculate the gradient of the functional */\n  gradient(func, selection=nil) {\n    var sel=func.selection // Use the object's selection by default\n    if (selection) sel=selection // If we're passed a selection use it\n\n    var grad=self.evalgradient(func, sel)\n\n    if (self.fixed) self.fixgrad(grad)\n    if (func.prefactor) grad*=func.prefactor\n    return grad\n  }\n\n  /* Calculates the total energy for a problem */\n  totalenergy() {\n    var energy=0\n    for (en in self.energies()) {\n      energy+=self.total(en)\n    }\n    return energy\n  }\n\n  /* Calculates the total force on the vertices */\n  totalforce() {\n    var energies = self.energies()\n    var f\n    if (!islist(energies) || energies.count()==0) print \"Warning: Problem has no active functionals.\"\n    for (en in energies) {\n      f+=self.gradient(en)\n    }\n\n    return f\n  }\n\n  /* Subtract constraint forces from a given force */\n  subtractconstraints(f) {\n    for (cons in self.constraints()) {\n      var g = self.gradient(cons)\n      self.subtractlocalconstraints(g)\n      // Todo Should use Gram=Schmidt to properly enforce multiple global constraints\n\n      var lambda=f.inner(g)/g.inner(g)\n      f.acc(-lambda, g)\n    }\n  }\n\n  totalforcewithconstraints() {\n    var f = self.totalforce()\n    self.initlocalconstraints() // Find which local constraints are active\n    self.subtractlocalconstraints(f) // Remove projections onto local constraints\n    self.subtractconstraints(f) // Remove projections onto constraint directions\n    return f\n  }\n\n  /* Test how closely constraints are satisfied.\n     Returns a vector of residuals */\n  testconstraints() {\n    var dv = []\n    for (cons in self.constraints()) {\n      dv.append(cons.target-self.total(cons))\n    }\n    return Matrix(dv)\n  }\n\n  /* Calculate matrix of force inner products */\n  forceinnerproducts(fv) {\n    var nc=fv.count()\n    var m=Matrix(nc,nc)\n\n    for (i in 0...nc) {\n      for (j in i...nc) {\n        m[i,j]=fv[i].inner(fv[j])\n        m[j,i]=m[i,j]\n      }\n    }\n    return m\n  }\n\n  /* Reproject onto constrained subspace */\n  reprojectconstraints() {\n    var v = self.gettarget()\n    var acons = self.constraints()\n    var nc = acons.count()\n    if (nc>0) {\n      var residual, i=0\n\n      do {\n        var dv = self.testconstraints(), fv = []\n\n        for (cons in acons) {\n          var ff = self.gradient(cons)\n          self.subtractlocalconstraints(ff)\n          fv.append(ff)\n        }\n\n        var m=self.forceinnerproducts(fv)\n\n        var sol = dv/m\n        for (i in 0...nc) {\n          v.acc(sol[i],fv[i])\n        }\n\n        residual=self.testconstraints().norm()\n        i+=1\n        if (i>self.maxconstraintsteps) {\n          print \"Warning: Too many steps in constraint satisfaction\"\n          return\n        }\n\n      } while (residual>self.ctol)\n    }\n  }\n\n  /* Find which local constraints are active */\n  initlocalconstraints() {\n    self.lcactive = []\n    for (cons in self.localconstraints()) {\n      if (cons.onesided) {\n        var integrand = self.integrand(cons)\n        var sel = Selection(self.target, fn (q) q<self.ctol, integrand)\n        if (cons.selection) sel=sel.intersection(cons.selection)\n        self.lcactive.append(sel)\n      } else {\n        if (cons.selection) self.lcactive.append(cons.selection)\n        else self.lcactive.append(true)\n      }\n    }\n  }\n\n  /* Subtracts local constraints */\n  subtractlocalconstraints(f) {\n    for (cons, i in self.localconstraints()) {\n      var g=self.gradient(cons, selection=self.lcactive[i])\n      self.sublocal(f,g)\n    }\n  }\n\n  /* Test how closely local constraints are satisfied.\n     Returns a list of vectors of residuals */\n  testlocalconstraints() {\n    var dv = []\n    for (cons, i in self.localconstraints()) {\n      if (isselection(self.lcactive[i])) {\n        dv.append(cons.functional.integrand(self.target, self.lcactive[i])-cons.target)\n      } else {\n        dv.append(self.integrand(cons)-cons.target)\n      }\n    }\n    return dv\n  }\n\n  lcnorm(dv) {\n    var norm = 0\n    for (v in dv) norm+=v.norm()\n    return norm\n  }\n\n  /* Reproject local constraints */\n  reprojectlocalconstraints() {\n    var v = self.target\n    var msh = ismesh(v)\n    var nv = v.count()\n    if (msh) {\n      v=v.vertexmatrix()\n      nv=v.dimensions()[1]\n    }\n\n    var localconstraints = self.localconstraints()\n    var nc = localconstraints.count()\n    if (nc>0) {\n      var residual, iter=0\n      var dv = self.testlocalconstraints()\n\n      if (self.lcnorm(dv)>self.ctol) do {\n        var fv = []\n\n        // Calculate constraint forces\n        for (cons, i in localconstraints) {\n          fv.append(self.gradient(cons, selection=self.lcactive[i]))\n        }\n\n        var nactive = 0\n        // Loop over vertices\n        for (k in 0...nv) {\n          // Find the discrepencies of each force at the vertex\n          var vv = Matrix(nc)\n          for (i in 0...nc) vv[i] = -dv[i][0,k] // Note minus sign\n\n          if (vv.norm()>self.ctol/nv) {\n            // Identify constraints active on this vertex\n            var va = [], fa = []\n            for (i in 0...nc) {\n              if (abs(vv[i])>self.ctol/nv) {\n                va.append(vv[i])\n                if (msh) fa.append(fv[i].column(k))\n                else fa.append(fv[i][k])\n              }\n            }\n\n            // Now solve for the necessary motion\n            var m=self.forceinnerproducts(fa)\n            if (m.norm()<self.ctol) continue\n            var sol = Matrix(va)/m\n\n            if (msh) { // Move the vertex\n              var newv=v.column(k)\n              for (i in 0...sol.count()) {\n                newv.acc(sol[i], fa[i])\n              }\n              v.setcolumn(k,newv)\n            } else {\n              for (i in 0...sol.count()) {\n                v[k].acc(sol[i], fa[i])\n              }\n            }\n\n            nactive+=1\n          }\n        }\n\n        dv=self.testlocalconstraints()\n        residual=self.lcnorm(dv)\n        if (nactive>0) residual/=nactive\n        iter+=1\n        if (iter>self.maxconstraintsteps && residual>self.ctol) {\n          print \"Warning: Too many steps in local constraint satisfaction. (Try increasing maxconstraintsteps or set ctol to a number greater than ${residual}).\"\n          return\n        }\n\n      } while (residual>self.ctol)\n    }\n  }\n\n  /* Take a step */\n  step(stepsize) {\n    var target = self.gettarget()\n    var frc = self.force // Use the force\n\n    if (!frc) return\n\n    target.acc(-stepsize, frc) // Take a step\n\n    self.initlocalconstraints()\n    self.reprojectlocalconstraints() // Reproject onto local constraints\n\n    self.reprojectconstraints() // Push back onto constraints\n  }\n\n  /* Adaptive stepsize */\n  energywithstepsize(size) {\n    var target = self.gettarget()\n    var vsave = target.clone()\n\n    self.step(size) // Take the step\n    var energy=self.totalenergy()\n\n    self.settarget(vsave)\n\n    return energy\n  }\n\n  /* Bracket the stepsize */\n  bracketstepsize() {\n    var s = [ 0, self.stepsize, 2*self.stepsize ]\n    if (2*self.stepsize>self.steplimit) {\n      s[1]=self.steplimit/2 \n      s[2]=self.steplimit \n    }\n\n    var bracket = [ self.totalenergy(), self.energywithstepsize(s[1]), self.energywithstepsize(s[2]) ]\n    var iter = 0\n\n    while (!(bracket[1]<bracket[0] && bracket[1]<bracket[2])) {\n      if (bracket[1]>bracket[0]) { // Step size is too big\n        s[1]=s[1]/2\n        bracket[1]=self.energywithstepsize(s[1])\n      } else if (bracket[2]<bracket[1]) { // Step size is too small\n        if (2*s[2]>self.steplimit) { // Ensure we don't exceed steplimit\n          if (bracket[1]<bracket[0] && bracket[2]<bracket[1]) return -1\n          return false \n        }\n        s[1]=s[2]\n        s[2]=2*s[2]\n        bracket[1]=bracket[2]\n        bracket[2]=self.energywithstepsize(s[2])\n      } else {\n        print \"Cannot resolve bracket. Current stepsizes [${s[0]},${s[1]},${s[2]}] and bracket [${bracket[0]},${bracket[1]},${bracket[2]}]\"\n        if (bracket[1]<bracket[0] && bracket[2]<bracket[1]) return -1\n        return nil\n      }\n      if (iter>self.maxbracketsteps) {\n        print \"Too many steps in bracketing stepsize. Current stepsizes [${s[0]},${s[1]},${s[2]}] and bracket [${bracket[0]},${bracket[1]},${bracket[2]}]\"\n        return nil \n      }\n      iter+=1\n    }\n\n    return [s, bracket]\n  }\n\n  /* Test for convergence */\n  hasconverged() {\n    var energy = self.energy\n    if (energy.count()>1) {\n      var de = abs(energy[-1]-energy[-2])\n      if (abs(energy[-1])<self.etol || de/abs(energy[-1])<self.etol) return true\n    }\n    return false\n  }\n\n  /* Report progress */\n  report(iter) {\n    if (self.energy.count()<2) return\n    var de = abs(self.energy[-1]-self.energy[-2])\n    if (!self.quiet) print \"Iteration ${iter}. Energy: ${self.energy[-1]} delta E: ${de} stepsize: ${self.stepsize}\"\n  }\n\n  /* Perform relaxation at fixed stepsize */\n  relax(n) {\n    if (self.energy.count()==0) self.energy.append(self.totalenergy())\n\n    for (i in 0...n) {\n      self.force = self.totalforcewithconstraints()\n      self.step(self.stepsize)\n      self.energy.append(self.totalenergy()) // Track the total energy\n      self.report(i)\n      if (self.hasconverged()) break\n    }\n    return self.energy\n  }\n\n  /* Bracket and descend.\n     If successful, self.stepsize is set to the optimal stepsize and\n     returns true */\n  dolinesearch() {\n    var brack=self.bracketstepsize()\n    if (brack==-1) { // Bracketing failed; should take regular step\n      //print \"Linesearch failed; taking regular step.\"\n      self.stepsize = self.steplimit\n      return self.steplimit\n    } else if (!islist(brack)) return false // Bracketing completely failed\n\n    var step = brent(brack[0], self.energywithstepsize, self.linmintol, self.linminmax)\n    if (islist(step)) self.stepsize = step[0]\n    return step\n  }\n\n  /* Gradient descent with linesearches */\n  linesearch(n) {\n    if (self.energy.count()==0) self.energy.append(self.totalenergy())\n\n    for (i in 0...n) {\n      self.force = self.totalforcewithconstraints()\n\n      if (!self.dolinesearch()) break\n      if (self.stepsize > self.steplimit) self.stepsize = self.steplimit\n\n      self.step(self.stepsize)\n      self.energy.append(self.totalenergy())\n      self.report(i)\n      if (self.hasconverged()) break\n    }\n    return self.energy\n  }\n\n  /* Conjugate gradient */\n  conjugategradient(n) {\n    if (self.energy.count()==0) self.energy.append(self.totalenergy())\n    var oforce, dk\n\n    for (i in 0...n) {\n      var force = self.totalforcewithconstraints()\n\n      if (oforce) {\n        // Fletcher-Reeves formula\n        //var beta = force.inner(force)/oforce.inner(oforce)\n\n        // Hager and Zhang formula\n        var yk = (oforce-force)\n        var dkyk = dk.inner(yk)\n        var beta = (yk - 2*dk*yk.inner(yk)/dkyk).inner(force)/dkyk\n\n        dk=-force+beta*dk\n        self.force = -dk\n      } else {\n        self.force = force\n        dk = -force\n      }\n\n      if (!self.dolinesearch()) break\n      if (self.stepsize > self.steplimit) self.stepsize = self.steplimit\n\n      self.step(self.stepsize)\n      self.energy.append(self.totalenergy())\n      self.report(i)\n      if (self.hasconverged()) break\n\n      oforce = force\n    }\n    return self.energy\n  }\n\n  update(dict) {\n    if (dict.contains(self.target)) self.target = dict[self.target]\n  }\n}\n\n/* Optimize meshes */\nclass ShapeOptimizer is Optimizer {\n  gettarget() {\n    return self.problem.mesh.vertexmatrix()\n  }\n\n  settarget(v) {\n    self.problem.mesh.setvertexmatrix(v)\n  }\n\n  energies() {\n    return self.problem.energies\n  }\n\n  constraints() {\n    var sc = []\n    for (c in self.problem.constraints) {\n      if (c.field==nil) sc.append(c)\n    }\n    return sc\n  }\n\n  localconstraints() {\n    var sc = []\n    for (c in self.problem.localconstraints) {\n      if (c.field==nil) sc.append(c)\n    }\n    return sc\n  }\n\n  evalgradient(func, sel) {\n    if (isselection(sel)) {\n      return func.functional.gradient(self.target, sel)\n    } else {\n      return func.functional.gradient(self.target)\n    }\n  }\n\n  sublocal(f, g) {\n    var nv = f.dimensions()[1]\n    for (var i=0; i<nv; i+=1) {\n      var gc=g.column(i)\n      var gg=gc.inner(gc)\n      if (abs(gg)>self.ctol) {\n        var fc=f.column(i) // Note we only retrieve the column of f if needed\n        var lambda=fc.inner(gc)/gg\n        fc.acc(-lambda, gc)\n        f.setcolumn(i, fc)\n      }\n    }\n  }\n\n  fixgrad(grad) {\n    if (islist(self.fixed)) {\n      var zero = Matrix(grad.dimensions()[0], 1)\n      for (i in self.fixed) {\n        grad.setcolumn(i, zero)\n      }\n    }\n  }\n\n  fix(s) {\n    if (isnil(self.fixed)) self.fixed=[]\n\n    var lst = s\n    if (isselection(s)) lst = s.idlistforgrade(0)\n\n    if (islist(lst)) { // Add these to the list if they're not already in it\n      for (i in lst) {\n        if (!(self.fixed.ismember(i))) self.fixed.append(i)\n      }\n    }\n  }\n\n  unfix(s) {\n    if (isnil(self.fixed)) return\n\n    var lst = s\n    if (isselection(s)) lst = s.idlistforgrade(0)\n\n    if (islist(lst)) { // Add these to the list if they're not already in it\n      for (i in lst) {\n        if (self.fixed.ismember(i)) self.fixed.remove(i)\n      }\n    }\n  }\n}\n\n/* Optimize fields */\nclass FieldOptimizer is Optimizer {\n  gettarget() {\n    return self.target\n  }\n\n  settarget(val) {\n    self.target.assign(val)\n  }\n\n  checkfield(fld) {\n    if (islist(fld)) return fld.ismember(self.target)\n    return (fld==self.target)\n  }\n\n  energies() { // Selects which energies are pertinent to the optimization problem\n    var en = []\n    for (e in self.problem.energies) {\n      if (e.functional.has(\"field\")) {\n        if (self.checkfield(e.functional.field)) en.append(e)\n      }\n    }\n    return en\n  }\n\n  constraints() {\n    var sc = []\n    for (c in self.problem.constraints) {\n      if (c.field==self.target) sc.append(c)\n    }\n    return sc\n  }\n\n  localconstraints() {\n    var sc = []\n    for (c in self.problem.localconstraints) {\n      if (c.field==self.target) sc.append(c)\n    }\n    return sc\n  }\n\n  evalgradient(func, sel) {\n    if (isselection(sel)) {\n      return func.functional.fieldgradient(self.target, self.problem.mesh, sel)\n    } else {\n      return func.functional.fieldgradient(self.target, self.problem.mesh)\n    }\n  }\n\n  sublocal(f, g) {\n    for (gi, i in g) {\n      var gg=gi.inner(gi)\n      if (abs(gg)>self.ctol) {\n        var lambda=f[i].inner(gi)/gg\n        f[i].acc(-lambda, gi)\n      }\n    }\n  }\n\n  fixgrad(grad) {\n    var sel=self.fixed\n\n    var zero = 0\n    var prototype = grad[0]\n    if (ismatrix(prototype)) {\n      var dim = prototype.dimensions()\n      zero=Matrix(dim[0], dim[1])\n    }\n\n    if (isselection(sel)) {\n      var shape = grad.shape()\n      for (g in 0...shape.count()) {\n        if (shape[g]==0 || sel.count(g)==0) continue // skip empty grades\n\n        for (id in sel.idlistforgrade(g)) {\n          for (k in 0...shape[g]) grad[g, id, k]=zero\n        }\n      }\n    }\n  }\n\n  fix(s) {\n    if (isnil(self.fixed)) self.fixed=Selection(self.target.mesh())\n    if (isselection(s)) self.fixed=self.fixed.union(s)\n    else print \"fix() requires a selection\"\n  }\n\n  unfix(s) {\n    if (isnil(self.fixed)) return\n    if (isselection(s)) self.fixed=self.fixed.difference(s)\n    else print \"unfix() requires a selection\"\n  }\n}\n"
  },
  {
    "path": "modules/parser.morpho",
    "content": "// Basic tokenizer\n\nclass StringStream {\n  init(str) {\n    self.str = str\n    self.len = str.count() \n    self.n = 0\n  }\n\n  readchar() {\n    if (self.n>=self.len) return nil \n    var out = self.str[self.n]\n    self.n+=1 \n    return out\n  }\n\n  eof() {\n    return (self.n >= self.len)\n  }\n} \n\nclass Tokenizer {\n  init(stream, whitespace=\" \\n\") {\n    self.stream = stream // The stream to use\n    self.current = self.advance() // Current character\n\n    self.whitespace = Dictionary() // Whitespace characters\n    for (c in whitespace) self.whitespace[c] = true\n  }\n\n  advance() {\n    self.current = self.stream.readchar()\n    return self.current\n  }\n\n  atend() {\n    return self.stream.eof()\n  }\n\n  iswhitespace(c) {\n    return self.whitespace.contains(c)\n  }\n\n  skipwhitespace() {\n    while (self.iswhitespace(self.current) && !self.atend()) {\n      self.advance()\n    }\n  }\n\n  next() {\n    var token = nil\n\n    self.skipwhitespace()\n    while (!self.atend()) {\n      var char = self.current\n      if (!char) break\n      if (self.iswhitespace(char)) break\n      if (token) token+=char else token=char\n      self.advance()\n    }\n    return token\n  }\n}\n"
  },
  {
    "path": "modules/plot.morpho",
    "content": "/* Plotting and visualization */\n\nimport graphics\nimport color\nimport meshtools\n\nvar _pltmshcolerr = Error(\"PltMshCl\", \"Color specification for plotmesh should be either a color, a list of [r,g,b] values or a list of colors for each grade.\")\nvar _pltinvldcolerr = Error(\"PltInvldCl\", \"Could not convert color to matrix.\")\n\n/* 3d cross product */\nfn cross3d(a, b) {\n  return Matrix([ a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0] ])\n}\n\n/* Finds a bounding box from a vertex matrix */\nfn _bbox(m) {\n  var col = m.transpose()\n  var bnds = []\n  for (i in 0...col.dimensions()[1]) bnds.append(bounds(col.column(i)))\n  return bnds\n}\n\n/** Deduces the plot color for a given grade */\nfn _plotcolorforgrade(grade, color) {\n  if (isnil(color)) return Gray(0.5) // No color provided, so use a default color\n  if (islist(color) || isarray(color)) {\n      if (color.count()==3 && isnumber(color[0]) && isnumber(color[1]) && isnumber(color[2])) return color // Color was given as a simple list\n\n      // Different colors for each grade\n      if (isobject(color[grade]) ||\n          islist(color[grade]) ||\n          ismatrix(color[grade])) return color[grade]\n\n      if (isnil(color[grade])) return Gray(0.5)\n  }\n  if (isobject(color)) return color\n  _pltmshcolerr.throw() \n}\n\n/** Finds the appropriate color for a given element */\nfn _plotcolorforelement(color, element) {\n  var col=color\n  if (ismatrix(col)) col=color.column(element)\n  return col\n}\n\n/** Converts a color to a matrix */\nfn _plotcolortomatrix(color) {\n  if (ismatrix(color)) return color\n  if (islist(color)) return Matrix(color)\n  if (isobject(color)) return Matrix(color.rgb(0))\n  _pltinvldcolerr.throw() \n  return nil\n}\n\n/** Converts a vertex matrix to a list of 3D points */\nfn _vertto3d(vert) {\n  var out = []\n  var dim = vert.dimensions()[0]\n  var nv = vert.dimensions()[1]\n  if (dim>3) dim=3\n\n  for (i in 0...nv) {\n    var pt = Matrix(3)\n    var v = vert.column(i)\n    for (j in 0...dim) pt[j]=v[j]\n    out.append(pt)\n  }\n\n  return out\n}\n\n/** Plots elements of a mesh\n * @param[in] mesh - mesh to plot\n * @param[in] selection - selection\n * @param[in] grade - Grade to show\n * @param[in] color - How to color elements:\n                      can be a single color\n                      a matrix with columns corresponding to elements\n                      a list of such matrices for each grade */\nfn plotmesh(mesh, selection=nil, grade=nil, color=nil,  filter=nil, transmit=nil) {\n  var g = Graphics()\n  var glist = []\n  if (isnil(grade)) glist.append(mesh.maxgrade())\n  if (islist(grade)) for (q in grade) glist.append(q)\n  if (isnumber(grade)) glist.append(grade)\n\n  var vert = _vertto3d(mesh.vertexmatrix())\n  var flat = nil\n\n  var bb = _bbox(mesh.vertexmatrix())\n  var centroid = Matrix(3)\n  var size[bb.count()]\n  for (x, i in bb) {\n    size[i]=x[1]-x[0]\n    centroid[i]=x[0]+size[i]/2\n    if (abs(size[i])<1e-10) flat = i\n  }\n\n  if (glist.ismember(0)) { // Show points as spheres\n    var vcol=_plotcolorforgrade(0, color) // Will either be a color or a matrix\n    var np = mesh.count(0)\n    for (i in 0...np) {\n      if (isselection(selection) && !selection.isselected(0, i)) continue // Skip unselected components\n\n      g.display(Sphere(vert[i], 0.025, color=_plotcolorforelement(vcol, i), filter=filter, transmit=transmit))\n    }\n  }\n\n  if (glist.ismember(1)) { // Show lines as cylinders\n    var lcol=_plotcolorforgrade(1, color) // Will either be a color or a matrix\n    var lines=mesh.connectivitymatrix(0,1)\n    if (issparse(lines)) {\n      var nl = mesh.count(1)\n      for (i in 0...nl) {\n        if (isselection(selection) && !selection.isselected(1, i)) continue // Skip unselected components\n        var el = lines.rowindices(i)\n        g.display(Cylinder(vert[el[0]], vert[el[1]],\n                          aspectratio=0.05, color=_plotcolorforelement(lcol, i), filter=filter, transmit=transmit))\n      }\n    }\n  }\n\n  if (glist.ismember(2)) { // Show faces\n    var fcol=_plotcolorforgrade(2, color) // Will either be a color or a matrix\n    var faces=mesh.connectivitymatrix(0,2)\n    var interpolate=false\n\n    if (issparse(faces)) {\n      var nf = mesh.count(2)\n      var nnv = 0, nnt = nf // Count number of triangles and vertices generated\n      if (isselection(selection)) nnt=selection.idlistforgrade(2).count()\n\n      if (ismatrix(fcol) &&\n          fcol.dimensions()[1]!=nf &&\n          fcol.dimensions()[1]==mesh.count(0)) interpolate = true\n\n      var nvert = Matrix(3, 3*nnt) // New array of vertices\n      var norm = Matrix(3, 3*nnt) // New array of normals\n      var ncol = Matrix(3, 3*nnt) // New array of normals\n      var tri = Sparse(3*nnt, nnt) // Connectivity\n\n      nnt=0\n      for (i in 0...nf) { // Draw a triangle for each face\n        if (isselection(selection) && !selection.isselected(2, i)) continue // Skip unselected components\n\n        var el = faces.rowindices(i)\n        var v[3]\n        for (p, i in el) v[i]=vert[p]\n        var normal \n        if (flat) {\n          normal=Matrix(bb.count())\n          normal[flat]=1\n        } else {\n          normal=cross3d(v[2]-v[1], v[1]-v[0])\n          if (normal.inner(v[0]-centroid)<0) {\n            normal=-normal\n          }\n        }\n\n        var tcol\n        if (!interpolate) tcol = _plotcolortomatrix(_plotcolorforelement(fcol, i))\n        for (j in 0...3) {\n          tri[nnv,nnt]=1\n          nvert.setcolumn(nnv, v[j])\n          norm.setcolumn(nnv, normal)\n          if (interpolate) tcol = _plotcolortomatrix(_plotcolorforelement(fcol, el[j]))\n          ncol.setcolumn(nnv, tcol)\n          nnv+=1\n        }\n        nnt+=1\n      }\n\n      g.display(TriangleComplex(nvert, norm, ncol, tri, filter=filter, transmit=transmit))\n    }\n\n  }\n\n  return g\n}\n\n/** Visualizes a selection\n * @param[in] mesh - mesh to plot\n * @param[in] selection - selection\n * @param[in] grade - Grades to show */\nfn plotselection(mesh, selection, grade=nil, filter=nil, transmit=nil) {\n  var ngrades = mesh.maxgrade()\n  var col[ngrades+1]\n\n  for (g in 0..ngrades) {\n    col[g]=nil\n    if (islist(grade) && !grade.ismember(g)) continue // Skip over grades\n    else if (isint(grade) && grade!=g) continue\n\n    var selected = selection.idlistforgrade(g)\n    if (selected.count()==0) continue                 // Skip over empty grade\n\n    var nel = mesh.count(g)                           // Make a color matrix\n    var cmat = Matrix(3, nel)\n    for (i in 0...nel) {\n      cmat.setcolumn(i, Matrix([0.5,0.5,0.5]))\n    }\n\n    for (i in selected) {                             // Highlight selected\n      cmat.setcolumn(i, Matrix([1,0,0]))\n    }\n\n    col[g]=cmat\n  }\n\n  return plotmesh(mesh, grade=grade, color=col, filter=filter, transmit=transmit)\n}\n\n/** Visualizes a field\n * @param[in] field - the field\n * @param[in] selection - Selection to use\n * @param[in] grade - Grade to show\n * @param[in] colormap - Colormap to use\n * @param[in] style - style to use\n * @param[in] scale - whether or not to scale values */\nfn plotfield(field, selection=nil, grade=nil, colormap=nil, style=nil, scale=true, filter=nil, transmit=nil, scalebar=nil, cmin=nil, cmax=nil) {\n  var mesh = field.mesh()\n  var shape = field.shape()\n  var sel = selection\n\n  var ngrades = shape.count()\n  var showgrades = [] // Grades to show\n  var col[ngrades+1]\n\n  var cm=colormap\n  if (cm==nil) cm = ViridisMap()    // Default color map\n\n  var bnd=bounds(field)         // Find bounds for the field\n  if (!isnil(cmin)) bnd[0] = cmin\n  if (!isnil(cmax)) bnd[1] = cmax\n  var sc = bnd[1]-bnd[0]\n  if (abs(sc)>1e-16) sc=1/sc else sc = 1\n\n  if (style==\"interpolate\") { // Interpolate scalar field onto areas\n    var nv=mesh.count(0)       // Number of elements in this grade\n    var cmat = Matrix(3, nv)   // Make a color matrix\n\n    if (field.shape()[0]==0) {\n      Error(\"PltFldInt\", \"Can't use 'interpolate' style: Field lacks values on vertices.\").throw() \n    }\n\n    if (sel) { // Ensure we have elements to plot \n      if (sel.count(2)==0) { // If the selection didn't include facets, infer them from the vertices\n        sel=sel.clone()\n        sel.addgrade(2)\n      } else if (sel.count(0)==0) { // If the selection didn't include vertices, infer them from the facets\n        sel=sel.clone()\n        sel.addgrade(0)\n      }\n\n      if (sel.count(2)==0 || sel.count(0)==0) Error(\"PltSlctnEmpty\", \"Selection is empty.\").throw() \n    }\n\n    for (i in 0...nv) {\n      if (sel && !sel[0, i]) continue \n      var val = field[0, i]\n      if (!isnil(cmin)) val = max(cmin, val)\n      if (!isnil(cmax)) val = min(cmax, val)\n      if (scale) val=(val-bnd[0])*sc\n      cmat.setcolumn(i, Matrix(cm.rgb(val)))\n    }\n\n    col[2]=cmat\n    showgrades.append(2)\n  } else {\n    for (g in 0...ngrades) {\n      if (shape[g]==0) continue\n\n      var nel=mesh.count(g)       // Number of elements in this grade\n      var cmat = Matrix(3, nel)   // Make a color matrix\n\n      for (i in 0...nel) {\n        if (sel && !sel[g, i]) continue \n        var val = field[g, i]\n        if (!isnil(cmin)) val = max(cmin, val)\n        if (!isnil(cmax)) val = min(cmax, val)\n        if (scale) val=(val-bnd[0])*sc\n        cmat.setcolumn(i, Matrix(cm.rgb(val)))\n      }\n\n      col[g]=cmat\n      showgrades.append(g)\n    }\n  }\n\n  var out = plotmesh(mesh, selection=sel, grade=showgrades, color=col, filter=filter, transmit=transmit)\n\n  if (isobject(scalebar)) {\n    var s = scalebar\n    s.colormap=cm\n    out+=s.draw(bnd[0], bnd[1]) \n  }\n\n  return out \n}\n\n/** Plots a set of axes\n  * @param[in] xx0 - a point at which to plot the axes */\nfn plotaxes(xx0, size=1) {\n  var x0 = xx0 \n  if (islist(xx0)) x0 = Matrix(xx0)\n  var axes = Graphics() \n  axes.display(Arrow(x0, x0+Matrix([size,0,0]), color=Red))\n  axes.display(Arrow(x0, x0+Matrix([0,size,0]), color=Green))\n  axes.display(Arrow(x0, x0+Matrix([0,0,size]), color=Blue))\n  return axes \n} \n\nfn _centroid(m, g, id) {\n  if (g==0) return m.vertexposition(id) \n  var el=m.connectivitymatrix(0,g).rowindices(id)\n  var x = 0 \n  for (vid in el) x+=m.vertexposition(vid)\n  x/=el.count() \n  return x \n}\n\nfn _to3d(x) {\n  var y = Matrix(3)\n  for (i in 0...min(x.count(),3)) y[i]=x[i]\n  return y\n}\n\nfn plotmeshlabels(mesh, grade=0, selection=nil, fontsize=10, offset=nil, dirn=nil, vertical=nil, color=nil) {\n  var gradelist = grade \n  if (!islist(grade)) gradelist = [grade]\n\n  var gout = Graphics()\n  var dir = dirn \n  if (isnil(dir)) dir = [1,0,0]\n\n  var vert = vertical\n  if (isnil(vert)) vert = [0,1,0]\n\n  fn _defaultoffset(x) { return 0.05*(x+Matrix([0.001,0,0])) }\n\n  var off = offset \n  if (islist(off)) off = Matrix(offset)\n  if (isnil(off)) off = _defaultoffset\n\n  var dcol = color\n  if (isnil(color)) dcol = White \n\n  for (g in gradelist) {\n    var col = dcol\n    if (isdictionary(color)) {  \n      if (color.contains(g)) col = color[g]\n      else col = White \n    } \n\n    for (id in 0...mesh.count(g)) {\n      if (selection && !selection[g,id]) continue \n      var x = _to3d(_centroid(mesh, g, id))\n      if (iscallable(dirn)) dir=dirn(x)\n      if (iscallable(vertical)) vert=vertical(x)\n      var offset = off \n      if (iscallable(off)) offset = off(x)\n\n      if (abs(Matrix(vert).inner(Matrix(dir))-1) < 1e-10) {\n        Error(\"PltLblDirn\", \"Text direction and vertical are colinear for element ${id} at [${x[0]}, ${x[1]}, ${x[2]}].\").throw()        \n      }\n\n      gout.display(Text(\"${id}\", x+offset, size=fontsize, dirn=dir, vertical=vert, color=col))\n    }\n  }\n\n  return gout \n}\n\n// Scalebars \nclass ScaleBar {\n  _vector(val, default, normalize=false) {\n    var out\n    if (ismatrix(val)) { out=val \n    } else if (islist(val)) { out=Matrix(val)\n    } else { out=Matrix(default) }\n    if (normalize) out/=out.norm() \n    return out \n  }\n\n  init(nticks=5, colormap=nil, length=1, posn=nil, dirn=nil, tickdirn=nil, textdirn=nil, textvertical=nil, textcolor=nil, fontsize=16) {\n    self.nticks = nticks \n    self.length = length // Length of scalebar \n    self.radius = 0.1*length // Radius of scalebar\n    self.nptsamples = 5 // Number of samples \n    self.npts = 16 // Number of azimuthal samples\n    self.fontsize = fontsize // Maximum font size for labels \n    self.colormap = colormap \n    if (!self.colormap) self.colormap=ViridisMap()\n\n    self.posn = self._vector(posn, [1,0,0])\n    self.dirn = self._vector(dirn, [0,1,0], normalize=true)  \n    self.tickdirn = self._vector(tickdirn, [1,0,0], normalize=true) \n    var lambda = self.tickdirn.inner(self.dirn)\n    if (abs(lambda-1)>1e-8) { self.tickdirn -= lambda*self.dirn }\n    self.tickdirn/=self.tickdirn.norm() \n\n    self.perp = cross3d(self.tickdirn, self.dirn)\n\n    self.textdirn = self._vector(textdirn, self.tickdirn, normalize=true) \n    self.textvertical = self._vector(textvertical, self.dirn, normalize=true) \n    self.textcolor = textcolor\n  }\n\n  _ticksforstep(a, b, dw) { // Creates a possible range given bounds and a stepsize\n    var la=dw*ceil(a/dw), lb=dw*floor(b/dw) // Multiples of dw such that la>a and lb<b\n    return la..lb:dw\n  }  \n\n  /* Creates a range corresponding to a sequence of ticks for \n   and interval [a,b] and with no more than nmax ticks */\n  ticks(a, b, nmax) {\n    var width=(b-a)\n    var dw = 10^(floor(log10(width/nmax))-1) // Guess an initial order of magnitude for the tick spacing\n\n    while(dw<width) {\n      for (x in [dw,2*dw,5*dw]) {\n        var r = self._ticksforstep(a, b, x) \n        if (r.count()<=nmax) return r \n      }\n      dw*=10\n    }\n\n    return a..b:(b-a)/nmax\n  }\n\n  coords(x,y,z) {\n    return self.posn + x*self.tickdirn + y*self.perp + z*self.dirn\n  }\n\n  getfontsize(ticks) {\n    var size = self.fontsize \n    var csize = (size/72) // Size of a line in our units \n    var vsep = self.length/ticks.count() // Separation between ticks \n    if (csize>vsep) size = floor((vsep/csize)*size)\n    return size \n  }\n\n  drawbar() { // Draws the actual color bar \n    var L = self.length/2\n    var r = self.radius \n    var m1 = AreaMesh(fn (u,v) self.coords(r*cos(u),r*sin(u),v), -Pi..Pi:Pi/self.npts, -L..L:L/self.nptsamples,  closed=[true,false]) \n    var m2 = AreaMesh(fn (u,R) self.coords(R*cos(u),R*sin(u),-L), -Pi..Pi:Pi/self.npts, 0..r:r) \n    var m3 = AreaMesh(fn (u,R) self.coords(R*cos(u),R*sin(u),L), -Pi..Pi:Pi/self.npts, 0..r:r) \n    var m =  MeshMerge([m1,m2,m3]).merge()\n\n    return plotfield(Field(m, fn (x,y,z) Matrix([x,y,z]).inner(self.dirn) ), style=\"interpolate\", colormap=self.colormap)\n  }\n\n  drawlabel(min, max) { // Draws the labels and ticks \n    var ticks = self.ticks(min, max, self.nticks)\n    var w = (max-min)\n    var g=Graphics() \n    var fsize = self.getfontsize(ticks) \n    var toffset = abs(self.dirn.inner(self.textdirn))\n    var tvert = sqrt(1-toffset^2)\n\n    for (t in ticks) {\n      var x=(t-min)/w - 0.5\n      var label = String(t)\n      var width = (0.6*label.count()*fsize/72)/2 // Estimate the width\n      var height = fsize/72 // Estimate the height \n      g.display(Cylinder(self.coords(1.2*self.radius,0,x*self.length),self.coords(1.5*self.radius,0,x*self.length),color=White))\n      g.display(\n        Text(label, self.coords((1.8+toffset)*self.radius,0,x*self.length-0.25*height*tvert-width*toffset), size=fsize, dirn=self.textdirn, vertical=self.textvertical, color=self.textcolor)\n        )\n    }\n    return g \n  }\n\n  draw(min, max) {\n    return self.drawbar() + self.drawlabel(min,max) \n  }\n}\n"
  },
  {
    "path": "modules/povray.morpho",
    "content": "\nimport graphics \n\nclass Camera {\n    init(antialias = true, width = 2048, height = 1536, viewangle = 24, viewpoint = nil, look_at = nil, sky = nil) {\n        self.antialias = antialias\n        self.width = width\n        self.height = height\n        self.viewangle = viewangle\n        self.viewpoint = viewpoint\n        self.look_at = look_at\n        self.sky = sky\n        self.light = nil\n        if (!self.viewpoint) self.viewpoint = Matrix([0,0,5])\n        if (!self.look_at) self.look_at = Matrix([0,0,0])\n        if (!self.sky) self.sky = Matrix([0,1,0])\n    }\n}\n\nclass POVRaytracer {\n  init(graphic, camera = nil) {\n\n    self.camera = camera\n    if (!self.camera) self.camera = Camera() // Default Camera\n    self.graphic = graphic\n\n    self.antialias = self.camera.antialias\n    self.width = self.camera.width\n    self.height = self.camera.height\n    self.viewangle = self.camera.viewangle\n    self.viewpoint = self.camera.viewpoint\n    self.look_at = self.camera.look_at\n    self.sky = self.camera.sky\n    self.light = nil\n  }\n\n  vector(v) {\n    return \"<${v[0]}, ${v[1]}, ${v[2]}>\"\n  }\n\n  color(c) {\n    if (isnil(c)) return \"<0.5, 0.5, 0.5>\"\n    if (ismatrix(c)) return self.vector(c)\n    else return self.vector(c.rgb(0))\n  }\n\n  optionalarg(item){\n    \n    var arg = \"\"\n  \n    if(isnil(item.filter) && !isnil(item.transmit)){\n      arg = \"transmit ${item.transmit}\"\n    }\n    else if(!isnil(item.filter) && isnil(item.transmit)){\n      arg = \"filter ${item.filter}\"\n    }\n    else if(!isnil(item.filter) && !isnil(item.transmit)){\n      arg = \"filter ${item.filter} transmit ${item.transmit}\"\n    }\n    return arg\n  }\n\n  visit(Text item, out) {\n    var arg = self.optionalarg(item)\n    out.write(\"text {\" +\n              \"  ttf \\\"cyrvetic.ttf\\\" \\\"${item.string}\\\" 0.1, 0 \\n\" + \n              \"  pigment { rgb ${self.color(item.color)} ${arg} }\")\n    out.write(\"  scale ${item.size / 75} \")\n    out.write(\"  translate ${item.posn[0]}*x + ${item.posn[1]}*y + ${item.posn[2]}*z \")\n    var m = item.transformationmatrix() \n    var str\n    if (m) {\n      str = \"  matrix <\"\n      for (i in 0..3) {\n        for (j in 0..2) {\n          str+=\" \"+String(m[i,j])\n          if (i==3 && j==2) str += \"> \\n }\"\n          else str += \", \"\n          if (j==2) str +=\"\\n\"\n        }\n      }\n    }\n    else {\n        str = \" } \"\n    }\n    out.write(str)\n  }\n\n  visit(Sphere item, out) {\n    var arg = self.optionalarg(item)\n    out.write(\"sphere {\"+\n              \" ${self.vector(item.center)}, ${item.r}\"+\n              \" texture { \"+\n              \" pigment { rgb ${self.color(item.color)} ${arg}\"+\n              \"} } }\")\n  }\n\n  visit(Cylinder item, out) {\n    var radius = 0.5*(item.end - item.start).norm()*item.aspectratio\n    var arg = self.optionalarg(item)\n    out.write(\"cylinder {\"+\n              \" ${self.vector(item.start)}, ${self.vector(item.end)}, ${radius}\"+\n              \" texture { \"+\n              \" pigment { rgb ${self.color(item.color)} ${arg}\"+\n              \"} } }\")\n  }\n\n  visit(Arrow item, out) {\n    var dx = (item.end - item.start).norm()\n    var radius = 0.5*dx*item.aspectratio\n    var cylend = item.start + (item.end - item.start)*(1-item.aspectratio)\n    var arg = self.optionalarg(item)\n    out.write(\"cylinder {\"+\n              \" ${self.vector(item.start)}, ${self.vector(cylend)}, ${radius}\"+\n              \" texture { \"+\n              \" pigment { rgb ${self.color(item.color)} ${arg} \"+\n              \"} } }\")\n    out.write(\"cone {\"+\n              \" ${self.vector(cylend)}, ${2*radius}, ${self.vector(item.end)}, 0\"+\n              \" texture { \"+\n              \" pigment { rgb ${self.color(item.color)} ${arg} \"+\n              \"} } }\")\n  }\n\n  visit(Tube item, out) {\n    self.visit(item.totrianglecomplex(), out)\n  }\n\n  visit(TriangleComplex item, out) {\n\n    var arg = self.optionalarg(item)\n\n    out.write(\"mesh2 {\");\n\n    var nv=item.position.dimensions()[1]\n\n    out.write(\"vertex_vectors { ${nv}, \")\n    for (i in 0...nv) {\n      var s = self.vector(item.position.column(i))\n      if (i<nv-1) s+=\", \"\n      out.write(s)\n    }\n    out.write(\"}\");\n\n    out.write(\"normal_vectors { ${nv}, \")\n    for (i in 0...nv) {\n      var s = self.vector(item.normals.column(i))\n      if (i<nv-1) s+=\", \"\n      out.write(s)\n    }\n    out.write(\"}\");\n\n    var individualcolors = ismatrix(item.colors)\n    if (individualcolors) {\n      out.write(\"texture_list { ${nv}, \")\n      for (i in 0...nv) {\n        var s = \"texture{ pigment{ rgb ${self.vector(item.colors.column(i))} ${arg} } }\"\n        if (i<nv-1) s+=\", \"\n        out.write(s)\n      }\n      out.write(\"}\");\n    }\n\n    var nfaces=item.connectivity.dimensions()[1]\n    out.write(\"face_indices { ${nfaces}, \")\n    for (i in 0...nfaces) {\n      var indx = item.connectivity.rowindices(i)\n      var s = self.vector(indx)\n      if (individualcolors) s += \",${indx[0]},${indx[1]},${indx[2]}\"\n      if (i<nv-1) s+=\", \"\n      out.write(s)\n    }\n    out.write(\"}\");\n\n    if (!individualcolors) {\n      out.write(\" texture { \"+\n                \" pigment { rgb ${self.color(item.colors)} ${arg} \"+\n                \"} }\")\n    }\n\n    out.write(\"}\");\n\n  }\n\n  write(file) {\n    var out = File(file, \"write\")\n\n    out.write(\"#include \\\"colors.inc\\\"\")\n    if (self.transparent) {\n      var col = self.graphic.background.rgb(0)\n      out.write(\"background { rgb <${col[0]}, ${col[1]}, ${col[2]}, 1> }\") // Transparent background by setting alpha to 1\n    }\n    else {\n      out.write(\"background { rgb ${self.vector(self.graphic.background.rgb(0))} }\")\n    }\n    out.write(\"camera {\"+\n              \"location ${self.vector(self.viewpoint)}\"+\n              \"up <0,1,0> right <-1.33,0,0> angle ${self.viewangle}\"+\n              \"look_at ${self.vector(self.look_at)} sky ${self.vector(self.sky)} }\")\n\n    for (item in self.graphic.displaylist) self.visit(item, out)\n\n    var shadowless = \"\"\n    if (self.shadowless) shadowless = \" shadowless\"\n    if (self.light) {\n      for (light in self.light) {\n        out.write(\"light_source {${self.vector(light)} color White${shadowless}}\")\n      }\n    } else out.write(\"light_source {<-5, -5, 8> color White${shadowless}}\")\n\n    out.close()\n    return out.relativepath()\n  }\n\n  render(file, quiet=false, display=true, shadowless=false, transparent=false) {\n    self.shadowless = shadowless // Sets the attribute to be used in the write method\n    self.transparent = transparent // Sets the attribute to be used in the write method and in the command line argument for povray\n    var path = self.write(file)\n    var silent = \"\"\n    if (quiet) silent = \"> /dev/null 2>&1\"\n    var disp = \"\"\n    if (!display) disp = \"-D\"\n    var ua = \"+A\"\n    if (self.transparent) ua = \"+UA\" // Sets alpha output on \n    system(\"povray \\\"${path}\\\" ${disp} ${ua} +W${self.width} +H${self.height} ${silent}\")\n    var out = self._slice(path, 0, path.count()-4)\n    if (!quiet && display) system(\"open ${out}.png\")\n  }\n  /*\n  private method: slice a string given a start and an end\n  @param string: string to be slice\n  @param start: starting index\n  @param end: ending index(exclusive)\n  */\n  _slice(string, start, end) {\n    var s = \"\"\n    for (i in start...end) {\n      s += string[i]\n    }\n    return s\n  }\n}\n"
  },
  {
    "path": "modules/shapeopt.morpho",
    "content": "/* ************************************\n * Shape optimization\n ************************************** */\n\n// Minimize a 1-d function by Brent's algorithm\nfn brent(bracket, func, tol, itermax) {\n  var zeps = 1e-10, cgold = 0.3819660\n\n  fn sign(x, y) { // Returns sign(y)*|x|\n    if (y<0) return -abs(x)\n    if (y>0) return abs(x)\n    return 0.0\n  }\n\n  var iter\n  var a, b // Minimum lies between a and b\n  if (bracket[0]<bracket[2]) a=bracket[0] else a=bracket[2]\n  if (bracket[0]>bracket[2]) b=bracket[0] else b=bracket[2]\n  var d=0.0, e=0.0\n  var xm\n\n  var v=bracket[1], w=v, x=v // Initialize\n  var fv=func(x), fw=fv, fx=fv\n\n  for (iter in 0...itermax) {\n    xm=0.5*(a+b)\n    var tol1=tol*abs(x)+zeps, tol2=2*tol1\n\n    // Check if converged\n    if (abs(x-xm) <= (tol2-0.5*(b-a))) return [x, fx]\n\n    if (abs(e) > tol1) { // Model the function by a parabola\n      var r = (x-w)*(fx-fv),\n          q = (x-v)*(fx-fw),\n          p = (x-v)*q-(x-w)*r\n      q=2*(q-r)\n      if (q>0) p = -p\n      q=abs(q)\n      var etemp = e\n      e=d\n\n      // Check if the parabolic fit is acceptable\n      if (abs(p) >= abs(0.5*q*etemp) || p<= q*(a-x) ||\n          p>= q*(b-x)) { // Bad: Take golden section step\n        if (x>=xm) e=a-x else e=b-x\n        d = cgold*e\n      } else { // Good: Use parabolic step\n        d=p/q\n        var u=x+d\n        if (u-a < tol2 || b-u < tol2) d = sign(tol1, xm-x)\n      }\n    } else {\n      if (x>=xm) e=a-x else e=b-x\n      d = cgold*e\n    }\n\n    var u\n    if (abs(d)>=tol1) u=x+d else u=x+sign(tol1, d)\n    var fu = func(u) // Evaluate function\n\n    // Update bracket\n    if (fu<=fx) {\n      if (u>=x) a=x else b=x\n      v=w; w=x; x=u\n      fv=fw; fw=fx; fx=fu\n    } else {\n      if (u<x) a=u else b=u\n      if (fu <= fw || w==x) {\n        v=w; w=u; fv=fw; fw=fu\n      } else if (fu<=fv || v==x || v==w) {\n        v=u; fv=fu\n      }\n    }\n  }\n  return [x, fx]\n}\n\nclass Constraint {\n  init (func, target) {\n    self.functional = func\n    self.target = target\n    self.selection = nil\n    self.onesided = false\n  }\n}\n\nclass Energy {\n  init (func) {\n    self.functional = func\n    self.selection = nil\n  }\n}\n\nclass ShapeOptimizer {\n  init(m) {\n    self.mesh = m\n    self.energies = []\n    self.constraints = []\n    self.localconstraints = []\n    self.fixed = nil\n    self.stepsize = 0.1\n    self.steplimit = 0.5\n    self.etol = 1e-8 // Energy convergence criterion\n    self.ctol = 1e-10 // Constraint satisfaction criterion\n    self.linmintol = 0.001 // Convergence tolerance for line minimizations\n    self.linminmax = 10 // Maximum number of iterations for line minimizations\n    self.maxconstraintsteps = 10 // Maximum number of constraint steps\n    self.quiet = false // Whether to report\n  }\n\n  /* Adds an energy to the problem */\n  addenergy(functional) {\n    var en = Energy(functional)\n    self.energies.append(en)\n    return en\n  }\n\n  /* Adds a constraint to the problem */\n  addconstraint(functional) {\n    var target = functional.total(self.mesh)\n    var cons = Constraint(functional, target)\n    self.constraints.append(cons)\n    return cons\n  }\n\n  /* Adds a local constraint to the problem */\n  addlocalconstraint(functional, onesided=false) {\n    var cons = Constraint(functional, 0)\n    cons.onesided = onesided\n    self.localconstraints.append(cons)\n    return cons\n  }\n\n  /* Calculate the total energy from a functional*/\n  total(obj) {\n    if (obj.selection) {\n      return obj.functional.total(self.mesh, obj.selection)\n    } else {\n      return obj.functional.total(self.mesh)\n    }\n  }\n\n  /* Calculate the integrand for a functional */\n  integrand(obj) {\n    if (obj.selection) {\n      return obj.functional.integrand(self.mesh, obj.selection)\n    } else {\n      return obj.functional.integrand(self.mesh)\n    }\n  }\n\n  /* Calculate the gradient for a functional */\n  gradient(obj, selection=nil) {\n    var grad\n    var sel=obj.selection // Use the object's selection by default\n    if (selection) sel=selection // If we're passed a selection use it\n\n    if (isselection(sel)) {\n      grad=obj.functional.gradient(self.mesh, sel)\n    } else {\n      grad=obj.functional.gradient(self.mesh)\n    }\n\n    // Zero fixed vertices\n    if (islist(self.fixed)) {\n      var zero = Matrix(grad.dimensions()[0], 1)\n      for (i in self.fixed) {\n        grad.setcolumn(i, zero)\n      }\n    }\n\n    return grad\n  }\n\n  /* Calculates the total energy for a problem */\n  totalenergy() {\n    var energy=0\n    for (en in self.energies) {\n      energy+=self.total(en)\n    }\n    return energy\n  }\n\n  /* Calculates the total force on the vertices */\n  totalforce() {\n    var f\n    for (en in self.energies) {\n      f+=self.gradient(en)\n    }\n    return f\n  }\n\n  /* Fix vertices */\n  fix(s) {\n    if (isnil(self.fixed)) self.fixed=[]\n\n    var lst = s\n    if (isselection(s)) lst = s.idlistforgrade(0)\n\n    if (islist(lst)) { // Add these to the list if they're not already in it\n      for (i in lst) {\n        if (!(self.fixed.ismember(i))) self.fixed.append(i)\n      }\n    }\n  }\n\n  /* Unfix vertices */\n  unfix(s) {\n    if (isnil(self.fixed)) return\n\n    var lst = s\n    if (isselection(s)) lst = s.idlistforgrade(0)\n\n    if (islist(lst)) { // Add these to the list if they're not already in it\n      for (i in lst) {\n        if (self.fixed.ismember(i)) self.fixed.remove(i)\n      }\n    }\n  }\n\n  /* Subtract constraint forces from a given force */\n  subtractconstraints(f) {\n    for (cons in self.constraints) {\n      var g = self.gradient(cons)\n      self.subtractlocalconstraints(g)\n      // Todo Should use Gram=Schmidt to properly enforce multiple global constraints\n\n      var lambda=f.inner(g)/g.inner(g)\n      f.acc(-lambda, g)\n    }\n  }\n\n  /* Test how closely constraints are satisfied.\n     Returns a vector of residuals */\n  testconstraints() {\n    var dv = []\n    for (cons in self.constraints) {\n      dv.append(cons.target-self.total(cons))\n    }\n    return Matrix(dv)\n  }\n\n  /* Calculate matrix of force inner products */\n  forceinnerproducts(fv) {\n    var nc=fv.count()\n    var m=Matrix(nc,nc)\n\n    for (i in 0...nc) {\n      for (j in i...nc) {\n        m[i,j]=fv[i].inner(fv[j])\n        m[j,i]=m[i,j]\n      }\n    }\n    return m\n  }\n\n  /* Reproject onto constrained subspace */\n  reprojectconstraints() {\n    var v = self.mesh.vertexmatrix()\n    var nc = self.constraints.count()\n    if (nc>0) {\n      var residual, i=0\n      do {\n        var dv = self.testconstraints(), fv = []\n\n        for (cons in self.constraints) {\n          var ff = self.gradient(cons)\n          self.subtractlocalconstraints(ff)\n          fv.append(ff)\n        }\n\n        var m=self.forceinnerproducts(fv)\n\n        var sol = dv/m\n        for (i in 0...nc) {\n          v.acc(sol[i],fv[i])\n        }\n\n        residual=self.testconstraints().norm()\n        i+=1\n        if (i>self.maxconstraintsteps) {\n          print \"Warning: Too many steps in constraint satisfaction\"\n          return\n        }\n      } while (residual>self.ctol)\n    }\n  }\n\n  /* Find which local constraints are active */\n  initlocalconstraints() {\n    self.lcactive = []\n    for (cons in self.localconstraints) {\n      if (cons.onesided) {\n        var integrand = self.integrand(cons)\n        var sel = Selection(self.mesh, fn (q) q<self.ctol, integrand)\n        if (cons.selection) sel=sel.intersection(cons.selection)\n        self.lcactive.append(sel)\n      } else {\n        if (cons.selection) self.lcactive.append(cons.selection)\n        else self.lcactive.append(true)\n      }\n    }\n  }\n\n  /* Subtract components of a force vertex by vertex */\n  /* Todo: This will not work well when there are multiple constraints hitting the same vertex */\n  sublocal(f, g) {\n    var nv = f.dimensions()[1]\n    for (var i=0; i<nv; i+=1) {\n      var gc=g.column(i)\n      var gg=gc.inner(gc)\n      if (abs(gg)>self.ctol) {\n        var fc=f.column(i) // Note we only retrieve the column of f if needed\n        var lambda=fc.inner(gc)/gg\n        fc.acc(-lambda, gc)\n        f.setcolumn(i, fc)\n      }\n    }\n  }\n\n  /* Subtracts local constraints */\n  subtractlocalconstraints(f) {\n    for (cons, i in self.localconstraints) {\n      var g=self.gradient(cons, selection=self.lcactive[i])\n      self.sublocal(f,g)\n    }\n  }\n\n  /* Test how closely local constraints are satisfied.\n     Returns a list of vectors of residuals */\n  testlocalconstraints() {\n    var dv = []\n    for (cons, i in self.localconstraints) {\n      if (isselection(self.lcactive[i])) {\n        dv.append(cons.functional.integrand(self.mesh, self.lcactive[i]))\n      } else {\n        dv.append(self.integrand(cons))\n      }\n    }\n    return dv\n  }\n\n  lcnorm(dv) {\n    var norm = 0\n    for (v in dv) norm+=v.norm()\n    return norm\n  }\n\n  /* Reproject local constraints */\n  reprojectlocalconstraints() {\n    var v = self.mesh.vertexmatrix()\n    var nv = v.dimensions()[1]\n    var nc = self.localconstraints.count()\n    if (nc>0) {\n      var residual, iter=0\n      var dv = self.testlocalconstraints()\n\n      if (self.lcnorm(dv)>self.ctol) do {\n        var fv = []\n\n        // Calculate constraint forces\n        for (cons, i in self.localconstraints) {\n          fv.append(self.gradient(cons, selection=self.lcactive[i]))\n        }\n\n        var nactive = 0\n        // Loop over vertices\n        for (k in 0...nv) {\n          // Find the discrepencies of each force at the vertex\n          var vv = Matrix(nc)\n          for (i in 0...nc) vv[i] = -dv[i][0,k] // Note minus sign\n\n          if (vv.norm()>self.ctol) {\n            // Identify constraints active on this vertex\n            var va = [], fa = []\n            for (i in 0...nc) {\n              if (abs(vv[i])>self.ctol) {\n                va.append(vv[i])\n                fa.append(fv[i].column(k))\n              }\n            }\n\n            // Now solve for the necessary motion\n            var m=self.forceinnerproducts(fa)\n            var sol = Matrix(va)/m\n\n            // Move the vertex\n            var newv=v.column(k)\n            for (i in 0...sol.count()) {\n              newv.acc(sol[i], fa[i])\n            }\n            v.setcolumn(k,newv)\n            nactive+=1\n          }\n        }\n\n        dv=self.testlocalconstraints()\n        residual=self.lcnorm(dv)\n        if (nactive>0) residual/=nactive\n        iter+=1\n        if (iter>self.maxconstraintsteps && residual>self.ctol) {\n          print \"Warning: Too many steps in local constraint satisfaction. (Try increasing maxconstraintsteps or set ctol to a number greater than ${residual}).\"\n          return\n        }\n\n      } while (residual>self.ctol)\n    }\n  }\n\n  step(stepsize) {\n    var v = self.mesh.vertexmatrix()\n    var frc = self.totalforce() // Compute the total force\n\n    self.initlocalconstraints() // Find which local constraints are active\n    self.subtractlocalconstraints(frc) // Remove projections onto local constraints\n\n    self.subtractconstraints(frc) // Remove projections onto constraint directions\n\n    v.acc(-stepsize, frc) // Take a step\n\n    self.initlocalconstraints()\n    self.reprojectlocalconstraints() // Reproject onto local constraints\n\n    self.reprojectconstraints() // Push back onto constraints\n  }\n\n  /* Perform relaxation at fixed scale */\n  relax(n) {\n    //var nv = v.dimensions()[1]\n    var energy = [ self.totalenergy() ]\n\n    for (i in 0...n) {\n      self.step(self.stepsize)\n\n      energy.append(self.totalenergy()) // Track the total energy\n      var de = abs(energy[i+1]-energy[i]) // How has it changed?\n\n      // Report\n      if (!self.quiet) print \"Iteration ${i}. Energy: ${energy[i+1]} delta E: ${de}\"\n\n      // Test for convergence\n      if (abs(energy[i+1])<self.etol || de/abs(energy[i+1])<self.etol) break\n    }\n    return energy\n  }\n\n  /* Adaptive stepsize */\n  energywithstepsize(size) {\n    var v=self.mesh.vertexmatrix()\n    var vsave=v.clone()\n\n    // Take the step\n    self.step(size)\n    var energy=self.totalenergy()\n\n    self.last=v\n    self.mesh.setvertexmatrix(vsave) // Restore vertices\n\n    return energy\n  }\n\n  /* Bracket the stepsize */\n  bracketstepsize() {\n    var s = [ 0, self.stepsize, 2*self.stepsize ]\n    var bracket = [ self.totalenergy(), self.energywithstepsize(s[1]), self.energywithstepsize(s[2]) ]\n    var iter = 0\n\n    while (!(bracket[1]<bracket[0] && bracket[1]<bracket[2])) {\n      if (bracket[2]<bracket[1]) { // Step size is too small\n        s[1]=s[2]\n        s[2]=2*s[2]\n        bracket[1]=bracket[2]\n        bracket[2]=self.energywithstepsize(s[2])\n      } else if (bracket[1]>bracket[0]) { // Step size is too big\n        s[1]=s[1]/2\n        bracket[1]=self.energywithstepsize(s[1])\n      } else {\n        print \"Cannot resolve bracket. Current stepsizes ${s} and bracket ${bracket}\"\n        return nil\n      }\n      if (iter>10) { print \"Couldn't bracket stepsize. Adjust stepsize and retry.\"; return nil }\n      iter+=1\n    }\n\n    return [s, bracket]\n  }\n\n  /* Perform relaxation at fixed scale */\n  linesearch(n) {\n    //var nv = v.dimensions()[1]\n    var energy = [ self.totalenergy() ]\n\n    for (i in 0...n) {\n      var brack=self.bracketstepsize()\n\n      if (!islist(brack)) return\n\n      var step = brent(brack[0], self.energywithstepsize, self.linmintol, self.linminmax)\n      if (isnil(step)) break\n\n      self.stepsize = step[0]\n\n      if (self.stepsize > self.steplimit) {\n        self.stepsize = self.steplimit\n        self.step(self.stepsize)\n      } else {\n        self.mesh.setvertexmatrix(self.last)\n      }\n\n      var de = abs(step[1]-energy[-1])\n      energy.append(step[1])\n      if (!self.quiet) print \"Iteration ${i}. Energy: ${energy[-1]} delta E: ${de} stepsize: ${self.stepsize}\"\n\n\n      // Test for convergence\n      if (abs(energy[i+1])<self.etol || de/abs(energy[i+1])<self.etol) break\n    }\n    return energy\n  }\n\n}\n"
  },
  {
    "path": "modules/symmetry.morpho",
    "content": "// Symmetries\n\n/** Translations by a constant vector */\nclass Translate {\n  init (vec) { // Store the translation vector\n    self.vec = vec\n  }\n\n  transform(posn) { // Forward transformation\n    return posn+self.vec\n  }\n\n  inverse(posn) { // Inverse transformation\n    return posn-self.vec\n  }\n}\n"
  },
  {
    "path": "modules/vtk.morpho",
    "content": "/* The VTK Module for Morpho (https://github.com/Morpho-lang/morpho) \n\nThis module provides support for I/O of Meshes and Fields using the VTK\nFile Format.\n\nThe data is stored in the Legacy VTK format, using the `Unstructured\nGrid` Dataset Format. See here for the format documentation:\nhttps://kitware.github.io/vtk-examples/site/VTKFileFormats/  \n\nBy convention, the methods in the classes that have a name starting with\na \"_\" are helper functions and are not meant to be used by the end user.\n\nContact: chaitanya.joshi@tufts.edu */\n\nimport meshtools \nimport parser\n\n// Defining a VTK class that contains methods common to both the importer and the exporter\nclass VTK {\n    init() {}\n\n    // Check whether `fieldname` is a a string is free of whitespaces (a VTK file requirement)\n    _checkfieldname(fieldname) {\n\n        // Check the fieldname\n        if (!isnil(fieldname)) {\n            // If not nil, first check whether the fieldname is a string\n            if (!isstring(fieldname)) {\n                self.err = Error(\"FnameNotStr\", \"Expected a string, but received `${fieldname}`.\")\n                self.err.throw()\n            }\n            // Now that it's a string, checking for embedded whitespaces\n            for (c in fieldname) {\n                if (c==\" \") {\n                    self.err = Error(\"InvalidFname\", \"fieldname (`${fieldname}`) cannot have embedded whitespaces.\")\n                    self.err.throw()\n                }\n            }\n        }\n\n    }\n\n    \n    // Ensure that `filename` ends in \".vtk\"\n    _ensurevtk(filename) {\n        \n        if (!isstring(filename)) {\n            self.err = Error(\"FnameNotStr\", \"Expected a string, but received `${filename}`.\")\n            self.err.throw()\n        }\n        var fl = filename.split(\".\")\n\n        var n = fl.count()\n\n        if (n == 0) { // No extension\n            return \"${filename}.vtk\"\n        }\n        else if (fl[n-1] != \"vtk\") {\n            // Either a wrong extension has been provided, or the\n            // filename itself is expected to contain a period, like\n            // mesh.2.vtk. We again add \".vtk\" at the end in this case.\n            return \"${filename}.vtk\"\n        }\n        else {\n            return filename\n        }\n\n    }\n\n}\n\n// VTKExporter: Exports Fields and / or Meshes to a .vtk file\nclass VTKExporter is VTK {\n\n    init(obj, fieldname=nil) {\n        \n        // Initialize empty lists to optionally store fields later \n        self.fields = []\n        self.fieldnames = []\n\n        // Check whether the object is a Mesh or a Field or none of them\n        if (ismesh(obj)) {\n\n            self.mesh = obj\n\n        } else if (isfield(obj)) {\n            \n            // If the object is a Field, then use its mesh for exporting\n            self.mesh = obj.mesh()\n            \n            // Add this field to the list\n            self.addfield(obj, fieldname=fieldname)\n\n        } else {\n            self.err = Error(\"InitErr\", \"Expected a Mesh or a Field object, but received `${obj}`.\")\n            self.err.throw()\n        }\n\n        self.maxg = self.mesh.maxgrade()\n\n    }\n\n    // Write the preamble for the file. \n    _preamble(file) {\n\n        file.write(\"# vtk DataFile Version 3.0 \") \n        file.write(\"Exported using Morpho https://github.com/Morpho-lang/morpho \") // This line can be anything the user wants.\n        file.write(\"ASCII \")\n        file.write(\"DATASET UNSTRUCTURED_GRID \\n\")\n    }\n    \n    // Writes a vector to the file \n    _writevector(file, v) {\n        if (v.count()==2) {\n            file.write(\"${v[0]} ${v[1]} 0 \")\n        }\n        else {\n            file.write(\"${v[0]} ${v[1]} ${v[2]} \")\n        }\n    }\n\n    // Writes all the vertices to the file\n    _writevertices(file) {\n        self.nvertices = self.mesh.count(0)\n\n        file.write(\"POINTS ${self.nvertices} float \")\n        \n        for (id in 0...self.mesh.count()) {\n            self._writevector(file, self.mesh.vertexposition(id))\n        }\n\n        file.write(\"\\n\")\n\n    }\n\n    /* Add higher grade elements */\n    /*\n    In the VTK legacy file for unstructured grid, the CELLS keyword\n    requires two arguments, `n` and `size`. `n` is the total number of\n    cells, and `size` is the total number of integers that follow,\n    including the numPoints line, which tells the number of points in\n    each cell. The numPoints adds one more number per cell. For example,\n    here is how a single triangle will be represented:\n    CELLS 4 13 \n    2 0 1 \n    2 1 2 \n    2 2 0 \n    3 0 1 2 \n    Here, 4 is the total number of cells including the\n    three edges and one triangle. The 13 is the total number of entries,\n    which is (2+1)*3 + (3+1)*1. In general, the n and size for Morpho\n    meshes become: n = sum_{g=1}^{g_max} n_g, where n_g is the number of\n    elements of grade g\n\n    and size = sum_{g=1}^{g_max} (g+2) * n_g\n    */\n\n    // Function to compute and write the CELLS keyword arguments as above\n    _cellskwargs(file) {\n\n        self.n = 0\n        self.size = 0 \n        self.ng = 0\n        for (g in 1..self.maxg) {\n            self.ng = self.mesh.count(g)\n            self.n = self.n + self.ng\n            self.size = self.size + (g + 2) * self.ng\n        }\n\n        file.write(\"CELLS ${self.n} ${self.size} \")\n\n    }\n\n    // Write an individual cell (edge or face) to the file\n    _writecell(file, g, conn, id) {\n\n        var vids = conn.rowindices(id) // vertex ids for the element\n        var cellstr = \"${g+1} \"       \n        for (v in 0..g) {\n            cellstr += \"${vids[v]} \"\n        }\n        file.write(\"${cellstr}\")\n    \n    }\n\n    // Write all the cells (edges and faces) to the file\n    _writecells(file) {\n\n        self._cellskwargs(file)\n\n        for (g in 1..self.maxg) {\n            var conn = self.mesh.connectivitymatrix(0, g)\n            var nv = g+1 // number of vertices in a grade g element   \n            for (id in 0...self.mesh.count(g)) {\n                \n                self._writecell(file, g, conn, id)\n\n            }\n        }\n\n        file.write(\"\\n\")\n\n    }\n\n    // Export the CELL_TYPES data corresponding to the cells. This is essentially th VTK ID of each cell (see the VTK documentation)\n    _writecelltypes(file) {\n        \n        /* The preamble for CELL_TYPES requires the kewyord argument\n        `n`, which we already evaluated in the _cellskwargs function */\n        file.write(\"CELL_TYPES ${self.n}\") \n\n        /* Now, the VTK cell IDs are:\n        `3` for edges \n        `5` for triangles and \n        `10` for tetrahedra */\n        self.vtkIDs = Matrix([3, 5, 10])\n        for (g in 1..self.maxg) {\n            for (id in 0...self.mesh.count(g)){\n                file.write(\"${self.vtkIDs[g-1]} \")\n            }\n        }\n\n        file.write(\"\\n\")\n        \n    }\n\n    // Write the whole mesh to the file\n    _writemesh(file) {\n\n        self._writevertices(file)\n        self._writecells(file)\n        self._writecelltypes(file)\n\n    }\n    \n    // Write a field to the file\n    _writefield(file, field, fieldname) {\n        /* For now, let's assume that the field has a shape [1,0,0],\n        meaning is only one item on the vertices, and no items on any\n        other grade. */\n\n        // Figure out the quantity sitting on the field \n        var f0 = field[0,0,0]\n\n        if ( isfloat(f0) ) {\n            self.dof = 1\n        } else {\n            self.dof = f0.count()\n        }\n\n        // Handle the scalar case first\n        if (self.dof == 1) {\n            \n            if ( isnil(fieldname) ) fieldname = \"scalars\"\n\n            file.write(\"SCALARS ${fieldname} float 1 \")\n            file.write(\"LOOKUP_TABLE default \")\n            for (id in 0...self.mesh.count()) {\n                file.write(\"${field[0,id]} \")\n            }\n        } else {\n\n            if ( isnil(fieldname) ) fieldname = \"vectors\"\n\n            file.write(\"VECTORS ${fieldname} float\")\n            for (id in 0...self.mesh.count()) {\n                self._writevector(file, field[0,id])\n                \n            }\n        \n        }\n\n        file.write(\"\\n\")\n\n    }\n\n    // Check whether `field` is a Field object, and throw error otherwise\n    _checkfield(field) {\n\n        // Check whether the first argument is a field\n        if (!isfield(field)) {\n            self.err = Error(\"ObjNotField\", \"Expected a Field object, but received `${field}`.\")\n            self.err.throw()\n        }\n\n        // Check whether the field has the right shape\n        var shp = field.shape()\n        var maxgrade = field.mesh().maxgrade()\n        var wrongShape = (shp[0]!=1)\n        for (i in 1...maxgrade) {\n            wrongShape = wrongShape or (shp[i]!=0)\n        }\n        if (wrongShape) {\n            self.err = Error(\"FieldShapeErr\", \"Received a field with shape `${shp}`. Fields with shape other than [1,0,0] are not currently supported.\")\n            self.err.throw()\n        }\n\n        // Check that the field is either a scalar or a 2/3D vector\n        // Figure out the quantity sitting on the field \n        var f0 = field[0,0,0]\n\n        if ( isfloat(f0) ) {\n            return // It's a scalar, so we're good\n        }\n        self.dim = f0.dimensions()\n        if (self.dim[1] != 1) {\n            self.err = Error(\"FieldDimErr\", \"Expected a scalar or a 2D/3D vector field, but received a non-columnar matrix with dimensions ${self.dim}.\")\n            self.err.throw()\n        }\n        if (self.dim[0] != 2 and self.dim[0] != 3) {\n            self.err = Error(\"FieldDimErr\", \"Expected a scalar or a 2D/3D vector field, but received a vector field with ${self.dim[0]} dimensions.\")\n            self.err.throw()\n        }\n    }\n\n    // Function to add a field to the exporter.\n    // Here, `field` is the Field object to be added, and `fieldname` is the name the user wishes to designate the field with. This `fieldname` cannot have embedded whitespaces in it.\n    // Both the field and the fieldname get added to their respective lists \n    addfield(field, fieldname=nil) {\n\n        self._checkfield(field)\n        // If above is passed, add field to the fields list!\n        self.fields.append(field)\n\n        super._checkfieldname(fieldname)\n        // If above is passed, add to the fieldnames list!\n        self.fieldnames.append(fieldname)\n    \n    }\n    \n    // Function to export the fields and / or meshes to a file with the name `filename`. This name has to include the \".vtk\" extension\n    export(filename) {\n        \n        // Ensure filename ends with \".vtk\"\n        self.filename = super._ensurevtk(filename)\n\n        // Open file for writing\n        var f = File(self.filename, \"write\")\n\n        // Add the preamble\n        self._preamble(f)\n\n        // Add the mesh\n        self._writemesh(f)\n\n        // Add fields if any\n        self.nf = self.fields.count() // Number of fields added so far\n        if (self.nf > 0) {\n            f.write(\"POINT_DATA ${self.nvertices} \")\n            for (i in 0...self.nf) {\n                self._writefield(f, self.fields[i], self.fieldnames[i])\n            }\n\n        }\n\n        // Close the file\n        f.close()\n\n    }\n\n}\n\n// VTKImporter: Imports Fields and / or Meshes from a .vtk file\nclass VTKImporter is VTK {\n\n    // Initialize with the filename\n    init(filename) {\n        \n        // Ensure filename ends with \".vtk\"\n        self.filename = super._ensurevtk(filename)\n        \n\n        // Start a mesh builder\n        self.mb = MeshBuilder(dimension=3)\n        self.mesh = nil\n\n        // Create a Dictionary for possible fields to be imported. The keys for the fields in this Dictionary will be the names given to those fields in the VTK file.\n        self.fields = Dictionary()\n\n        // Open the file\n        self.f = File(self.filename)\n\n        // Start the tokenizer\n        self.tok = Tokenizer(self.f)\n        \n        // Go through the whole file\n        while (!self.f.eof()) {\n            self.t = self.tok.next()\n\n            // The mesh vertices start with `POINTS`\n            if (self.t==\"POINTS\") {\n                // Number after `POINTS` is the number of vertices\n                self.nvertices = Int(self.tok.next()) \n                // The word after that is `float`. Let's skip that!\n                self.t = self.tok.next()\n                for (i in 0...self.nvertices) {\n                    self._addvertex()\n                    \n                }\n\n            }\n            // Higher order elements start with `CELLS`\n            if (self.t==\"CELLS\") {\n                // The number after `CELLS` is the number of cells\n                self.ncells = Int(self.tok.next())\n                // The number after that is for memory purposes and we\n                // can ignore that\n                self.t = self.tok.next()\n                for (i in 0...self.ncells) {\n                    self._addcell()\n                }\n\n            }\n\n            // If we get to POINT_DATA, that means mesh is ready\n            // We can build it now.\n            if (self.t==\"POINT_DATA\") self.mesh = self.mb.build()\n\n            if (self.t==\"SCALARS\") {\n\n                // The first word after SCALARS is the name of the field\n                self.fieldname = self.tok.next()\n                // The next 4 words are `float`, `1`, `LOOKUP_TABLE` and\n                // `default`. Skipping those...\n                for (j in 1..4) self.t = self.tok.next()\n                // Initialize a scalar field\n                self.field = Field(self.mesh, 0.0)\n                // The following works ONLY when the fields are on the vertices\n                for (i in 0...self.nvertices) {\n                    self.field[i] = Float(self.tok.next())\n                }\n                self.fields[self.fieldname] = self.field\n            }\n\n            \n            if (self.t==\"VECTORS\") {\n\n                // The first word after VECTORS is the name of the field\n                self.fieldname = self.tok.next()\n                // The next word is float. Skipping that one...\n                self.t = self.tok.next()\n                // Initialize a vector field\n                self.field = Field(self.mesh, Matrix([0,0,0]))\n                // The following works ONLY when the fields are on the vertices\n                for (i in 0...self.nvertices) {\n                    self.field[i] = self._getvector()\n                }\n                self.fields[self.fieldname] = self.field\n            }\n\n        }\n\n        // If there are no fields, then `POINT_DATA` would never be reached. In that case, build the mesh now.\n        if (isnil(self.mesh)) self.mesh = self.mb.build()\n                \n    }\n\n    // Generate a vector out of the next three characters\n    _getvector() {\n\n        self.vx = Float(self.tok.next())\n        self.vy = Float(self.tok.next())\n        self.vz = Float(self.tok.next())\n        return Matrix([self.vx, self.vy, self.vz])\n    \n    }\n\n    // Add a vertex from a given point in the file to the MeshBuilder object \n    _addvertex() {\n        \n        self.v = self._getvector()\n        self.mb.addvertex(self.v)\n    \n    }\n\n    // Add a higher order element from a given point in the file to the MeshBuilder object\n    _addcell() {\n        self.numPoints = Int(self.tok.next())\n        \n        if (self.numPoints==2) {\n        \n            self.e1 = Int(self.tok.next())\n            self.e2 = Int(self.tok.next())\n            self.mb.addedge([self.e1, self.e2])\n        \n        } else if (self.numPoints==3) {\n\n            self.f1 = Int(self.tok.next())\n            self.f2 = Int(self.tok.next())\n            self.f3 = Int(self.tok.next())\n            self.mb.addface([self.f1, self.f2, self.f3])\n            \n        } else if (self.numPoints==4) {\n\n            self.f1 = Int(self.tok.next())\n            self.f2 = Int(self.tok.next())\n            self.f3 = Int(self.tok.next())\n            self.f4 = Int(self.tok.next())\n            self.mb.addvolume([self.f1, self.f2, self.f3, self.f4])\n            \n        }\n    }\n\n    // returns the mesh\n    mesh() {\n        return self.mesh\n    }\n\n    // Returns the field named `fieldname`\n    field(fieldname) {\n\n        super._checkfieldname(fieldname)\n        try {\n            return self.fields[fieldname]\n        } catch {\n            \"DctKyNtFnd\":\n                Error(\"FieldNotFound\", \"Couldn't find the field `${fieldname}` in the file.\").throw()\n                \n        }\n    }\n\n    // Returns a list of the names of the fields present in the file\n    fieldlist() {\n        return self.fields.keys()\n    }\n\n    // Checks whether the file contains a field by a given fieldname \n    containsfield(fieldname) {\n\n        super._checkfieldname(fieldname)\n        return self.fields.contains(fieldname)\n\n    }\n\n}\n"
  },
  {
    "path": "releasenotes/version-0.5.1.md",
    "content": "# Release notes for 0.5.1\n\nWe're pleased to announce morpho 0.5.1, which contains: \n\n* Improvements to error handling:\n  - Programs can now generate errors with the Error class.\n  - Programs can handle errors that occur with try/catch.\n* FloryHuggins functional added for hydrogel simulations.\n* POVray module supports transparency.\n* Improvements to arrays.\n* Various bugfixes."
  },
  {
    "path": "releasenotes/version-0.5.2.md",
    "content": "# Release notes for 0.5.2\n\nWe're pleased to announce morpho 0.5.2, which contains: \n\n* New `meshgen` module to create meshes in 2D and 3D.\n* New `delaunay` module to create Delaunay triangulations in arbitrary dimensions.\n* New `vtk` module to load and save meshes and fields in VTK legacy format. \n* `List`, `Array` and `Matrix` slices (e.g. `a[0..5]` means elements 0 to 5 of a list) \n* Minor improvements to apply, min, max and bounds functions. \n* Code improvements to facilitate future expansion of Morpho. \n* Numerous bugfixes. \n"
  },
  {
    "path": "releasenotes/version-0.5.3.md",
    "content": "# Release notes for 0.5.3\n\nWe're pleased to announce morpho 0.5.3, which contains: \n\n* Support for complex numbers via a `Complex` class. \n* Meshslice module enables taking a slice through a `Mesh` and associated `Field` objects. \n* Improved `File` class. \n* Many bugfixes.\n* Apple M1 support.\n"
  },
  {
    "path": "releasenotes/version-0.5.4.md",
    "content": "# Release notes for 0.5.4\n\nWe're pleased to announce morpho 0.5.4, which contains many improvements. This is a large update with many new features as below. Going forward, new incremental work will be collected in the dev branch (we started doing this for this version) and we will move to a bimonthly release schedule. \n\n## Meshtools\n\nThe `meshtools` module has been extensively revised with many new features: \n\n* New `MeshPruner` class added that enables coarsening of meshes, analogous to `MeshRefiner`. \n* Refinement of 3D elements.\n* Refinement of selections is improved.\n* Bugfixes for `MeshRefiner` and `MeshMerge` to prevent duplicate elements being generated in some circumstances. \n\n## Text\n\nThe `morphoview` application now supports text rendering. A number of modules have been updated to take advantage of this: \n\n* `plot` now provides `ScaleBar` objects that are useful for `plotfield`, as well as `plotmeshlabels` to label a mesh with element ids. \n* `graphics` now provides a `Text` class for textual elements, and has some performance improvements. \n* `color` now provides a number of new `ColorMap`s: `ViridisMap`, `InfernoMap`, `MagmaMap` and `PlasmaMap`, all of which are more friendly for people with color vision deficiency. \n\n## Variadic functions\n\nYou can now create functions that accept a variable number of parameters. Arguments passed to a function can be accessed as a `List`. \n\n    fn func(x, ...v) {\n        for (a in v) print a\n    }\n\nOther improvements:\n\n* New `VolumeIntegral` module to complement `AreaIntegral` and `LineIntegral`. \n* Internal improvements to the morpho virtual machine. \n* A `System` class to enable you to get platform information. \n* Numerous bugfixes. \n* Numerous improvements to the documentation.\n* Improvements to the `povray` module. \n* New examples for `plot` module. \n* You can now translate the view in `morphoview` by right click and dragging or using alt-arrow keys. "
  },
  {
    "path": "releasenotes/version-0.5.5.md",
    "content": "# Release notes for 0.5.5\n\nWe're pleased to announce morpho 0.5.5, which contains a number of important improvements, and notably some significant performance improvements.\n\n## Documentation\n\nWe have added two new chapters to the manual, one on working with Meshes and the other describing the examples in detail. Additional chapters to follow. We've also improved the formatting of the manual, and a number of previously undocumented features are now documented in the manual and in the interactive help.\n\n## Developer tools\n\nMorpho now provides a profiler and a rewritten debugger. To use these, run with -profile or -debug respectively. Upon completion, the profiler will produce a report of the fraction of program execution time spent in each morpho function, allowing the programmer to identify optimization targets. We've used this tool to significantly improve a number of morpho components.\n\n## Mixins\n\nYou can now create a class from multiple other classes (called a mixin) using the new `with` keyword: \n\n    class Foo is Boo with Hoo, Moo { ... }\n\nBoo is the superclass of Foo, but methods defined in Hoo and Moo are copied into Foo before Foo's methods are defined. This enables greater modularity and facilitates code reuse.\n\n## New linear algebra features\n\nIts now possible to convert Sparse matrices to dense matrices and vice-versa by passing them to the relevant constructor function, e.g.\n\n    var a = Sparse([[0,0,1],[1,1,1]])\n    var b = Matrix(a)\n\nYou can assemble matrices in block form using other matrices:\n\n    var c = Matrix([[a,0],[0,a]])\n\nYou can compute the eigenvalues and eigenvectors of a matrix with the new eigenvalues() and eigensystem() methods.\n\nPreliminary work for numerical hessians is in place.\n\n## Other improvements\n\n* Interactive mode now supports UTF8 characters.\n* Object now provides respondsto() and has() to determine the available methods and properties respectively. \n* Optimizing compiler [off by default; run with -O flag] lays the groundwork for significant future performance improvements.\n* MeshGen and Delauney modules run significantly faster.\n* Hydrogel functional is faster and dimensionally independent.\n* Numerous minor bugfixes.\n"
  },
  {
    "path": "releasenotes/version-0.5.6.md",
    "content": "# Release notes for 0.5.6\n\nWe're pleased to announce morpho 0.5.6, which contains a number of improvements, particularly focussed on performance and extensibility. \n\n## Parallelized Force and Energy calculations\n\nMorpho now supports parallelized force and energy calculations, which can lead to significant speedups for some programs. To use these, run morpho with -w flag and supply the number of worker threads to use: \n\n    morpho5 -w4 program.morpho\n\nFurther features for parallel programming will appear in future releases. \n\n## Resources and Packages\n\nThe morpho runtime can now look for resource files---help files, morpho files, etc.---in multiple places. The default location is now configurable at installation, and also via a .morphopackages file stored in the user home directory. This enables morpho modules to live in their own git repository, together with resource files, and should make it easier for users to contribute to morpho. More details are in the dev guide. \n\n## Extensions\n\nIt's now possible to extend morpho through dynamic libraries written in C and linked at runtime. From the user's perspective, these work just like modules using the `import` keyword. \n\n## New manual chapter on visualization\n\nWe continue to improve the manual, and now include a chapter on visualization. The developer guide has also been updated. \n\n## Other improvements\n\n* Improvements to morpho's object model. New Function, Closures and Invocation classes provided that respond to standard methods. \n* Fixes to some functionals to work correctly with 2D meshes. \n* You can now supply anonymous functions in the arguments to a function.\n* You can set the minimum and maximum values for plotfield using the optional cmin and cmax and arguments. \n* Manual contains additional information on installation"
  },
  {
    "path": "releasenotes/version-0.5.7.md",
    "content": "# Release notes for 0.5.7\n\nWe're pleased to announce morpho 0.5.7, which is the final release in the 0.5 series. This release contains a number of improvements and bugfixes. \n\n## Windows install instructions fixed\n\nWe have updated the installation instructions for Windows with this release to work with either WSL1 or WSL2. \n\n## Gradients in AreaIntegral and VolumeIntegral\n\nYou can now compute the local gradient of a field using the grad() function within the integrand supplied to AreaIntegral and VolumeIntegral. This significantly enhances the number of models morpho can handle. \n\n## Improved System class\n\n* System.print(), System.readline() and System.sleep() methods added. \n\n* System.clock() now reports wall time (useful for testing the effect of parallelization)\n\n* System.arguments() provides access to the command line options morpho was run with. \n\n## Minor improvements\n\n* New Matrix.roll() and List.roll() methods shift the contents of a List or Matrix respectively. \n\n* Field.linearize() provides access to the underlying Matrix store. \n\n* IdentityMatrix() constructor function. \n\n* Debugger now supports printing of global variables and object properties. \n\n* Fix issues with compilation on some platforms.\n\n* Experimental support for accessing integrand values for individual elements on some functionals. \n\n* Numerous minor bugfixes.\n"
  },
  {
    "path": "releasenotes/version-0.6.0.md",
    "content": "# Release notes for 0.6.0\n\nWe're pleased to announce Morpho 0.6.0, which represents a great deal of behind-the-scenes work to ready the Morpho codebase for future developments. See our Roadmap document for more details. \n\n## Morpho now built as a shared library\n\nRather than the previous monolithic strucutre, the Morpho codebase has been divided into a shared library (\"libmorpho\") and a terminal application (\"morpho-cli\"). This means that Morpho can easily be embedded in other applications, and improves maintainability as these components can be updated separately. Morphoview has been migrated to a separate repository.\n\n## Internal improvements\n\n* Major code reorganization to improve the logical structure and maintainability of the morpho codebase. \n* Transitioned to Cmake build system to improve cross-platform compilation.\n* Rewritten parser to improve error reporting and enable reuse across Morpho. \n\n## Improved quadrature\n\nFunctionals like `LineIntegral`, `AreaIntegral` and `VolumeIntegral` can now make use of a greatly improved quadrature routine. This will become the default in future versions of Morpho. Particularly in 3D, the new routine offers significantly improved performance, and can be extended in future. To use the new quadrature routine simply set the `method` optional argument: \n\n    var a = AreaIntegral(integrandfn, method = {})\n\nThe method Dictionary can specifically request particular quadrature rules or orders; more information will be in the dev guide. \n\n## Namespaces\n\nYou can now use the `import` keyword with a new keyword, `as`, to import the contents of a module into a given namespace: \n\n    import color as col\n\n    print col.Red \n\nThis helps Morpho programs avoid library conflicts and improves modularization.\n\n## Tuple data type\n\nMorpho now supports Tuples, an ordered immutable collection. The syntax is similar to Python: \n\n    var t = (0,1,2,3)\n\nTuples act much like Lists, but can be used as keys in a Dictionary. \n\n## JSON import and export\n\nMorpho now provides a `JSON` class which supports import and output using the JavaScript Object Notation (JSON) format, widely used for data interchange.\n\n    var a = JSON.parse(\"[1,2,3]\")\n    print a \n\n## Minor new features\n\n* Formatted output for numbers is now available using the `format` method on the `Int` and `Float` classes. \n* Errors can now be raised as \"warnings\", which are alerts to the user that do not interrupt execution.\n\n## Improved documentation\n\nMany previously un- or under-documented features have now been added to the interactive help. If you notice something that isn't well documented, please alert us via the issue tracker in Github. \n\n## Minor fixes\n\n* Many improvements to the debugger, including better support for printing object properties. \n* Improved calculation of derivatives. \n* Bugfixes to closures, string interpolation, parallel force and energy calculations and many others."
  },
  {
    "path": "releasenotes/version-0.6.1.md",
    "content": "# Release notes for 0.6.1\n\nWe're pleased to announce Morpho 0.6.1, which incorporates very important new language features and sets morpho up for future improvements.\n\n## Types\n\nMorpho now supports types. Variables can be declared with a specified type like so\n\n    String s = \"Hello\" \n\nand the type of function parameters can be specified like\n\n    fn f(String s, List l) { }\n\n## Multiple dispatch\n\nMorpho now supports *multiple dispatch*, whereby you can define multiple implementations of a function that accept different parameter types. The correct implementation to use is selected at runtime: \n\n    fn f(String x) { }\n    fn f(List x) { }\n\nMethods defined on classes also support this mechanism. You can still specify parameters that without a type, in which case all types are accepted. Multiple dispatch is implemented efficiently (it incurs only a small overhead relative to a traditional function call) and is very useful to remove complex type checking. We are using this feature to improve how morpho works internally, as well as to implement new morpho packages. \n\n## Additional hessians \n\nLineCurvatureSq and LineTorsionSq now provide the hessian() method. \n\n## Preliminary support for finite element discretizations\n\nWe have begun to include support for additional discretizations beyond the linear elements supported by prior versions of Morpho in the codebase. This feature is a work in progress and not yet completely ready for use; we expect to complete it in forthcoming releases. \n\n## Minor fixes\n\n* Bugfixes to parallelization.\n* Error messages now refer to the module in which the error was found. \n* Can now call throw() and warning() directly on the Error class. \n"
  },
  {
    "path": "releasenotes/version-0.6.2.md",
    "content": "# Release notes for 0.6.2\n\nWe're pleased to announce Morpho 0.6.2, which is primarily a maintenance release and incorporates a number of bugfixes and improvements.\n\n## Morphopm package manager\n\nAlongside this release, we are pleased to announce a new package manager for morpho called `morphopm`, which makes installation of morpho packages significantly easier for users. Morphopm is available [on github](https://github.com/Morpho-lang/morpho-morphopm) and can also be installed via homebrew:\n\n    brew tap morpho-lang/morpho\n    brew install morpho-morphopm\n\n## Benchmarks\n\nThe benchmarks folder, which used to contain a number of basic benchmarks for morpho, has been moved to a [new repository](https://github.com/Morpho-lang/morpho-benchmark) with several new benchmarks added. We will be using these to continue to improve morpho's performance.\n\n## Ternary operator\n\nMorpho now provides the ternary operator similar to other C-family languages:\n\n    var a = (b < c ? b : c)\n\n## Minor fixes\n\n* Keywords can now be used as method and property labels.\n* The povray module now produces silent output on linux if the quiet option is set.\n* apply() now works properly with metafunctions.\n* Bugfixes in the Sparse class.\n* Improvements to resource locator.\n* Fixes to multiple dispatch with variadic parameters.\n* Fixes to Range class.\n* Morpho running the test suite now passes valgrind memory checker.\n* meshtools now explicitly checks that the Mesh generated isn't too large.\n"
  },
  {
    "path": "releasenotes/version-0.6.3.md",
    "content": "# Release notes for 0.6.3\n\nWe're pleased to announce Morpho 0.6.3, which contains a number of improvements and represents the first steps towards cross-platform compatibility.\n\n## Additional finite elements\n\nMorpho now provides finite element types beyond the linear Lagrangian elements (CG1) used previously. Quadratic Lagrangian elements (CG2) are implemented for fields on 1, 2 and 3D elements, and we will provide additional elements in future releases. Interpolated gradients are available for all element types.\n\n## Preliminary Windows build\n\nThe Morpho library and terminal app now can be natively built on Windows using Clang. To enable this, Morpho has been significantly refactored to isolate platform-specific code in a single location.\n\nWe will provide a Windows installer and binaries in future releases once morphoview has also been ported.\n\n## Self is not longer necessary for invocations\n\nIt is no longer required to use the `self` keyword to invoke a method call, i.e.\n\n    self.foo() \n\ncan be replaced with:\n\n    foo() \n\nLocal variables take precedence over method labels, so that\n\n    var foo = \"hi\"\n    foo() \n\nraises an error even if foo is a method in the same class. \n\n## Minor fixes\n\n* Improvements to the adaptive quadrature routines, which now use better rules by default and report clearer error messages.\n* Many improvements to the test suite, with tests more clearly structured.\n* String parsing now unicode aware.\n* AreaIntegral now supports an experimental option weightByReference, which weights the integral by a reference mesh, rather than the target mesh (this is useful for some elasticity problems)."
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        morpho.h build.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        morpho.h build.h\n)\n\nadd_subdirectory(builtin)\nadd_subdirectory(classes)\nadd_subdirectory(core)\nadd_subdirectory(datastructures)\nadd_subdirectory(debug)\nadd_subdirectory(geometry)\nadd_subdirectory(linalg)\nadd_subdirectory(support)\n"
  },
  {
    "path": "src/build.h",
    "content": "/** @file build.h\n *  @author T J Atherton\n *\n *  @brief Define constants that choose how Morpho is built\n */\n\n#include <float.h>\n\n/* **********************************************************************\n * Version\n * ********************************************************************** */\n\n#define MORPHO_VERSIONSTRING \"0.6.3\"\n\n#define MORPHO_VERSION_MAJOR 0\n#define MORPHO_VERSION_MINOR 6\n#define MORPHO_VERSION_PATCH 3\n\n/* **********************************************************************\n * Paths and file system\n * ********************************************************************** */\n\n#ifndef MORPHO_HELP_BASEDIR\n    #define MORPHO_HELP_BASEDIR \"/usr/local/share/morpho/help\"\n#endif\n\n#ifndef MORPHO_MODULE_BASEDIR\n    #define MORPHO_MODULE_BASEDIR \"/usr/local/share/morpho/modules\"\n#endif\n\n#define MORPHO_HELPDIR \"share/help\"           // Package subdir. where help files are found\n#define MORPHO_MODULEDIR \"share/modules\"      // Package subdir. where modules are found\n#define MORPHO_EXTENSIONDIR \"lib\"             // Package subdir. where extensions are found\n\n#define MORPHO_EXTENSION \"morpho\"             // File extension for morpho files\n#define MORPHO_HELPEXTENSION \"md\"             // File extension for help files\n#ifndef MORPHO_DYLIBEXTENSION\n    #define MORPHO_DYLIBEXTENSION \"dylib\"     // File extension for extensions\n#endif\n\n#define MORPHO_DIRSEPARATOR '/'               // File directory separator\n\n#define MORPHO_PACKAGELIST \".morphopackages\"  // File in $HOME that contains package locations\n\n/* **********************************************************************\n * Numeric tolerances\n * ********************************************************************** */\n\n/** Value used to detect zero */\n#define MORPHO_EPS DBL_EPSILON\n\n/** Relative tolerance used to compare double precision equality */\n#define MORPHO_RELATIVE_EPS DBL_EPSILON\n\n/* **********************************************************************\n * Size limits\n * ********************************************************************** */\n\n/** @brief Maximum length of a Morpho error string. */\n#define MORPHO_ERRORSTRINGSIZE 255\n\n/** @brief Default size of input buffer. */\n#define MORPHO_INPUTBUFFERDEFAULTSIZE 1024\n\n/** @brief Maximum file name length. */\n#define MORPHO_MAXIMUMFILENAMELENGTH 255\n\n/** @brief Size of the call frame stack. */\n#define MORPHO_CALLFRAMESTACKSIZE 255\n\n/** @brief Size of the error handler stack. */\n#define MORPHO_ERRORHANDLERSTACKSIZE 64\n\n/** @brief Maximum number of object types */\n#define MORPHO_MAXIMUMOBJECTDEFNS 64\n\n/** @brief Type numbers in a value must be less than this value */\n#define MORPHO_MAXIMUMVALUETYPES 8\n\n/** @brief Maximum number of arguments */\n#define MORPHO_MAXARGS 255 /** @warning Note that this cannot easily be adjusted >255 without changing the instruction encoding */\n\n/** @brief Maximum number of constants */\n#define MORPHO_MAXCONSTANTS 65536 /** @warning Note that this cannot easily be adjusted >65536 without changing the instruction encoding */\n\n/* **********************************************************************\n* Performance\n* ********************************************************************** */\n\n/** @brief Build Morpho VM with computed gotos */\n#define MORPHO_COMPUTED_GOTO\n\n/** @brief Build Morpho VM with small but hacky value type [NaN boxing] */\n#ifndef _NO_NAN_BOXING\n#define MORPHO_NAN_BOXING\n#endif\n/** @brief Number of bytes to bind before GC first runs */\n#define MORPHO_GCINITIAL 1024\n/** It seems that DeltaBlue benefits strongly from garbage collecting while the heap is still fairly small */\n\n/** @brief Controls how rapidly the GC tries to collect garbage */\n#define MORPHO_GCGROWTHFACTOR 2\n\n/** @brief Initial size of the stack */\n#define MORPHO_STACKINITIALSIZE 256\n\n/** @brief Controls how rapidly the stack grows */\n#define MORPHO_STACKGROWTHFACTOR 2\n\n/** @brief Limits size of statically allocated arrays on the C stack */\n#define MORPHO_MAXIMUMSTACKALLOC 256\n\n/** @brief Default number of threads */\n#define MORPHO_DEFAULTTHREADNUMBER 0\n\n/** @brief Size of L1 cache line */\n#define _MORPHO_L1CACHELINESIZE 128 // M1/M2 is 128; most intel are 64\n\n/** @brief Pad data structures involved in multiprocessing */\n#define _MORPHO_PADDING char __padding[_MORPHO_L1CACHELINESIZE]\n\n/* **********************************************************************\n * Core library [options set in CMake]\n * ********************************************************************** */\n\n/** Build with Matrix class using BLAS/LAPACK */\n//#define MORPHO_INCLUDE_LINALG\n\n/** Build with Sparse class */\n//#define MORPHO_INCLUDE_SPARSE\n\n/** Build with geometry classes */\n//#define MORPHO_INCLUDE_GEOMETRY\n\n/* **********************************************************************\n * Libraries\n * ********************************************************************** */\n\n/** Use Apple's accelerate library for dense linear algebra */\n#define MORPHO_LINALG_USE_ACCELERATE\n\n/** Use the LAPACKE library for dense linear algebra */\n//#define MORPHO_LINALG_USE_LAPACKE\n\n/** Use CSparse for sparse matrix */\n#define MORPHO_LINALG_USE_CSPARSE\n\n/* **********************************************************************\n* Debugging\n* ********************************************************************** */\n\n/** @brief Include debugging features */\n#define MORPHO_DEBUG\n\n/** @brief Print each instruction executed by the VM. */\n//#define MORPHO_DEBUG_PRINT_INSTRUCTIONS\n\n/** @brief Display syntax tree after parsing */\n//#define MORPHO_DEBUG_DISPLAYSYNTAXTREE\n\n/** @brief Display register allocations during compilation */\n//#define MORPHO_DEBUG_DISPLAYREGISTERALLOCATION\n\n/** @brief Disables garbage collector */\n//#define MORPHO_DEBUG_DISABLEGARBAGECOLLECTOR\n\n/** @brief Stress test garbage collector */\n#ifdef _DEBUG_STRESSGARBAGECOLLECTOR\n    #define MORPHO_DEBUG_STRESSGARBAGECOLLECTOR\n#endif\n\n/** @brief Log garbage collector */\n//#define MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n\n/** @brief Check GC size tracking */\n//#define MORPHO_DEBUG_GCSIZETRACKING\n\n/** @brief Fill global constant table */\n//#define MORPHO_DEBUG_FILLGLOBALCONSTANTTABLE\n\n/** @brief Debug symbol table */\n//#define MORPHO_DEBUG_SYMBOLTABLE\n\n/** @brief Diagnose opcode usage */\n//#define MORPHO_OPCODE_USAGE\n\n/** @brief Buiild with profile support */\n#define MORPHO_PROFILER\n"
  },
  {
    "path": "src/builtin/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        builtin.c       builtin.h     \n        functiondefs.c  functiondefs.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        builtin.h functiondefs.h\n)\n"
  },
  {
    "path": "src/builtin/builtin.c",
    "content": "/** @file builtin.c\n *  @author T J Atherton\n *\n *  @brief Morpho built in functions and classes\n*/\n\n#include \"builtin.h\"\n#include \"common.h\"\n#include \"object.h\"\n#include \"functiondefs.h\"\n#include \"file.h\"\n#include \"system.h\"\n#include \"classes.h\"\n\n#include \"sparse.h\"\n#include \"geometry.h\"\n\n/* **********************************************************************\n * Global data\n * ********************************************************************** */\n\n/** A table of built in functions */\ndictionary builtin_functiontable;\n\n/** A table of built in classes */\ndictionary builtin_classtable;\n\n/** A table of symbols used by built in classes */\ndictionary builtin_symboltable;\n\n/** Maintain a list of objects created by builtin */\nobject *builtin_objects;\n\n/** Current function and class tables */\ndictionary *_currentfunctiontable;\ndictionary *_currentclasstable;\n\n/* **********************************************************************\n * Utility functions\n * ********************************************************************** */\n\n/** Initialize an objectbuiltinfunction */\nvoid builtin_init(objectbuiltinfunction *func) {\n    func->flags=BUILTIN_FLAGSEMPTY;\n    func->function=NULL;\n    func->name=MORPHO_NIL;\n    func->klass=NULL;\n    signature_init(&func->sig);\n}\n\n/** Clear an objectbuiltinfunction */\nvoid builtin_clear(objectbuiltinfunction *func) {\n    morpho_freeobject(func->name);\n    signature_clear(&func->sig);\n}\n\n/** @brief An enumerate loop.\n    @details Successively calls enumerate on obj, passing the result to the supplied function.\n    @param[in] v - the virtual machine\n    @param[in] obj - object to enumerate over\n    @param[in] fn - function to call\n    @param[in] ref - reference to pass to the function\n    @returns true on success */\nbool builtin_enumerateloop(vm *v, value obj, builtin_loopfunction fn, void *ref) {\n    value enumerate=MORPHO_NIL;\n    value count=MORPHO_NIL, in=MORPHO_INTEGER(-1), val=MORPHO_NIL;\n    \n    if (morpho_lookupmethod(obj, enumerateselector, &enumerate)) {\n        if (!morpho_invoke(v, obj, enumerate, 1, &in, &count)) return false;\n        if (!MORPHO_ISINTEGER(count)) return false;\n        \n        for (indx i=0; i<MORPHO_GETINTEGERVALUE(count); i++) {\n            in=MORPHO_INTEGER(i);\n            \n            if (!morpho_invoke(v, obj, enumerate, 1, &in, &val)) return false;\n            \n            if (!(*fn) (v, i, val, ref)) return false;\n        }\n    }\n    \n    return true;\n}\n\n/** Binds an object to the builtin environment */\nvoid builtin_bindobject(object *obj) {\n    if (!obj->next && /* Object is not already bound to the program (or something else) */\n        builtin_objects!=obj &&\n        obj->status==OBJECT_ISUNMANAGED) {\n        obj->status=OBJECT_ISBUILTIN;\n        obj->next=builtin_objects;\n        builtin_objects=obj;\n    }\n}\n\n/* **********************************************************************\n * Optional arguments\n * ********************************************************************** */\n\nint vm_getoptionalargs(vm *v);\n\n/** Process optional arguments */\nbool builtin_options(vm *v, int nargs, value *args, int *nfixed, int noptions, ...) {\n    va_list optlist;\n    va_start(optlist, noptions);\n    int nopt=vm_getoptionalargs(v);\n    \n    for (unsigned int i=0; i<noptions; i++) {\n        value symbol = va_arg(optlist, value);\n        value *dest = va_arg(optlist, value*);\n        \n        for (int k=0; k<nopt; k++) {\n            int r = nargs + 1 + 2*k; // Corresponding register\n            if (MORPHO_ISSAME(symbol, args[r])) {\n                *dest = args[r+1];\n                break;\n            }\n        }\n        // TODO: Should raise an error for unexpected options here by looking for arguments that are strings and unmanaged?\n    }\n    if (nfixed) *nfixed = nargs; // Exclude register 0\n    \n    va_end(optlist);\n    \n    return true;\n}\n\n/** Tests whether an object is callable */\nbool builtin_iscallable(value val) {\n    return (MORPHO_ISOBJECT(val) && (MORPHO_ISFUNCTION(val) ||\n                                     MORPHO_ISCLOSURE(val) ||\n                                     MORPHO_ISINVOCATION(val) ||\n                                     MORPHO_ISBUILTINFUNCTION(val) ||\n                                     MORPHO_ISMETAFUNCTION(val)));\n}\n\n/* **********************************************************************\n * object_builtinfunction definition\n * ********************************************************************** */\n\n/** Instance object definitions */\nvoid objectbuiltinfunction_printfn(object *obj, void *v) {\n    objectbuiltinfunction *f = (objectbuiltinfunction *) obj;\n    if (f) morpho_printf(v, \"<fn %s>\", (MORPHO_ISNIL(f->name) ? \"\" : MORPHO_GETCSTRING(f->name)));\n}\n\nvoid objectbuiltinfunction_freefn(object *obj) {\n    builtin_clear((objectbuiltinfunction *) obj);\n}\n\nsize_t objectbuiltinfunction_sizefn(object *obj) {\n    return sizeof(objectbuiltinfunction);\n}\n\nobjecttypedefn objectbuiltinfunctiondefn = {\n    .printfn=objectbuiltinfunction_printfn,\n    .markfn=NULL,\n    .freefn=objectbuiltinfunction_freefn,\n    .sizefn=objectbuiltinfunction_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * Create and find builtin functions\n * ********************************************************************** */\n\n/** Gets the current function table */\ndictionary *builtin_getfunctiontable(void) {\n    return _currentfunctiontable;\n}\n\n/** Sets the current function table */\nvoid builtin_setfunctiontable(dictionary *dict) {\n    _currentfunctiontable=dict;\n}\n\n/** Gets the current class table */\ndictionary *builtin_getclasstable(void) {\n    return _currentclasstable;\n}\n\n/** Sets the current class table */\nvoid builtin_setclasstable(dictionary *dict) {\n    _currentclasstable=dict;\n}\n\n/** Add a builtin function.\n * @param name  name of the function\n * @param func  the corresponding C function\n * @param flags flags to define the function\n * @returns value referring to the objectbuiltinfunction */\nvalue builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags) {\n    value out=MORPHO_NIL;\n    morpho_addfunction(name, NULL, func, flags, &out);\n    return out;\n}\n\n/** Finds a builtin function from its name */\nvalue builtin_findfunction(value name) {\n    value out=MORPHO_NIL;\n    dictionary_get(&builtin_functiontable, name, &out);\n    return out;\n}\n\nobjectclass *builtin_getparentclass(value fn) {\n    if (MORPHO_ISFUNCTION(fn)) return MORPHO_GETFUNCTION(fn)->klass;\n    else if (MORPHO_ISBUILTINFUNCTION(fn)) return MORPHO_GETBUILTINFUNCTION(fn)->klass;\n    else if (MORPHO_ISMETAFUNCTION(fn)) return MORPHO_GETMETAFUNCTION(fn)->klass;\n    else if (MORPHO_ISCLASS(fn)) return MORPHO_GETCLASS(fn)->superclass;\n    \n    return NULL;\n}\n\n/** Adds a new builtinfunction to a given dictionary.\n * @param[in] dict  the dictionary\n * @param[in] name  name of the function to add\n * @param[in] fn function to add\n * @param[out] out the function added (which may be a metafunction)\n * @returns true on success */\nbool builtin_addfunctiontodict(dictionary *dict, value name, value fn, value *out) {\n    bool success=false;\n    value entry=fn; // Dictionary entry for this name\n    value selector = dictionary_intern(&builtin_symboltable, name); // Use interned name\n    \n    if (dictionary_get(dict, selector, &entry)) { // There was an existing function\n        if (MORPHO_ISBUILTINFUNCTION(entry)) { // It was a builtinfunction, so we need to create a metafunction\n            if (builtin_getparentclass(fn) !=\n                MORPHO_GETBUILTINFUNCTION(entry)->klass) { // Override superclass methods for now\n                dictionary_insert(dict, selector, fn);\n            } else if (metafunction_wrap(name, entry, &entry)) { // Wrap the old definition in a metafunction\n                \n                builtin_bindobject(MORPHO_GETOBJECT(entry));\n                metafunction_add(MORPHO_GETMETAFUNCTION(entry), fn); // Add the new definition\n                success=dictionary_insert(dict, selector, entry);\n            }\n        } else if (MORPHO_ISMETAFUNCTION(entry)) { // It was already a metafunction so simply add the new function\n            success=metafunction_add(MORPHO_GETMETAFUNCTION(entry), fn);\n        }\n    } else success=dictionary_insert(dict, selector, fn);\n    \n    if (success && out) *out = entry;\n    \n    return success;\n}\n\n/** Add a function to the morpho runtime\n * @param name  name of the function\n * @param signature [optional] signature for the function\n * @param func  the corresponding C function\n * @param flags flags to define the function\n * @param[out] value the function created as usable with morpho_call\n * @returns true on success */\nbool morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags, value *out) {\n    objectbuiltinfunction *new = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION);\n    if (!new) goto morpho_addfunction_cleanup;\n    \n    builtin_init(new);\n    new->function=func;\n    new->flags=flags;\n    \n    new->name=object_stringfromcstring(name, strlen(name));\n    if (!name) goto morpho_addfunction_cleanup;\n    \n    // Parse function signature if provided\n    if (signature &&\n        !signature_parse(signature, &new->sig)) {\n        UNREACHABLE(\"Syntax error in signature definition.\");\n    }\n    \n    value newfn = MORPHO_OBJECT(new);\n    \n    if (!builtin_addfunctiontodict(_currentfunctiontable, new->name, newfn, NULL)) {\n        UNREACHABLE(\"Redefinition of function in same extension [in builtin.c]\");\n    }\n    \n    // Retain the objectbuiltinfunction in the builtin_objects table\n    builtin_bindobject(MORPHO_GETOBJECT(newfn));\n    if (out) *out = newfn;\n    \n    return true;\n    \nmorpho_addfunction_cleanup:\n    if (new) {\n        builtin_clear(new);\n        object_free((object *) new);\n    }\n    \n    return false;\n}\n\n/* **********************************************************************\n * Create and find builtin classes\n * ********************************************************************** */\n\n/** Defines a built in class\n * @param[in] name          the name of the class\n * @param[in] desc          class description; use MORPHO_GETCLASSDEFINITION(name) to obtain this\n * @param[in] superclass the class's superclass\n * @returns the class object */\nvalue builtin_addclass(char *name, builtinclassentry desc[], value superclass) {\n    value label = object_stringfromcstring(name, strlen(name));\n    builtin_bindobject(MORPHO_GETOBJECT(label));\n    objectclass *new = object_newclass(label);\n    builtin_bindobject((object *) new);\n    objectclass *superklass = NULL;\n    \n    if (!new) return MORPHO_NIL;\n    \n    /** Copy methods from superclass */\n    if (MORPHO_ISCLASS(superclass)) {\n        superklass = MORPHO_GETCLASS(superclass);\n        dictionary_copy(&superklass->methods, &new->methods);\n        new->superclass=superklass;\n    }\n    \n    for (unsigned int i=0; desc[i].name!=NULL; i++) {\n        if (desc[i].type==BUILTIN_METHOD) {\n            objectbuiltinfunction *newmethod = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION);\n            builtin_init(newmethod);\n            newmethod->function=desc[i].function;\n            newmethod->klass=new;\n            newmethod->name=object_stringfromcstring(desc[i].name, strlen(desc[i].name));\n            newmethod->flags=desc[i].flags;\n            if (desc[i].signature) {\n                signature_parse(desc[i].signature, &newmethod->sig);\n            }\n            \n            dictionary_intern(&builtin_symboltable, newmethod->name);\n            value method = MORPHO_OBJECT(newmethod);\n            \n            builtin_bindobject((object *) newmethod);\n            \n            builtin_addfunctiontodict(&new->methods, newmethod->name, method, NULL);\n        }\n    }\n    \n    if (dictionary_get(_currentclasstable, label, NULL)) {\n        UNREACHABLE(\"Redefinition of class in same extension [in builtin.c]\");\n    }\n    \n    dictionary_insert(_currentclasstable, label, MORPHO_OBJECT(new));\n    \n    return MORPHO_OBJECT(new);\n}\n\n/** Finds a builtin class from its name */\nvalue builtin_findclass(value name) {\n    value out=MORPHO_NIL;\n    dictionary_get(&builtin_classtable, name, &out);\n    return out;\n}\n\n/** Copies the built in symbol table into a new dictionary */\nvoid builtin_copysymboltable(dictionary *out) {\n    dictionary_copy(&builtin_symboltable, out);\n}\n\n/** Interns a given symbol. */\nvalue builtin_internsymbol(value symbol) {\n    return dictionary_intern(&builtin_symboltable, symbol);\n}\n\n/** Interns a symbol given as a C string. */\nvalue builtin_internsymbolascstring(char *symbol) {\n    value selector = object_stringfromcstring(symbol, strlen(symbol));\n    builtin_bindobject(MORPHO_GETOBJECT(selector));\n    value internselector = builtin_internsymbol(selector);\n    return internselector;\n}\n\n/** Checks if a symbol exists in the global symbol table */\nbool builtin_checksymbol(value symbol) {\n    value val;\n    return dictionary_get(&builtin_symboltable, symbol, &val);\n}\n\n/* **********************************************************************\n * Initialization/Finalization\n * ********************************************************************** */\n\nextern objecttypedefn objectstringdefn;\nextern objecttypedefn objectclassdefn;\n\nobjecttype objectbuiltinfunctiontype;\n\nvoid builtin_initialize(void) {\n    dictionary_init(&builtin_functiontable);\n    dictionary_init(&builtin_classtable);\n    dictionary_init(&builtin_symboltable);\n    builtin_objects=NULL;\n    \n    builtin_setfunctiontable(&builtin_functiontable);\n    builtin_setclasstable(&builtin_classtable);\n    \n    // Initialize core object types\n    objectstringtype=object_addtype(&objectstringdefn);\n    objectclasstype=object_addtype(&objectclassdefn);\n    objectbuiltinfunctiontype=object_addtype(&objectbuiltinfunctiondefn);\n    \n    /* Initialize builtin classes and functions */\n    instance_initialize(); // Must initialize first so that Object exists\n    \n    string_initialize();  // Classes\n    function_initialize();\n    metafunction_initialize();\n    class_initialize();\n    upvalue_initialize();\n    invocation_initialize();\n    dict_initialize();\n    list_initialize();\n    closure_initialize();\n    array_initialize();\n    range_initialize();\n    complex_initialize();\n    err_initialize();\n    tuple_initialize();\n    \n    float_initialize();// Veneer classes\n    int_initialize();\n    bool_initialize();\n    \n    file_initialize();\n    system_initialize();\n    json_initialize();\n    \n    // Initialize function definitions\n    functiondefs_initialize();\n    \n    // Initialize linear algebra\n#ifdef MORPHO_INCLUDE_LINALG\n    matrix_initialize();\n#endif\n    \n#ifdef MORPHO_INCLUDE_SPARSE\n    sparse_initialize();\n#endif\n    \n#ifdef MORPHO_INCLUDE_GEOMETRY\n    // Initialize geometry\n    geometry_initialize();\n#endif\n  \n    morpho_addfinalizefn(builtin_finalize);\n}\n\nvoid builtin_finalize(void) {\n    while (builtin_objects!=NULL) {\n        object *next = builtin_objects->next;\n        object_free(builtin_objects);\n        builtin_objects=next;\n    }\n    \n    dictionary_clear(&builtin_functiontable);\n    dictionary_clear(&builtin_classtable);\n    dictionary_clear(&builtin_symboltable);\n}\n"
  },
  {
    "path": "src/builtin/builtin.h",
    "content": "/** @file builtin.h\n *  @author T J Atherton\n *\n *  @brief Morpho built in functions and classes\n*/\n\n#ifndef builtin_h\n#define builtin_h\n\n#include \"object.h\"\n#include \"clss.h\"\n\n#ifndef MORPHO_CORE\n#include \"morpho.h\"\n#endif\n\n#include \"signature.h\"\n\n/* -------------------------------------------------------\n * Built in function objects\n * ------------------------------------------------------- */\n\n/** Flags that describe properties of the built in function */\ntypedef unsigned int builtinfunctionflags;\n\n#define BUILTIN_FLAGSEMPTY    0\n\n#define MORPHO_FN_FLAGSEMPTY  (0)\n#define MORPHO_FN_PUREFN      (1<<1)\n#define MORPHO_FN_CONSTRUCTOR (1<<2)\n#define MORPHO_FN_REENTRANT   (1<<3)\n#define MORPHO_FN_OPTARGS     (1<<4)\n\n/** Type of C function that implements a built in Morpho function */\ntypedef value (*builtinfunction) (vm *v, int nargs, value *args);\n\n/** Object type for built in function */\nextern objecttype objectbuiltinfunctiontype;\n#define OBJECT_BUILTINFUNCTION objectbuiltinfunctiontype\n\n/** A built in function object */\ntypedef struct  {\n    object obj;\n    value name;\n    builtinfunctionflags flags;\n    builtinfunction function;\n    objectclass *klass; \n    signature sig;\n} objectbuiltinfunction;\n\n/** Gets an objectfunction from a value */\n#define MORPHO_GETBUILTINFUNCTION(val)   ((objectbuiltinfunction *) MORPHO_GETOBJECT(val))\n\n/** Tests whether an object is a function */\n#define MORPHO_ISBUILTINFUNCTION(val) object_istype(val, OBJECT_BUILTINFUNCTION)\n\n/* -------------------------------------------------------\n * Built in classes\n * ------------------------------------------------------- */\n\n/** A type used to store the entries of a built in class */\ntypedef struct {\n    enum { BUILTIN_METHOD, BUILTIN_PROPERTY } type;\n    char *name;\n    char *signature;\n    builtinfunctionflags flags;\n    builtinfunction function;\n} builtinclassentry;\n\n/** The following macros help to define a built in class. They should be used outside of any function declaration.\n *  To use:\n *  MORPHO_BEGINCLASS(Object)  - Starts the declaration\n *  MORPHO_PROPERTY(\"test\")       - Adds a property called \"test\" to the definition\n *  MORPHO_METHOD(\"init\", object_init, BUILTIN_FLAGSEMPTY)  - Adds a method called \"init\" to the definition\n *  MORPHO_ENDCLASS                  - Ends the declaration */\n\n#define MORPHO_BEGINCLASS(name) builtinclassentry builtinclass_##name[] = {\n\n#define MORPHO_PROPERTY(label)  ((builtinclassentry) { .type=(BUILTIN_PROPERTY), .name=(label), .flags=BUILTIN_FLAGSEMPTY, .function=NULL})\n\n#define MORPHO_METHOD(label, func, flg)  ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=NULL, .flags=flg, .function=func})\n\n#define MORPHO_METHOD_SIGNATURE(label, sig, func, flg)  ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=sig, .flags=flg, .function=func})\n\n#define MORPHO_ENDCLASS         , MORPHO_PROPERTY(NULL) \\\n                                };\n\n/** Use this macro to retrieve the class definition for calling builtin_addclass */\n\n#define MORPHO_GETCLASSDEFINITION(name)    (builtinclass_##name)\n\n/** Macros and functions for built in classes */\n\n/** Get the nth argument from the args list */\n#define MORPHO_GETARG(args, n)  (args[n+1])\n\n/** This macro gets self */\n#define MORPHO_SELF(args)       (args[0])\n\n/** Raise an error and return nil */\n#define MORPHO_RAISE(v, err)  { morpho_runtimeerror(v, err ); return MORPHO_NIL; }\n#define MORPHO_RAISEVARGS(v, err, ...) \\\n                              { morpho_runtimeerror(v, err, __VA_ARGS__); \\\n                                return MORPHO_NIL; }\n\n/* -------------------------------------------------------\n * Loop functions to enumerate over enumerable objects\n * ------------------------------------------------------- */\n\n/** Type of C function that implements a built in Morpho function */\ntypedef bool (*builtin_loopfunction) (vm *v, indx i, value item, void *ref);\n\n/* -------------------------------------------------------\n * Interface\n * ------------------------------------------------------- */\n\ndictionary *builtin_getfunctiontable(void);\nvoid builtin_setfunctiontable(dictionary *dict);\n\ndictionary *builtin_getclasstable(void);\nvoid builtin_setclasstable(dictionary *dict);\n\nvalue builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags);\nvalue builtin_findfunction(value name);\n\nbool morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags, value *out);\n\nvalue builtin_addclass(char *name, builtinclassentry desc[], value superclass);\nvalue builtin_findclass(value name);\n\nvoid builtin_copysymboltable(dictionary *out);\n\nvalue builtin_internsymbol(value symbol);\nvalue builtin_internsymbolascstring(char *symbol);\nbool builtin_checksymbol(value symbol);\n\nbool builtin_options(vm *v, int nargs, value *args, int *nfixed, int noptions, ...);\nbool builtin_iscallable(value val);\n\nbool builtin_enumerateloop(vm *v, value obj, builtin_loopfunction fn, void *ref);\n\n/* -------------------------------------------------------\n * Veneer classes\n * ------------------------------------------------------- */\n\nvoid object_setveneerclass(objecttype type, value class);\nobjectclass *object_getveneerclass(objecttype type);\nbool object_veneerclasstotype(objectclass *clss, objecttype *type);\n\nvoid value_setveneerclass(value type, value class);\nobjectclass *value_getveneerclass(value type);\nobjectclass *value_veneerclassfromtype(int type);\nbool value_veneerclasstotype(objectclass *clss, int *type);\n\n/* -------------------------------------------------------\n * Initialization/finalization\n * ------------------------------------------------------- */\n\nvoid builtin_initialize(void);\nvoid builtin_finalize(void);\n\n#endif /* builtin_h */\n"
  },
  {
    "path": "src/builtin/functiondefs.c",
    "content": "/** @file functiondefs.c\n *  @author T J Atherton\n *\n *  @brief Built in function definitions\n */\n\n#include <time.h>\n#include <stdlib.h>\n#include <complex.h>\n\n#include \"functiondefs.h\"\n#include \"random.h\"\n#include \"builtin.h\"\n#include \"common.h\"\n#include \"cmplx.h\"\n\n#include \"matrix.h\"\n#include \"sparse.h\"\n\n#include \"mesh.h\"\n#include \"field.h\"\n#include \"selection.h\"\n\n#ifndef M_PI\n    #define M_PI 3.14159265358979323846\n#endif\n\n/* **********************************************************************\n * Built in functions\n * ********************************************************************** */\n\n/* ************************************\n * Math\n * *************************************/\n\n#define BUILTIN_MATH(function) \\\nvalue builtin_##function(vm *v, int nargs, value *args) { \\\n    if (nargs==1) { \\\n        value arg = MORPHO_GETARG(args, 0); \\\n        if (MORPHO_ISFLOAT(arg)) { \\\n            return MORPHO_FLOAT(function(MORPHO_GETFLOATVALUE(arg))); \\\n        } else if (MORPHO_ISINTEGER(arg)) { \\\n            return MORPHO_FLOAT(function((double) MORPHO_GETINTEGERVALUE(arg))); \\\n        } else if (MORPHO_ISCOMPLEX(arg)){\\\n            return complex_builtin##function(v,MORPHO_GETCOMPLEX(arg));\\\n        } else { \\\n            morpho_runtimeerror(v, MATH_ARGS, #function);\\\n        } \\\n    } \\\n    morpho_runtimeerror(v, MATH_NUMARGS, #function);\\\n    return MORPHO_NIL; \\\n}\n\n/** Math functions */\nBUILTIN_MATH(fabs)\nBUILTIN_MATH(exp)\nBUILTIN_MATH(log)\nBUILTIN_MATH(log10)\n\nBUILTIN_MATH(sin)\nBUILTIN_MATH(cos)\nBUILTIN_MATH(tan)\nBUILTIN_MATH(asin)\nBUILTIN_MATH(acos)\n\nBUILTIN_MATH(sinh)\nBUILTIN_MATH(cosh)\nBUILTIN_MATH(tanh)\n\nBUILTIN_MATH(floor)\nBUILTIN_MATH(ceil)\n\n#undef BUILTIN_MATH\n\n/** Boolean output function need to output morpho true or false **/\n\n#define BUILTIN_MATH_BOOL(function) \\\nvalue builtin_##function(vm *v, int nargs, value *args) { \\\n    if (nargs==1) { \\\n        value arg = MORPHO_GETARG(args, 0); \\\n        if (MORPHO_ISFLOAT(arg)) { \\\n                return MORPHO_BOOL(function(MORPHO_GETFLOATVALUE(arg))); \\\n        } else if (MORPHO_ISINTEGER(arg)) { \\\n            return MORPHO_BOOL(function((double) MORPHO_GETINTEGERVALUE(arg))); \\\n        } else if (MORPHO_ISCOMPLEX(arg)){\\\n            return complex_builtin##function(MORPHO_GETCOMPLEX(arg));\\\n        } else { \\\n            morpho_runtimeerror(v, MATH_ARGS, #function);\\\n        } \\\n    } \\\n    morpho_runtimeerror(v, MATH_NUMARGS, #function);\\\n    return MORPHO_NIL; \\\n}\n\n\nBUILTIN_MATH_BOOL(isfinite)\nBUILTIN_MATH_BOOL(isinf)\nBUILTIN_MATH_BOOL(isnan)\n\n#undef BUILTIN_MATH_BOOL\n/** The sqrt function is needs to be able to return a complex number for negative arguments */\nvalue builtin_sqrt(vm *v, int nargs, value *args) { \n    if (nargs==1) { \n        value arg = MORPHO_GETARG(args, 0); \n        if (MORPHO_ISCOMPLEX(arg)){\n            return complex_builtinsqrt(v,MORPHO_GETCOMPLEX(arg));\n        }\n        else if (MORPHO_ISNUMBER(arg)) {\n            double val;\n            if (morpho_valuetofloat(arg,&val)) {\n                if (val<0) {// need to use complex sqrt\n                    objectcomplex C = MORPHO_STATICCOMPLEX(val, 0);\n                    return complex_builtinsqrt(v, &C);\n                } else { \n                    return MORPHO_FLOAT(sqrt(val));\n                }\n            } else morpho_runtimeerror(v, MATH_ARGS, \"sqrt\"); \n        } else morpho_runtimeerror(v, MATH_ARGS, \"sqrt\"); \n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, \"sqrt\");\n    return MORPHO_NIL; \n}\n\n/** The arctan function is special; it can either take one or two arguments */\nvalue builtin_arctan(vm *v, int nargs, value *args) {\n    bool useComplex = false;\n    for (unsigned int i=0; i<nargs; i++) {\n        if (MORPHO_ISNUMBER(MORPHO_GETARG(args,i))) {\n            continue;\n        } else if (MORPHO_ISCOMPLEX(MORPHO_GETARG(args, i))) {\n            useComplex = true;\n        } else {\n            morpho_runtimeerror(v, MATH_ARGS, \"arctan\");\n            return MORPHO_NIL;\n        }\n    }\n    if (useComplex) {\n        if (nargs == 1) {\n            return complex_builtinatan(v,MORPHO_GETARG(args, 0));\n        } else if (nargs==2) {\n            // Note Morpho uses the opposite order to C!\n            return complex_builtinatan2(v,MORPHO_GETARG(args, 1),MORPHO_GETARG(args, 0));\n        }\n        morpho_runtimeerror(v, MATH_NUMARGS, \"arctan\");\n        return MORPHO_NIL;\n    } else {\n        double x[2];\n        for (unsigned int i=0; i<nargs; i++) {\n            morpho_valuetofloat(MORPHO_GETARG(args, i), x+i);\n        }\n        \n        if (nargs==1) {\n            return MORPHO_FLOAT(atan(x[0]));\n        } else if (nargs==2) {\n            return MORPHO_FLOAT(atan2(x[1], x[0])); // Note Morpho uses the opposite order to C!\n        }\n            \n        morpho_runtimeerror(v, MATH_NUMARGS, \"arctan\");\n        return MORPHO_NIL;\n    }\n}\n\nvalue builtin_real(vm *v, int nargs, value *args) {\n    if (nargs==1) { \n        value arg = MORPHO_GETARG(args, 0);\n        if (MORPHO_ISNUMBER(arg)) {\n            return arg;\n        } else if (MORPHO_ISCOMPLEX(arg)) {\n            objectcomplex *c=MORPHO_GETCOMPLEX(arg);\n            double val;\n            complex_getreal(c,&val);\n            return MORPHO_FLOAT(val);\n        }\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, \"real\");\n    return MORPHO_NIL;\n}\n\nvalue builtin_imag(vm *v, int nargs, value *args) {\n    if (nargs==1) { \n        value arg = MORPHO_GETARG(args, 0);\n        if (MORPHO_ISNUMBER(arg)) {\n            return MORPHO_FLOAT(0);\n        } else if (MORPHO_ISCOMPLEX(arg)) {\n            objectcomplex *c=MORPHO_GETCOMPLEX(arg);\n            double val;\n            complex_getimag(c,&val);\n            return MORPHO_FLOAT(val);\n        }\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, \"imag\");\n    return MORPHO_NIL;\n}\n\nvalue builtin_angle(vm *v, int nargs, value *args) {\n    if (nargs==1) { \n        value arg = MORPHO_GETARG(args, 0);\n        if (MORPHO_ISNUMBER(arg)) {\n            double val;\n            morpho_valuetofloat(arg,&val);\n            if (val>=0) {\n                return MORPHO_FLOAT(0);\n            }\n            else return MORPHO_FLOAT(M_PI);\n        } else if (MORPHO_ISCOMPLEX(arg)) {\n            objectcomplex *c=MORPHO_GETCOMPLEX(arg);\n            double val;\n            complex_angle(c,&val);\n            return MORPHO_FLOAT(val);\n        }\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, \"angle\");\n    return MORPHO_NIL;\n}\n\nvalue builtin_conj(vm *v, int nargs, value *args) {\n    if (nargs==1) { \n        value arg = MORPHO_GETARG(args, 0);\n        if (MORPHO_ISNUMBER(arg)) {\n            return arg;\n        } else if (MORPHO_ISCOMPLEX(arg)) {\n            objectcomplex *a=MORPHO_GETCOMPLEX(arg);\n            value out=MORPHO_NIL;\n            objectcomplex *new = object_newcomplex(0,0);\n            if (new) {\n                complex_conj(a, new);\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            }    \n            return out;\n        }\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, \"conj\");\n    return MORPHO_NIL;\n}\n\n/* ************************************\n * Random numbers\n * *************************************/\n\n/** Generate a random float between 0 and 1 */\nvalue builtin_random(vm *v, int nargs, value *args) {\n    return MORPHO_FLOAT(random_double());\n}\n\n/** Generate a random normally distributed number */\nvalue builtin_randomnormal(vm *v, int nargs, value *args) {\n    double x,y,r;\n\n    do {\n        x=2.0*random_double()-1.0;\n        y=2.0*random_double()-1.0;\n      \n        r=x*x+y*y;\n    } while (r>=1.0);\n    \n    return MORPHO_FLOAT(x*sqrt((-2.0*log(r))/r));\n}\n\n/** Generate a random integer with a bound.\n Efficient and unbiased algorithm from: https://www.pcg-random.org/posts/bounded-rands.html */\nvalue builtin_randomint(vm *v, int nargs, value *args) {\n    uint32_t x = random_int();\n    /* Leave quickly if no range was asked for */\n    if (nargs==0) return MORPHO_INTEGER((int) x);\n    \n    /* Otherwise, generate a number in range. */\n    int r=0;\n    if (!morpho_valuetoint(MORPHO_GETARG(args, 0), &r)||r<0) {\n        morpho_runtimeerror(v, VM_INVALIDARGSDETAIL,FUNCTION_RANDOMINT, 1, \"positive integer\");\n    }\n    \n    uint32_t range=(uint32_t) r;\n    uint64_t m = (uint64_t) x  * (uint64_t) range;\n    uint32_t l = (uint32_t) m;\n    \n    if (l < range) {\n        uint32_t t = -range;\n        if (t >= range) {\n            t -= range;\n            if (t >= range)\n                t %= range;\n        }\n        while (l < t) {\n            x = random_int();\n            m = (uint64_t) x * (uint64_t) range;\n            l = (uint32_t) m;\n        }\n    }\n    return MORPHO_INTEGER(m >> 32);\n}\n\n/* ************************************\n * Type checking and conversion\n * *************************************/\n\n/** Typecheck functions to test for the type of a quantity */\n#define BUILTIN_TYPECHECK(type, test) \\\n    value builtin_##type(vm *v, int nargs, value *args) { \\\n        if (nargs==1) { \\\n            return MORPHO_BOOL(test(MORPHO_GETARG(args, 0))); \\\n            } else morpho_runtimeerror(v, TYPE_NUMARGS, #type); \\\n        \\\n        return MORPHO_NIL; \\\n    }\n    \nBUILTIN_TYPECHECK(isnil, MORPHO_ISNIL)\nBUILTIN_TYPECHECK(isint, MORPHO_ISINTEGER)\nBUILTIN_TYPECHECK(isfloat, MORPHO_ISFLOAT)\nBUILTIN_TYPECHECK(isnumber, MORPHO_ISNUMBER)\nBUILTIN_TYPECHECK(iscomplex, MORPHO_ISCOMPLEX)\nBUILTIN_TYPECHECK(isbool, MORPHO_ISBOOL)\nBUILTIN_TYPECHECK(isobject, MORPHO_ISOBJECT)\nBUILTIN_TYPECHECK(isstring, MORPHO_ISSTRING)\nBUILTIN_TYPECHECK(isclass, MORPHO_ISCLASS)\nBUILTIN_TYPECHECK(isrange, MORPHO_ISRANGE)\nBUILTIN_TYPECHECK(isdictionary, MORPHO_ISDICTIONARY)\nBUILTIN_TYPECHECK(islist, MORPHO_ISLIST)\nBUILTIN_TYPECHECK(istuple, MORPHO_ISTUPLE)\nBUILTIN_TYPECHECK(isarray, MORPHO_ISARRAY)\n\n#ifdef MORPHO_INCLUDE_LINALG\nBUILTIN_TYPECHECK(ismatrix, MORPHO_ISMATRIX)\n#endif\n\n#ifdef MORPHO_INCLUDE_SPARSE\nBUILTIN_TYPECHECK(issparse, MORPHO_ISSPARSE)\n#endif\n\n#ifdef MORPHO_INCLUDE_GEOMETRY\nBUILTIN_TYPECHECK(ismesh, MORPHO_ISMESH)\nBUILTIN_TYPECHECK(isselection, MORPHO_ISSELECTION)\nBUILTIN_TYPECHECK(isfield, MORPHO_ISFIELD)\n#endif\n\n#undef BUILTIN_TYPECHECK\n\n/** Check if something is callable */\nvalue builtin_iscallablefunction(vm *v, int nargs, value *args) {\n    if (nargs==1) {\n        if (builtin_iscallable(MORPHO_GETARG(args, 0))) return MORPHO_TRUE;\n    } else morpho_runtimeerror(v, TYPE_NUMARGS, FUNCTION_ISCALLABLE);\n    return MORPHO_FALSE;\n}\n\n/** Convert something to an integer */\nvalue builtin_int(vm *v, int nargs, value *args) {\n    if (nargs==1) {\n        value arg = MORPHO_GETARG(args, 0);\n        \n        if (MORPHO_ISSTRING(arg)) {\n            string_tonumber(MORPHO_GETSTRING(arg), &arg);\n        }\n        \n        if (MORPHO_ISFLOAT(arg)) {\n            return MORPHO_FLOATTOINTEGER(arg);\n        } else if (MORPHO_ISINTEGER(arg)) {\n            return arg;\n        }\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, FUNCTION_INT);\n    return MORPHO_NIL;\n}\n\n/** Convert to a floating point number */\nvalue builtin_float(vm *v, int nargs, value *args) {\n    if (nargs==1) {\n        value arg = MORPHO_GETARG(args, 0);\n        \n        if (MORPHO_ISSTRING(arg)) {\n            string_tonumber(MORPHO_GETSTRING(arg), &arg);\n        }\n        \n        if (MORPHO_ISINTEGER(arg)) {\n            return MORPHO_INTEGERTOFLOAT(arg);\n        } else if (MORPHO_ISFLOAT(arg)){\n            return arg;\n        }\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, FUNCTION_FLOAT);\n    return MORPHO_NIL;\n}\n\n/** Convert to a boolean */\nvalue builtin_bool(vm *v, int nargs, value *args) {\n    if (nargs==1) {\n        return MORPHO_BOOL(MORPHO_ISTRUE(MORPHO_GETARG(args, 0)));\n    }\n    morpho_runtimeerror(v, MATH_NUMARGS, FUNCTION_BOOL);\n    return MORPHO_NIL;\n}\n\n/** Remainder */\nvalue builtin_mod(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    if (nargs==2) {\n        value a = MORPHO_GETARG(args, 0);\n        value b = MORPHO_GETARG(args, 1);\n        \n        if (MORPHO_ISINTEGER(a) && MORPHO_ISINTEGER(b)) {\n            out=MORPHO_INTEGER(MORPHO_GETINTEGERVALUE(a) % MORPHO_GETINTEGERVALUE(b));\n        } else {\n            if (MORPHO_ISINTEGER(a)) a=MORPHO_INTEGERTOFLOAT(a);\n            if (MORPHO_ISINTEGER(b)) b=MORPHO_INTEGERTOFLOAT(b);\n            \n            if (MORPHO_ISFLOAT(a) && MORPHO_ISFLOAT(b)) {\n                out=MORPHO_FLOAT(fmod(MORPHO_GETFLOATVALUE(a), MORPHO_GETFLOATVALUE(b)));\n            } else morpho_runtimeerror(v, MATH_NUMARGS, FUNCTION_INT);\n        }\n    } else morpho_runtimeerror(v, VM_INVALIDARGS, 2, nargs);\n    return out;\n}\n\n/** Find the minimum and maximum values in an enumerable object */\ntypedef struct {\n    value min;\n    value max;\n} minmaxstruct;\n\nstatic bool minmaxfn(vm *v, indx i, value val, void *ref) {\n    minmaxstruct *m=(minmaxstruct *) ref;\n    value l=m->min, r=val;\n    if (i==0 || morpho_extendedcomparevalue(l, r)<0) m->min=val;\n    \n    l=m->max; r=val;\n    if (i==0 || morpho_extendedcomparevalue(l, r)>0) m->max=val;\n    \n    return true;\n}\n\nstatic bool builtin_minmax(vm *v, value obj, value *min, value *max) {\n    minmaxstruct m;\n    // intialize the minmaxstuct\n    m.max = MORPHO_NIL;\n    m.min = MORPHO_NIL;\n\n    if (!builtin_enumerateloop(v, obj, minmaxfn, &m)) return false;\n        \n    if (min) *min = m.min;\n    if (max) *max = m.max;\n    \n    return true;\n}\n\nbool builtin_minmaxargs(vm *v, int nargs, value *args, value *min, value *max, char *fname) {\n    for (unsigned int i=0; i<nargs; i++) {\n        value arg = MORPHO_GETARG(args, i);\n        if (MORPHO_ISOBJECT(arg)) {\n            if (!builtin_minmax(v, arg, (min ? &min[i] : NULL), (max ? &max[i]: NULL))) return false;\n        } else if (morpho_isnumber(arg)) {\n            if (min) min[i]=arg;\n            if (max) max[i]=arg;\n        } else {\n            morpho_runtimeerror(v, MAX_ARGS, fname);\n            return false;\n        }\n    }\n    return true;\n}\n\n/** Find the minimum and maximum values in an enumerable object */\nstatic value builtin_bounds(vm *v, int nargs, value *args) {\n    value minlist[nargs+1],maxlist[nargs+1];\n    value out = MORPHO_NIL;\n    \n    if (builtin_minmaxargs(v, nargs, args, minlist, maxlist, FUNCTION_BOUNDS)) {\n        if (nargs>0) {\n            value bounds[2];\n            value_minmax(nargs, minlist, &bounds[0], NULL);\n            value_minmax(nargs, maxlist, NULL, &bounds[1]);\n            \n            objectlist *list = object_newlist(2, bounds);\n            if (list) {\n                out = MORPHO_OBJECT(list);\n                morpho_bindobjects(v, 1, &out);\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n            \n        } else morpho_runtimeerror(v, MAX_ARGS, FUNCTION_BOUNDS);\n    }\n    \n    return out;\n}\n\n/** Find the minimum value in an enumerable object */\nstatic value builtin_min(vm *v, int nargs, value *args) {\n    value m[nargs+1];\n    value out = MORPHO_NIL;\n    \n    if (builtin_minmaxargs(v, nargs, args, m, NULL, FUNCTION_MIN)) {\n        if (nargs>0) value_minmax(nargs, m, &out, NULL);\n        else morpho_runtimeerror(v, MAX_ARGS, FUNCTION_MIN);\n    }\n    \n    return out;\n}\n\n/** Find the maximum value in an enumerable object */\nstatic value builtin_max(vm *v, int nargs, value *args) {\n    value m[nargs+1];\n    value out = MORPHO_NIL;\n    \n    if (builtin_minmaxargs(v, nargs, args, NULL, m, FUNCTION_MAX)) {\n        if (nargs>0) value_minmax(nargs, m, NULL, &out);\n        else morpho_runtimeerror(v, MAX_ARGS, FUNCTION_MAX);\n    }\n    \n    return out;\n}\n\n/** find the sign of a number */\nstatic value builtin_sign(vm *v, int nargs, value *args){\n    if (nargs==1) { \n        value arg = MORPHO_GETARG(args, 0); \n        if (MORPHO_ISFLOAT(arg)) { \n\n            if (MORPHO_GETFLOATVALUE(arg)>0) {\n                return MORPHO_FLOAT(1); \n            }\n            else if (MORPHO_GETFLOATVALUE(arg)<0){\n                return MORPHO_FLOAT(-1); \n            }\n            else return MORPHO_FLOAT(0);\n\n        } else if (MORPHO_ISINTEGER(arg)) { \n            if (MORPHO_GETINTEGERVALUE(arg)>0) {\n                return MORPHO_FLOAT(1); \n            }\n            else if (MORPHO_GETINTEGERVALUE(arg)<0){\n                return MORPHO_FLOAT(-1); \n            }\n            else return MORPHO_FLOAT(0);\n        } else { \n            morpho_runtimeerror(v, MATH_ARGS,FUNCTION_SIGN);\n        } \n    } \n    morpho_runtimeerror(v, MATH_NUMARGS,FUNCTION_SIGN);\n    return MORPHO_NIL; \n}\n\n/* ************************************\n * Apply\n * *************************************/\n\n/** Apply a function to a list of arguments */\nvalue builtin_apply(vm *v, int nargs, value *args) {\n    value ret = MORPHO_NIL;\n    \n    if (nargs<2) morpho_runtimeerror(v, APPLY_ARGS);\n    \n    value fn =  MORPHO_GETARG(args, 0);\n    value x =  MORPHO_GETARG(args, 1);\n    \n    if (!morpho_iscallable(fn)) {\n        morpho_runtimeerror(v, APPLY_NOTCALLABLE);\n        return MORPHO_NIL;\n    }\n    \n    if (nargs==2 && MORPHO_ISTUPLE(x)) {\n        objecttuple *t = MORPHO_GETTUPLE(x);\n        \n        morpho_call(v, fn, t->length, t->tuple, &ret);\n    } else if (nargs==2 && MORPHO_ISLIST(x)) {\n        objectlist *lst = MORPHO_GETLIST(x);\n        \n        morpho_call(v, fn, lst->val.count, lst->val.data, &ret);\n    } else {\n        morpho_call(v, fn, nargs-1, &MORPHO_GETARG(args, 1), &ret);\n    }\n    \n    return ret;\n}\n\n/* ************************************\n * System\n * *************************************/\n\n/** Call the operating system */\nvalue builtin_system(vm *v, int nargs, value *args) {\n    if (nargs==1) {\n        value arg=MORPHO_GETARG(args, 0);\n        if (MORPHO_ISSTRING(arg)) {\n            return MORPHO_INTEGER(system(MORPHO_GETCSTRING(arg)));\n        }\n    }\n    return MORPHO_NIL;\n}\n\n/** Clock */\nvalue builtin_clock(vm *v, int nargs, value *args) {\n    clock_t time;\n    time = clock();\n    return MORPHO_FLOAT( ((double) time)/((double) CLOCKS_PER_SEC) );\n}\n\n#define BUILTIN_MATH(function) \\\n    builtin_addfunction(#function, builtin_##function, BUILTIN_FLAGSEMPTY);\n\n#define BUILTIN_MATH_BOOL(function) \\\n    builtin_addfunction(#function, builtin_##function, BUILTIN_FLAGSEMPTY);\n\n#define BUILTIN_TYPECHECK(function) \\\n    builtin_addfunction(#function, builtin_##function, BUILTIN_FLAGSEMPTY);\n\nvoid functiondefs_initialize(void) {\n    builtin_addfunction(FUNCTION_CLOCK, builtin_clock, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_RANDOM, builtin_random, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_RANDOMINT, builtin_randomint, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_RANDOMNORMAL, builtin_randomnormal, BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_SYSTEM, builtin_system, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_ARCTAN, builtin_arctan, BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_ABS, builtin_fabs, BUILTIN_FLAGSEMPTY);\n    \n    BUILTIN_MATH(exp)\n    BUILTIN_MATH(log)\n    BUILTIN_MATH(log10)\n\n    BUILTIN_MATH(sin)\n    BUILTIN_MATH(cos)\n    BUILTIN_MATH(tan)\n    BUILTIN_MATH(asin)\n    BUILTIN_MATH(acos)\n\n    BUILTIN_MATH(sinh)\n    BUILTIN_MATH(cosh)\n    BUILTIN_MATH(tanh)\n    BUILTIN_MATH(sqrt)\n\n    BUILTIN_MATH(floor)\n    BUILTIN_MATH(ceil)\n\n    BUILTIN_MATH_BOOL(isfinite)\n    BUILTIN_MATH_BOOL(isinf)\n    BUILTIN_MATH_BOOL(isnan)\n    \n    BUILTIN_TYPECHECK(isnil)\n    BUILTIN_TYPECHECK(isint)\n    BUILTIN_TYPECHECK(isfloat)\n    BUILTIN_TYPECHECK(isnumber)\n    BUILTIN_TYPECHECK(isbool)\n    BUILTIN_TYPECHECK(isobject)\n    BUILTIN_TYPECHECK(isstring)\n    BUILTIN_TYPECHECK(isclass)\n    BUILTIN_TYPECHECK(isrange)\n    BUILTIN_TYPECHECK(isdictionary)\n    BUILTIN_TYPECHECK(islist)\n    BUILTIN_TYPECHECK(istuple)\n    BUILTIN_TYPECHECK(isarray)\n    \n#ifdef MORPHO_INCLUDE_LINALG\n    BUILTIN_TYPECHECK(ismatrix)\n#endif\n    \n#ifdef MORPHO_INCLUDE_SPARSE\n    BUILTIN_TYPECHECK(issparse)\n#endif\n    \n#ifdef MORPHO_INCLUDE_GEOMETRY\n    BUILTIN_TYPECHECK(ismesh)\n    BUILTIN_TYPECHECK(isselection)\n    BUILTIN_TYPECHECK(isfield)\n#endif\n\n    builtin_addfunction(FUNCTION_REAL,builtin_real,BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_IMAG,builtin_imag,BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_ANGLE,builtin_angle,BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_CONJ,builtin_conj,BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_ISCALLABLE, builtin_iscallablefunction, BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_INT, builtin_int, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_FLOAT, builtin_float, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_BOOL, builtin_bool, BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_MOD, builtin_mod, BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_BOUNDS, builtin_bounds, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_MIN, builtin_min, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(FUNCTION_MAX, builtin_max, BUILTIN_FLAGSEMPTY);\n    \n    builtin_addfunction(FUNCTION_SIGN, builtin_sign, BUILTIN_FLAGSEMPTY);\n\n    builtin_addfunction(FUNCTION_APPLY, builtin_apply, BUILTIN_FLAGSEMPTY);\n    \n    morpho_defineerror(MATH_ARGS, ERROR_HALT, MATH_ARGS_MSG);\n    morpho_defineerror(MATH_NUMARGS, ERROR_HALT, MATH_NUMARGS_MSG);\n    morpho_defineerror(MATH_ATANARGS, ERROR_HALT, MATH_ATANARGS_MSG);\n    morpho_defineerror(TYPE_NUMARGS, ERROR_HALT, TYPE_NUMARGS_MSG);\n    morpho_defineerror(MAX_ARGS, ERROR_HALT, MAX_ARGS_MSG);\n    morpho_defineerror(APPLY_ARGS, ERROR_HALT, APPLY_ARGS_MSG);\n    morpho_defineerror(APPLY_NOTCALLABLE, ERROR_HALT, APPLY_NOTCALLABLE_MSG);\n}\n\n#undef BUILTIN_MATH\n#undef BUILTIN_MATH_BOOL\n"
  },
  {
    "path": "src/builtin/functiondefs.h",
    "content": "/** @file functiondefs.h\n *  @author T J Atherton\n *\n *  @brief Built in function definitions\n */\n\n#ifndef functiondefs_h\n#define functiondefs_h\n\n#include <stdio.h>\n\n/* -------------------------------------------------------\n * Built in function labels\n * ------------------------------------------------------- */\n\n#define FUNCTION_RANDOM        \"random\"\n#define FUNCTION_RANDOMINT     \"randomint\"\n#define FUNCTION_RANDOMNORMAL  \"randomnormal\"\n#define FUNCTION_CLOCK         \"clock\"\n#define FUNCTION_SYSTEM        \"system\"\n\n#define FUNCTION_INT           \"Int\"\n#define FUNCTION_FLOAT         \"Float\"\n#define FUNCTION_BOOL          \"Bool\"\n#define FUNCTION_MOD           \"mod\"\n#define FUNCTION_ABS           \"abs\"\n#define FUNCTION_ISCALLABLE    \"iscallable\"\n#define FUNCTION_MIN           \"min\"\n#define FUNCTION_MAX           \"max\"\n#define FUNCTION_BOUNDS        \"bounds\"\n\n#define FUNCTION_REAL           \"real\"\n#define FUNCTION_IMAG           \"imag\"\n#define FUNCTION_ANGLE          \"angle\"\n#define FUNCTION_CONJ           \"conj\"\n\n#define FUNCTION_SIGN           \"sign\"\n\n#define FUNCTION_APPLY         \"apply\"\n\n#define FUNCTION_ARCTAN        \"arctan\"\n\n/* -------------------------------------------------------\n * Errors thrown by builtin functions\n * ------------------------------------------------------- */\n\n#define MATH_ARGS                    \"ExpctNmArgs\"\n#define MATH_ARGS_MSG                \"Function '%s' expects numerical arguments.\"\n\n#define MATH_NUMARGS                 \"ExpctArgNm\"\n#define MATH_NUMARGS_MSG             \"Function '%s' expects 1 numerical argument.\"\n\n#define MATH_ATANARGS                \"AtanArgNm\"\n#define MATH_ATANARGS_MSG            \"Function 'arctan' expects either 1 or 2 numerical arguments.\"\n\n#define TYPE_NUMARGS                 \"TypArgNm\"\n#define TYPE_NUMARGS_MSG             \"Function '%s' expects one argument.\"\n\n#define MAX_ARGS                     \"MnMxArgs\"\n#define MAX_ARGS_MSG                 \"Function '%s' expects at least one numerical argument, list or matrix.\"\n\n#define APPLY_ARGS                   \"ApplyArgs\"\n#define APPLY_ARGS_MSG               \"Function 'apply' expects at least two arguments.\"\n\n#define APPLY_NOTCALLABLE            \"ApplyNtCllble\"\n#define APPLY_NOTCALLABLE_MSG        \"Function 'apply' requires a callable object as its first argument.\"\n\n/* -------------------------------------------------------\n * Interface to define builtin functions\n * ------------------------------------------------------- */\n\nvoid functiondefs_initialize(void);\n\n#endif /* functions_h */\n"
  },
  {
    "path": "src/classes/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        classes.h\n        array.c        array.h\n        bool.c         bool.h \n        closure.c      closure.h\n        clss.c         clss.h\n        cmplx.c        cmplx.h\n        dict.c         dict.h\n        err.c          err.h\n        file.c         file.h\n        flt.c          flt.h\n        function.c     function.h\n        instance.c     instance.h\n        int.c          int.h\n        invocation.c   invocation.h\n        json.c         json.h\n        list.c         list.h\n        metafunction.c metafunction.h\n        range.c        range.h\n        strng.c        strng.h\n        system.c       system.h\n        tuple.c        tuple.h\n        upvalue.c      upvalue.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        classes.h\n        array.h\n        closure.h\n        clss.h\n        cmplx.h\n        dict.h\n        err.h\n        file.h\n        floatveneer.h\n        function.h\n        instance.h\n        invocation.h\n        json.h\n        list.h\n        metafunction.h\n        range.h\n        strng.h\n        system.h\n        upvalue.h\n)\n"
  },
  {
    "path": "src/classes/array.c",
    "content": "/** @file array.c\n *  @author T J Atherton\n *\n *  @brief Defines array object type and Array class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * Array objects\n * ********************************************************************** */\n\n/** Array object definitions */\nvoid objectarray_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Array>\");\n}\n\nvoid objectarray_markfn(object *obj, void *v) {\n    objectarray *c = (objectarray *) obj;\n    for (unsigned int i=0; i<c->nelements; i++) {\n        morpho_markvalue(v, c->values[i]);\n    }\n}\n\nsize_t objectarray_sizefn(object *obj) {\n    return sizeof(objectarray) +\n        sizeof(value) * ( ((objectarray *) obj)->nelements+2*((objectarray *) obj)->ndim );\n}\n\nobjecttypedefn objectarraydefn = {\n    .printfn=objectarray_printfn,\n    .markfn=objectarray_markfn,\n    .freefn=NULL,\n    .sizefn=objectarray_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Initializes an array given the size */\nvoid object_arrayinit(objectarray *array, unsigned int ndim, unsigned int *dim) {\n    object_init((object *) array, OBJECT_ARRAY);\n    unsigned int nel = (ndim==0 ? 0 : 1);\n\n    /* Store pointers into the data array */\n    array->dimensions=array->data;\n    array->multipliers=array->data+ndim;\n    array->values=array->data+2*ndim;\n\n    /* Store the description of array dimensions */\n    array->ndim=ndim;\n    for (unsigned int i=0; i<ndim; i++) {\n        array->dimensions[i]=MORPHO_INTEGER(dim[i]);\n        array->multipliers[i]=MORPHO_INTEGER(nel);\n        nel*=dim[i];\n    }\n\n    /* Store the size of the object for convenient access */\n    array->nelements=nel;\n\n    /* Arrays are initialized to nil. */\n#ifdef MORPHO_NAN_BOXING\n    memset(array->values, 0, sizeof(value)*nel);\n#else\n    for (unsigned int i=0; i<nel; i++) array->values[i]=MORPHO_FLOAT(0.0);\n#endif\n}\n\n/** @brief Creates an array object\n * @details Arrays are stored in memory as follows:\n *          objectarray structure with flexible array member value\n *          value [0..dim-1] the dimensions of the array\n *          value [dim..2*dim-1] stores multipliers for each dimension to translate to the index\n *          value [2*dim..] array elements in column major order, i.e. the matrix\n *          [ [ 1, 2],\n *           [ 3, 4] ] is stored as:\n *          <structure> // the structure\n *          2, 2, // the dimensions\n *          1, 2, // multipliers for each index to access elements\n *          1, 3, 2, 4 // the elements in column major order */\nobjectarray *object_newarray(unsigned int ndim, unsigned int *dim) {\n    /* Calculate the number of elements */\n    unsigned int nel=(ndim==0 ? 0 : dim[0]);\n    for (unsigned int i=1; i<ndim; i++) nel*=dim[i];\n\n    size_t size = sizeof(objectarray)+sizeof(value)*(2*ndim + nel);\n\n    objectarray *new = (objectarray *) object_new(size, OBJECT_ARRAY);\n    if (new) object_arrayinit(new, ndim, dim);\n\n    return new;\n}\n\n/* **********************************************************************\n * Array utility functions\n * ********************************************************************** */\n\n/** Converts a list of values to a list of integers */\nbool array_valuelisttoindices(unsigned int ndim, value *in, unsigned int *out) {\n\n    for (unsigned int i=0; i<ndim; i++) {\n        if (MORPHO_ISINTEGER(in[i])) out[i]=MORPHO_GETINTEGERVALUE(in[i]);\n        else if(MORPHO_ISFLOAT(in[i])) out[i]=round(MORPHO_GETFLOATVALUE(in[i]));\n        else return false;\n    }\n\n    return true;\n}\n\n/** Creates a new 1D array from a list of values */\nobjectarray *object_arrayfromvaluelist(unsigned int n, value *v) {\n    objectarray *new = object_newarray(1, &n);\n\n    if (new) memcpy(new->values, v, sizeof(value)*n);\n\n    return new;\n}\n\n/** Creates a new 1D array from a list of varray_value */\nobjectarray *object_arrayfromvarrayvalue(varray_value *v) {\n    return object_arrayfromvaluelist(v->count, v->data);\n}\n\n/** Creates a new array object with the dimensions given as a list of values */\nobjectarray *object_arrayfromvalueindices(unsigned int ndim, value *dim) {\n    unsigned int indx[ndim];\n    if (array_valuelisttoindices(ndim, dim, indx)) {\n        return object_newarray(ndim, indx);\n    }\n    return NULL;\n}\n\n/** Clones an array. Does *not* clone the contents. */\nobjectarray *object_clonearray(objectarray *array) {\n    objectarray *new = object_arrayfromvalueindices(array->ndim, array->data);\n\n    if (new) memcpy(new->data, array->data, sizeof(value)*(array->nelements+2*array->ndim));\n\n    return new;\n}\n\n/** Recursively print a slice of an array */\nbool array_print_recurse(vm *v, objectarray *a, unsigned int *indx, unsigned int dim, varray_char *out) {\n    unsigned int bnd = MORPHO_GETINTEGERVALUE(a->dimensions[dim]);\n    value val=MORPHO_NIL;\n\n    varray_charadd(out, \"[ \", 2);\n    for (indx[dim]=0; indx[dim]<bnd; indx[dim]++) {\n        if (dim==a->ndim-1) { // Print if innermost element\n            if (array_getelement(a, a->ndim, indx, &val)==ARRAY_OK) {\n                morpho_printtobuffer(v, val, out);\n            } else return false;\n        } else if (!array_print_recurse(v, a, indx, dim+1, out)) return false; // Otherwise recurse\n\n        if (indx[dim]<bnd-1) { // Separators between items\n            varray_charadd(out, \", \", 2);\n        }\n    }\n    varray_charadd(out, \" ]\", 2);\n\n    return true;\n}\n\n/* Print the contents of an array */\nvoid array_print(vm *v, objectarray *a) {\n    varray_char out;\n    varray_charinit(&out);\n\n    unsigned int indx[a->ndim];\n    if (array_print_recurse(v, a, indx, 0, &out)) {\n        varray_charwrite(&out, '\\0'); // Ensure zero terminated\n        morpho_printf(v, \"%s\", out.data);\n    }\n\n    varray_charclear(&out);\n}\n\n/** Converts an array error into an error code */\nerrorid array_error(objectarrayerror err) {\n    switch (err) {\n        case ARRAY_OUTOFBOUNDS: return VM_OUTOFBOUNDS;\n        case ARRAY_WRONGDIM: return VM_ARRAYWRONGDIM;\n        case ARRAY_NONINTINDX: return VM_NONNUMINDX;\n        case ARRAY_ALLOC_FAILED: return ERROR_ALLOCATIONFAILED;\n        case ARRAY_OK: UNREACHABLE(\"array_error called incorrectly.\");\n    }\n    UNREACHABLE(\"Unhandled array error.\");\n    return VM_OUTOFBOUNDS;\n}\n\n/** Converts an array error into an matrix error code for use in slices*/\nerrorid array_to_matrix_error(objectarrayerror err) {\n#ifdef MORPHO_INCLUDE_LINALG\n    switch (err) {\n        case ARRAY_OUTOFBOUNDS: return MATRIX_INDICESOUTSIDEBOUNDS;\n        case ARRAY_WRONGDIM: return MATRIX_INVLDNUMINDICES;\n        case ARRAY_NONINTINDX: return MATRIX_INVLDINDICES;\n        case ARRAY_ALLOC_FAILED: return ERROR_ALLOCATIONFAILED;\n        case ARRAY_OK: UNREACHABLE(\"array_to_matrix_error called incorrectly.\");\n    }\n    UNREACHABLE(\"Unhandled array error.\");\n    return VM_OUTOFBOUNDS;\n#else\n    return array_error(err);\n#endif\n}\n\n/** Converts an array error into an list error code for use in slices*/\nerrorid array_to_list_error(objectarrayerror err) {\n    switch (err) {\n        case ARRAY_OUTOFBOUNDS: return VM_OUTOFBOUNDS;\n        case ARRAY_WRONGDIM: return LIST_NUMARGS;\n        case ARRAY_NONINTINDX: return LIST_ARGS;\n        case ARRAY_ALLOC_FAILED: return ERROR_ALLOCATIONFAILED;\n        case ARRAY_OK: UNREACHABLE(\"array_to_list_error called incorrectly.\");\n    }\n    UNREACHABLE(\"Unhandled array error.\");\n    return VM_OUTOFBOUNDS;\n}\n\n/** Gets an array element */\nobjectarrayerror array_getelement(objectarray *a, unsigned int ndim, unsigned int *indx, value *out) {\n    unsigned int k=0;\n\n    if (ndim!=a->ndim) return ARRAY_WRONGDIM;\n\n    for (unsigned int i=0; i<ndim; i++) {\n        if (indx[i]>=MORPHO_GETINTEGERVALUE(a->dimensions[i])) return ARRAY_OUTOFBOUNDS;\n        k+=indx[i]*MORPHO_GETINTEGERVALUE(a->multipliers[i]);\n    }\n\n    *out = a->values[k];\n    return ARRAY_OK;\n}\n\n/** Creates a slice from a slicable object A.\n * @param[in] a - the sliceable object (array, list, matrix, etc..).\n * @param[in] dimFcn - a function that checks if the number of indecies is compatabile with the slicable object.\n * @param[in] constuctor - a function that create the a new object of the type of a.\n * @param[in] copy - a function that can copy information from a to out.\n * @param[in] ndim - the number of dimensions being indexed.\n * @param[in] slices - a set of indices that can be lists ranges or ints.\n * @param[out] out - returns the requested slice of a.\n*/\nobjectarrayerror getslice(value *a, bool dimFcn(value *, unsigned int),\n                          void constructor(unsigned int *, unsigned int,value *),\n                          objectarrayerror copy(value * ,value *, unsigned int, unsigned int *, unsigned int *),\n                          unsigned int ndim, value *slices, value *out) {\n    //dimension checking\n    if (!(*dimFcn) (a,ndim)) return ARRAY_WRONGDIM;\n\n    unsigned int slicesize[ndim];\n    for (unsigned int i=0; i<ndim; i++) {\n        if (MORPHO_ISINTEGER(slices[i])||MORPHO_ISFLOAT(slices[i])) {// if this is an number\n            slicesize[i] = 1; // it only has one element\n        } else if (MORPHO_ISLIST(slices[i])) { // if this is a list\n            objectlist * s = MORPHO_GETLIST(slices[i]);\n            slicesize[i] = s->val.count; // get the number of elements\n        } else if (MORPHO_ISRANGE(slices[i])) { //if its a range\n            objectrange * s = MORPHO_GETRANGE(slices[i]);\n            slicesize[i] = range_count(s);\n        } else return ARRAY_NONINTINDX; // by returning array a VM_NONNUMIDX will be thrown\n    }\n\n    // initialize out with the right size\n    (constructor) (slicesize, ndim, out);\n    \n    if (!MORPHO_ISOBJECT(*out)) return ARRAY_ALLOC_FAILED;\n\n    // fill it out recurivly\n    unsigned int indx[ndim];\n    unsigned int newindx[ndim];\n    objectarrayerror err = setslicerecursive(a, out, copy, ndim, 0, indx, newindx, slices);\n    \n    if (err!=ARRAY_OK) { // Free allocated object if an error has occurred\n        morpho_freeobject(*out);\n        *out = MORPHO_NIL;\n    }\n    \n    return err;\n}\n\n/** Iterates though the a ndim number of provided slices recursivly and copies the data from a to out.\n * @param[in] a - the sliceable object (array, list, matrix, etc..).\n * @param[out] out - returns the requeted slice of a.\n * @param[in] copy - a function that can copy information from a to out.\n * @param[in] ndim - the total number of dimentions being indexed.\n * @param[in] curdim - the current dimention being indexed.\n * @param[in] indx - an ndim list of indices that builds up to a locataion in a to copy data from.\n * @param[in] newindx - the place in out to put the data copied from a\n * @param[in] slices - a set of indices that can be lists ranges or ints.\n*/\nobjectarrayerror setslicerecursive(value* a, value* out,objectarrayerror copy(value * ,value *, unsigned int, unsigned int *,unsigned int *),\\\n                                   unsigned int ndim, unsigned int curdim, unsigned int *indx,unsigned int *newindx, value *slices){\n    // this gets given an array and out and a list of slices,\n    // we resolve the top slice to a number and add it to a list\n    objectarrayerror arrayerr;\n\n    if (curdim == ndim) { // we've resolved all the indices we can now use the list\n        arrayerr = (*copy)(a,out,ndim,indx,newindx);\n        if (arrayerr!=ARRAY_OK) return arrayerr;\n    } else { // we need to iterate though the current object\n        if (MORPHO_ISINTEGER(slices[curdim])) {\n            indx[curdim] = MORPHO_GETINTEGERVALUE(slices[curdim]);\n            newindx[curdim] = 0;\n\n            arrayerr = setslicerecursive(a, out, copy, ndim, curdim+1, indx, newindx, slices);\n            if (arrayerr!=ARRAY_OK) return arrayerr;\n\n        } else if (MORPHO_ISLIST(slices[curdim])) { // if this is a list\n\n            objectlist * s = MORPHO_GETLIST(slices[curdim]);\n            for (unsigned int  i = 0; i<s->val.count; i++ ){ // iterate through the list\n                if (MORPHO_ISINTEGER(s->val.data[i])) {\n                    indx[curdim] = MORPHO_GETINTEGERVALUE(s->val.data[i]);\n                    newindx[curdim] = i;\n                } else return ARRAY_NONINTINDX;\n\n                arrayerr = setslicerecursive(a, out, copy, ndim, curdim+1, indx, newindx, slices);\n                if (arrayerr!=ARRAY_OK) return arrayerr;\n\n            }\n        } else if (MORPHO_ISRANGE(slices[curdim])) { //if its a range\n            objectrange * s = MORPHO_GETRANGE(slices[curdim]);\n            value rangeValue;\n            for (unsigned int  i = 0; i<range_count(s); i++) { // iterate though the range\n                rangeValue=range_iterate(s,i);\n                if (MORPHO_ISINTEGER(rangeValue)) {\n                    indx[curdim] = MORPHO_GETINTEGERVALUE(rangeValue);\n                    newindx[curdim] = i;\n                } else return ARRAY_NONINTINDX;\n                arrayerr = setslicerecursive(a, out, copy, ndim, curdim+1, indx, newindx, slices);\n                if (arrayerr!=ARRAY_OK) return arrayerr;\n            }\n        } else return ARRAY_NONINTINDX;\n            //if (!(*dimFcn)(a,ndim)) return ARRAY_WRONGDIM;\n\n    }\n    return ARRAY_OK;\n}\n\n/** Sets an array element */\nobjectarrayerror array_setelement(objectarray *a, unsigned int ndim, unsigned int *indx, value in) {\n    unsigned int k=0;\n\n    if (ndim!=a->ndim) return ARRAY_WRONGDIM;\n\n    for (unsigned int i=0; i<ndim; i++) {\n        if (indx[i]>=MORPHO_GETINTEGERVALUE(a->dimensions[i])) return ARRAY_OUTOFBOUNDS;\n        k+=indx[i]*MORPHO_GETINTEGERVALUE(a->multipliers[i]);\n    }\n\n    a->values[k]=in;\n    return ARRAY_OK;\n}\n\n/* ---------------------------\n * Array constructor functions\n * --------------------------- */\n\n/** Returns the maximum nesting depth in a list, including this one.\n * @param[in] list - the list to examine\n * @param[out] out - optionally return the dimensions of the nested lists.\n * To get dimension information:\n * Call list_nestingdepth with out set to NULL; this returns the size of the array needed.\n * Initialize the dimension array to zero.\n * Call list_nestingdepth again with out set to an output array */\nunsigned int list_nestingdepth(objectlist *list, unsigned int *out) {\n    unsigned int dim=0;\n    for (unsigned int i=0; i<list->val.count; i++) {\n        if (MORPHO_ISLIST(list->val.data[i])) {\n            unsigned int sdim=list_nestingdepth(MORPHO_GETLIST(list->val.data[i]), ( out ? out+1 : NULL));\n            if (sdim>dim) dim=sdim;\n        }\n    }\n    if (out && list->val.count>*out) *out=list->val.count;\n    return dim+1;\n}\n\n/* Internal function that recursively copied a nested list into an array.\n   Use public interface array_copyfromnestedlist */\nstatic void array_copyfromnestedlistrecurse(objectlist *list, unsigned int ndim, unsigned int *indx, unsigned int depth, objectarray *out) {\n    for (unsigned int i=0; i<list->val.count; i++) {\n        indx[depth] = i;\n        value val = list->val.data[i];\n        if (MORPHO_ISLIST(val)) array_copyfromnestedlistrecurse(MORPHO_GETLIST(val), ndim, indx, depth+1, out);\n        else array_setelement(out, ndim, indx, val);\n    }\n}\n\n/** Copies a nested list into an array.*/\nvoid array_copyfromnestedlist(objectlist *in, objectarray *out) {\n    unsigned int indx[out->ndim];\n    for (unsigned int i=0; i<out->ndim; i++) indx[i]=0;\n    array_copyfromnestedlistrecurse(in, out->ndim, indx, 0, out);\n}\n\n/** Constructs an array from a list initializer or returns NULL if the initializer isn't compatible with the requested array */\nobjectarray *array_constructfromlist(unsigned int ndim, unsigned int *dim, objectlist *initializer) {\n    // Establish the dimensions of the nested list\n    unsigned int nldim = list_nestingdepth(initializer, NULL);\n    unsigned int ldim[nldim];\n    for (unsigned int i=0; i<nldim; i++) ldim[i]=0;\n    list_nestingdepth(initializer, ldim);\n\n    if (ndim>0) { // Check compatibility\n        if (ndim!=nldim) return NULL;\n        for (unsigned int i=0; i<ndim; i++) if (ldim[i]!=dim[i]) return NULL;\n    }\n\n    objectarray *new = object_newarray(nldim, ldim);\n    array_copyfromnestedlist(initializer, new);\n\n    return new;\n}\n\n/** Constructs an array from an initializer or returns NULL if the initializer isn't compatible with the requested array */\nobjectarray *array_constructfromarray(unsigned int ndim, unsigned int *dim, objectarray *initializer) {\n    if (ndim>0) { // Check compatibility\n        if (ndim!=initializer->ndim) return NULL;\n        for (unsigned int i=0; i<ndim; i++) {\n            if (dim[i]!=MORPHO_GETINTEGERVALUE(initializer->dimensions[i])) return NULL;\n        }\n    }\n\n    return object_clonearray(initializer);\n}\n\n/** Array constructor function */\nvalue array_constructor(vm *v, int nargs, value *args) {\n    unsigned int ndim; // Number of dimensions\n    unsigned int dim[nargs+1]; // Size of each dimension\n    value initializer=MORPHO_NIL; // An initializer if provided\n\n    // Check that args are present\n    if (nargs==0) { morpho_runtimeerror(v, ARRAY_ARGS); return MORPHO_NIL; }\n\n    for (ndim=0; ndim<nargs; ndim++) { // Loop over arguments\n        if (!MORPHO_ISNUMBER(MORPHO_GETARG(args, ndim))) break; // Stop once a non-numerical argument is encountered\n    }\n\n    // Get dimensions\n    if (ndim>0) array_valuelisttoindices(ndim, &MORPHO_GETARG(args, 0), dim);\n    // Initializer is the first non-numerical argument; anything after is ignored\n    if (ndim<nargs) initializer=MORPHO_GETARG(args, ndim);\n\n    objectarray *new=NULL;\n\n    // Now construct the array\n    if (MORPHO_ISNIL(initializer)) {\n        new = object_newarray(ndim, dim);\n    } else if (MORPHO_ISARRAY(initializer)) {\n        new = array_constructfromarray(ndim, dim, MORPHO_GETARRAY(initializer));\n        if (!new) morpho_runtimeerror(v, ARRAY_CMPT);\n    } else if (MORPHO_ISLIST(initializer)) {\n        new = array_constructfromlist(ndim, dim, MORPHO_GETLIST(initializer));\n        if (!new) morpho_runtimeerror(v, ARRAY_CMPT);\n    } else {\n        morpho_runtimeerror(v, ARRAY_ARGS);\n    }\n\n    // Bind the new array to the VM\n    value out=MORPHO_NIL;\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\n/** Checks that an array is being indexed with the correct number of indices with a generic interface */\nbool array_slicedim(value * a, unsigned int ndim){\n    objectarray * array= MORPHO_GETARRAY(*a);\n    if (ndim>array->ndim) return false;\n    return true;\n}\n\n/** Constructsan array is with a generic interface */\nvoid array_sliceconstructor(unsigned int *slicesize,unsigned int ndim,value* out){\n    *out = MORPHO_OBJECT(object_newarray(ndim,slicesize));\n}\n\n/** Copies data from array a to array out with a generic interface */\nobjectarrayerror array_slicecopy(value * a,value * out, unsigned int ndim, unsigned int *indx,unsigned int *newindx){\n    value data;\n    objectarrayerror arrayerr;\n    arrayerr = array_getelement(MORPHO_GETARRAY(*a),ndim,indx,&data); // read the data\n    if (arrayerr!=ARRAY_OK) return arrayerr;\n\n    arrayerr=array_setelement(MORPHO_GETARRAY(*out), ndim, newindx, data); // write the data\n    return arrayerr;\n\n}\n\n/* **********************************************************************\n * Array class\n * ********************************************************************** */\n\n/** Gets the array element with given indices */\nvalue Array_getindex(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectarray *array=MORPHO_GETARRAY(MORPHO_SELF(args));\n    unsigned int indx[nargs];\n\n    if (array_valuelisttoindices(nargs, &MORPHO_GETARG(args, 0), indx)) {\n        objectarrayerror err=array_getelement(array, nargs, indx, &out);\n        if (err!=ARRAY_OK) MORPHO_RAISE(v, array_error(err) );\n\n    } else {\n        // these aren't simple indices, lets try to make a slice\n        objectarrayerror err = getslice(&MORPHO_SELF(args),&array_slicedim,&array_sliceconstructor,&array_slicecopy,nargs,&MORPHO_GETARG(args, 0),&out);\n        if (err!=ARRAY_OK) MORPHO_RAISE(v, array_error(err) );\n        if (!MORPHO_ISNIL(out)){\n            morpho_bindobjects(v,1,&out);\n        } else MORPHO_RAISE(v, VM_NONNUMINDX);\n    }\n\n    return out;\n}\n\n/** Sets the matrix element with given indices */\nvalue Array_setindex(vm *v, int nargs, value *args) {\n    objectarray *array=MORPHO_GETARRAY(MORPHO_SELF(args));\n    unsigned int indx[nargs-1];\n\n    if (array_valuelisttoindices(nargs-1, &MORPHO_GETARG(args, 0), indx)) {\n        objectarrayerror err=array_setelement(array, nargs-1, indx, MORPHO_GETARG(args, nargs-1));\n        if (err!=ARRAY_OK) MORPHO_RAISE(v, array_error(err) );\n    } else MORPHO_RAISE(v, VM_NONNUMINDX);\n\n    return MORPHO_NIL;\n}\n\n/** Print an array */\nvalue Array_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISARRAY(self)) return Object_print(v, nargs, args);\n    \n    array_print(v, MORPHO_GETARRAY(self));\n\n    return MORPHO_NIL;\n}\n\n/** Find an array's size */\nvalue Array_count(vm *v, int nargs, value *args) {\n    objectarray *slf = MORPHO_GETARRAY(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(slf->nelements);\n}\n\n/** Array dimensions */\nvalue Array_dimensions(vm *v, int nargs, value *args) {\n    objectarray *a=MORPHO_GETARRAY(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    objectlist *new=object_newlist(a->ndim, a->data);\n\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n\n    return out;\n}\n\n/** Enumerate members of an array */\nvalue Array_enumerate(vm *v, int nargs, value *args) {\n    objectarray *slf = MORPHO_GETARRAY(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<0) {\n            out=MORPHO_INTEGER(slf->nelements);\n        } else if (n<slf->nelements) {\n            out=slf->values[n];\n        } else morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n    return out;\n}\n\n/** Clone an array */\nvalue Array_clone(vm *v, int nargs, value *args) {\n    objectarray *slf = MORPHO_GETARRAY(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    objectarray *new = object_clonearray(slf);\n    if (new) {\n        out = MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\nMORPHO_BEGINCLASS(Array)\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Array_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Array_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(ARRAY_DIMENSIONS_METHOD, Array_dimensions, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Array_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Array_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Array_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Array_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nobjecttype objectarraytype;\n\nvoid array_initialize(void) {\n    // Create array object type\n    objectarraytype=object_addtype(&objectarraydefn);\n    \n    // Locate the Object class to use as the parent class of Array\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Array constructor function\n    morpho_addfunction(ARRAY_CLASSNAME, ARRAY_CLASSNAME \" (...)\", array_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Create Array veneer class\n    value arrayclass=builtin_addclass(ARRAY_CLASSNAME, MORPHO_GETCLASSDEFINITION(Array), objclass);\n    object_setveneerclass(OBJECT_ARRAY, arrayclass);\n    \n    // Array error messages\n    morpho_defineerror(ARRAY_ARGS, ERROR_HALT, ARRAY_ARGS_MSG);\n    morpho_defineerror(ARRAY_INIT, ERROR_HALT, ARRAY_INIT_MSG);\n    morpho_defineerror(ARRAY_CMPT, ERROR_HALT, ARRAY_CMPT_MSG);\n}\n"
  },
  {
    "path": "src/classes/array.h",
    "content": "/** @file array.h\n *  @author T J Atherton\n *\n *  @brief Defines array object type and Array class\n */\n\n#ifndef array_h\n#define array_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Array object type\n * ------------------------------------------------------- */\n\nextern objecttype objectarraytype;\n#define OBJECT_ARRAY objectarraytype\n\ntypedef struct {\n    object obj;\n    unsigned int ndim;\n    unsigned int nelements;\n    value *values;\n    value *dimensions;\n    value *multipliers;\n    value data[];\n} objectarray;\n\n/** Tests whether an object is an array */\n#define MORPHO_ISARRAY(val) object_istype(val, OBJECT_ARRAY)\n\n/** Gets the object as an array */\n#define MORPHO_GETARRAY(val)   ((objectarray *) MORPHO_GETOBJECT(val))\n\n/** Creates an array object */\nobjectarray *object_newarray(unsigned int dimension, unsigned int *dim);\n\n/** Creates a new array from a list of values */\nobjectarray *object_arrayfromvaluelist(unsigned int n, value *v);\n\n/** Creates a new 1D array from a list of varray_value */\nobjectarray *object_arrayfromvarrayvalue(varray_value *v);\n\n/** Creates a new array object with the dimensions given as a list of values */\nobjectarray *object_arrayfromvalueindices(unsigned int ndim, value *dim);\n\n/* -------------------------------------------------------\n * Array veneer class\n * ------------------------------------------------------- */\n\n#define ARRAY_CLASSNAME                   \"Array\"\n\n#define ARRAY_DIMENSIONS_METHOD           \"dimensions\"\n\n/* -------------------------------------------------------\n * Array error messages\n * ------------------------------------------------------- */\n\n#define ARRAY_ARGS                        \"ArrayArgs\"\n#define ARRAY_ARGS_MSG                    \"Array must be called with integer dimensions as arguments.\"\n\n#define ARRAY_INIT                        \"ArrayInit\"\n#define ARRAY_INIT_MSG                    \"Array initializer must be another array or a list.\"\n\n#define ARRAY_CMPT                        \"ArrayCmpt\"\n#define ARRAY_CMPT_MSG                    \"Array initializer is not compatible with the requested dimensions.\"\n\n/* -------------------------------------------------------\n * Array interface\n * ------------------------------------------------------- */\n\n/* Public interfaces to various data structures */\ntypedef enum { ARRAY_OK, ARRAY_WRONGDIM, ARRAY_OUTOFBOUNDS, ARRAY_NONINTINDX, ARRAY_ALLOC_FAILED } objectarrayerror;\n\nerrorid array_error(objectarrayerror err);\nerrorid array_to_matrix_error(objectarrayerror err);\nerrorid array_to_list_error(objectarrayerror err);\n\nbool array_valuelisttoindices(unsigned int ndim, value *in, unsigned int *out);\nobjectarrayerror array_getelement(objectarray *a, unsigned int ndim, unsigned int *indx, value *out);\nobjectarrayerror array_setelement(objectarray *a, unsigned int ndim, unsigned int *indx, value in);\nvoid array_print(vm *v, objectarray *a);\nobjectarrayerror setslicerecursive(value* a, value* out,objectarrayerror copy(value * ,value *,\\\n                                    unsigned int, unsigned int *,unsigned int *),unsigned int ndim,\\\n                                    unsigned int curdim, unsigned int *indx,unsigned int *newindx, value *slices);\nobjectarrayerror getslice(value *a, bool dimFcn(value *,unsigned int),\\\n                          void constuctor(unsigned int *,unsigned int,value *),\\\n                          objectarrayerror copy(value * ,value *, unsigned int, unsigned int *,unsigned int *),\\\n                          unsigned int ndim, value *slices, value *out);\nobjectarrayerror array_slicecopy(value * a,value * out, unsigned int ndim, unsigned int *indx,unsigned int *newindx);\nvoid array_sliceconstructor(unsigned int *slicesize,unsigned int ndim,value* out);\nbool array_slicedim(value * a, unsigned int ndim);\n\nvoid array_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/bool.c",
    "content": "/** @file bool.c\n *  @author T J Atherton\n *\n *  @brief Veneer class for bool values\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * Bool veneer class\n * ********************************************************************** */\n\nMORPHO_BEGINCLASS(Bool)\nMORPHO_METHOD(MORPHO_CLASS_METHOD, Object_class, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_RESPONDSTO_METHOD, Object_respondsto, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INVOKE_METHOD, Object_invoke, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, MORPHO_FN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nvoid bool_initialize(void) {\n    // Create Bool veneer class\n    value boolclass=builtin_addclass(BOOL_CLASSNAME, MORPHO_GETCLASSDEFINITION(Bool), MORPHO_NIL);\n    value_setveneerclass(MORPHO_TRUE, boolclass);\n}\n"
  },
  {
    "path": "src/classes/bool.h",
    "content": "/** @file bool.h\n *  @author T J Atherton\n *\n *  @brief Veneer class for bool values\n */\n\n#ifndef bool_h\n#define bool_h\n\n/* -------------------------------------------------------\n * Bool veneer class\n * ------------------------------------------------------- */\n\n#define BOOL_CLASSNAME \"Bool\"\n\n/* -------------------------------------------------------\n * Bool error messages\n * ------------------------------------------------------- */\n\n/* -------------------------------------------------------\n * Bool interface\n * ------------------------------------------------------- */\n\nvoid bool_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/classes.h",
    "content": "/** @file classes.h\n *  @author T J Atherton\n *\n *  @brief List of classes and object types\n */\n\n#ifndef classes_h\n#define classes_h\n\n#include \"builtin.h\"\n\n#include \"upvalue.h\"\n#include \"function.h\"\n#include \"metafunction.h\"\n#include \"clss.h\"\n#include \"cmplx.h\"\n#include \"closure.h\"\n#include \"invocation.h\"\n#include \"instance.h\"\n#include \"list.h\"\n#include \"array.h\"\n#include \"range.h\"\n#include \"strng.h\"\n#include \"dict.h\"\n#include \"tuple.h\"\n#include \"err.h\"\n\n#include \"bool.h\"\n#include \"flt.h\"\n#include \"int.h\"\n\n//#include \"file.h\"\n//#include \"system.h\"\n#include \"json.h\"\n\n#include \"matrix.h\"\n\n#endif /* classes_h */\n"
  },
  {
    "path": "src/classes/closure.c",
    "content": "/** @file closure.c\n *  @author T J Atherton\n *\n *  @brief Defines closure object type and Closure class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * objectclosure definitions\n * ********************************************************************** */\n\nvoid objectclosure_printfn(object *obj, void *v) {\n    objectclosure *f = (objectclosure *) obj;\n    morpho_printf(v, \"<\");\n    objectfunction_printfn((object *) f->func, v);\n    morpho_printf(v, \">\");\n}\n\nvoid objectclosure_markfn(object *obj, void *v) {\n    objectclosure *c = (objectclosure *) obj;\n    morpho_markobject(v, (object *) c->func);\n    for (unsigned int i=0; i<c->nupvalues; i++) {\n        morpho_markobject(v, (object *) c->upvalues[i]);\n    }\n}\n\nsize_t objectclosure_sizefn(object *obj) {\n    return sizeof(objectclosure)+sizeof(objectupvalue *)*((objectclosure *) obj)->nupvalues;\n}\n\nobjecttypedefn objectclosuredefn = {\n    .printfn=objectclosure_printfn,\n    .markfn=objectclosure_markfn,\n    .freefn=NULL,\n    .sizefn=objectclosure_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Closure functions */\nvoid object_closureinit(objectclosure *c) {\n    c->func=NULL;\n}\n\n/** @brief Creates a new closure\n *  @param sf       the objectfunction of the current environment\n *  @param func     a function object to enclose\n *  @param np       the prototype number to use */\nobjectclosure *object_newclosure(objectfunction *sf, objectfunction *func, indx np) {\n    objectclosure *new = NULL;\n    varray_upvalue *up = NULL;\n\n    if (np<sf->prototype.count) {\n        up = &sf->prototype.data[np];\n    }\n\n    if (up) {\n        new = (objectclosure *) object_new(sizeof(objectclosure) + sizeof(objectupvalue*)*up->count, OBJECT_CLOSURE);\n        if (new) {\n            object_closureinit(new);\n            new->func=func;\n            for (unsigned int i=0; i<up->count; i++) {\n                new->upvalues[i]=NULL;\n            }\n            new->nupvalues=up->count;\n        }\n    }\n\n    return new;\n}\n\n/* **********************************************************************\n * objectclosure utility functions\n * ********************************************************************** */\n\n/* **********************************************************************\n * Closure veneer class\n * ********************************************************************** */\n\nvalue Closure_tostring(vm *v, int nargs, value *args) {\n    objectclosure *self=MORPHO_GETCLOSURE(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n    \n    varray_char buffer;\n    varray_charinit(&buffer);\n\n    if (self->func) {\n        varray_charadd(&buffer, \"<<fn \", 5);\n        morpho_printtobuffer(v, self->func->name, &buffer);\n        varray_charadd(&buffer, \">>\", 2);\n    }\n\n    out = object_stringfromvarraychar(&buffer);\n    if (MORPHO_ISSTRING(out)) {\n        morpho_bindobjects(v, 1, &out);\n    }\n    varray_charclear(&buffer);\n\n    return out;\n}\n\nMORPHO_BEGINCLASS(Closure)\nMORPHO_METHOD(MORPHO_TOSTRING_METHOD, Closure_tostring, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectclosuretype;\n\nvoid closure_initialize(void) {\n    // Create closure object type\n    objectclosuretype=object_addtype(&objectclosuredefn);\n    \n    // Locate the Object class to use as the parent class of Closure\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // No constructor function; closures are generated by the runtime\n    \n    // Create Closure veneer class\n    value closureclass=builtin_addclass(CLOSURE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Closure), objclass);\n    object_setveneerclass(OBJECT_CLOSURE, closureclass);\n}\n"
  },
  {
    "path": "src/classes/closure.h",
    "content": "/** @file closure.h\n *  @author T J Atherton\n *\n *  @brief Defines closure object type and Closure class\n */\n\n#ifndef closure_h\n#define closure_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Closure objects\n * ------------------------------------------------------- */\n\nextern objecttype objectclosuretype;\n#define OBJECT_CLOSURE objectclosuretype\n\ntypedef struct {\n    object obj;\n    objectfunction *func;\n    int nupvalues;\n    objectupvalue *upvalues[];\n} objectclosure;\n\nobjectclosure *object_newclosure(objectfunction *sf, objectfunction *func, indx np);\n\n/** Tests whether an object is a closure */\n#define MORPHO_ISCLOSURE(val) object_istype(val, OBJECT_CLOSURE)\n\n/** Gets the object as a closure */\n#define MORPHO_GETCLOSURE(val)   ((objectclosure *) MORPHO_GETOBJECT(val))\n\n/** Retrieve the function object from a closure */\n#define MORPHO_GETCLOSUREFUNCTION(val)  (((objectclosure *) MORPHO_GETOBJECT(val))->func)\n\n/* -------------------------------------------------------\n * Closure veneer class\n * ------------------------------------------------------- */\n\n#define CLOSURE_CLASSNAME \"Closure\"\n\n/* -------------------------------------------------------\n * Closure error messages\n * ------------------------------------------------------- */\n\n/* -------------------------------------------------------\n * Closure interface\n * ------------------------------------------------------- */\n\nvoid closure_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/clss.c",
    "content": "/** @file clss.c\n *  @author T J Atherton\n *\n *  @brief Defines class object type\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * objectclass definitions\n * ********************************************************************** */\n\n/** Class object definitions */\nvoid objectclass_printfn(object *obj, void *v) {\n    morpho_printf(v, \"@%s\", MORPHO_GETCSTRING(((objectclass *) obj)->name));\n}\n\nvoid objectclass_markfn(object *obj, void *v) {\n    objectclass *c = (objectclass *) obj;\n    morpho_markvalue(v, c->name);\n    morpho_markdictionary(v, &c->methods);\n    morpho_markvarrayvalue(v, &c->parents);\n    morpho_markvarrayvalue(v, &c->children);\n}\n\nvoid objectclass_freefn(object *obj) {\n    objectclass *klass = (objectclass *) obj;\n    morpho_freeobject(klass->name);\n    dictionary_clear(&klass->methods);\n    varray_valueclear(&klass->parents);\n    varray_valueclear(&klass->children);\n    varray_valueclear(&klass->linearization);\n}\n\nsize_t objectclass_sizefn(object *obj) {\n    return sizeof(objectclass);\n}\n\nobjecttypedefn objectclassdefn = {\n    .printfn=objectclass_printfn,\n    .markfn=objectclass_markfn,\n    .freefn=objectclass_freefn,\n    .sizefn=objectclass_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\nobjectclass *object_newclass(value name) {\n    objectclass *newclass = (objectclass *) object_new(sizeof(objectclass), OBJECT_CLASS);\n\n    if (newclass) {\n        newclass->name=object_clonestring(name);\n        dictionary_init(&newclass->methods);\n        varray_valueinit(&newclass->parents);\n        varray_valueinit(&newclass->children);\n        varray_valueinit(&newclass->linearization);\n        newclass->superclass=NULL;\n        newclass->uid=0;\n    }\n\n    return newclass;\n}\n\n/* **********************************************************************\n * C3 Linearization algorithm\n * ********************************************************************** */\n\n/** C3 linearization aims to provide a linear ordering for a class hierarchy. It respects:\n \n * 1. Consistency with the hierarchy of classes (i.e. a class should appear AFTER any of its children).\n * 2. Consistency with the local precedence order for each class definition.\n * 3. Consistency with the extended precedence graph.\n \n * see: - Barrett et al. \"A Monotonic Superclass Linearization for Dylan\" [https://opendylan.org/_static/c3-linearization.pdf]\n *    - Simionato, \"The Python 2.3 Method Resolution Order\" Python 2.3 [https://www.python.org/download/releases/2.3/mro/]\n *    - Hivert & Thierry \"Controlling the C3 super class linearization algorithm for large hierarchies of classes\" [https://arxiv.org/pdf/2401.12740] */\n\nvoid _print(varray_value *list) {\n    printf(\"[ \");\n    for (int i=0; i<list->count; i++) {\n        morpho_printvalue(NULL, list->data[i]);\n        if (i<list->count-1) printf(\", \");\n    }\n    printf(\" ]\");\n}\n\n/** Check if value v is in the tail of a list? */\nbool _intail(varray_value *list, value v) {\n    for (int i=1; i<list->count; i++) {\n        if (MORPHO_ISEQUAL(list->data[i], v)) return true;\n    }\n    return false;\n}\n\n/** Remove value v from a list in  */\nvoid _remove(varray_value *list, value v) {\n    for (int i=0; i<list->count; i++) {\n        if (MORPHO_ISEQUAL(list->data[i], v)) {\n            if (i<list->count-1) memmove(list->data+i, list->data+i+1, sizeof(value)*(list->count-i-1));\n            list->count--;\n        }\n    }\n}\n\n/** Check if value v is in any tail of the set of lists */\nbool _inanytail(int n, varray_value *in, value v) {\n    for (int i=0; i<n; i++) {\n        if (_intail(&in[i], v)) return true;\n    }\n    return false;\n}\n\n/** Check if any of the sets contain elements */\nbool _done(int n, varray_value *in) {\n    for (int i=0; i<n; i++) if (in[i].count>0) return false;\n    return true;\n}\n\n/** Performs one C3 merge operation for a set of lists  */\nbool _merge(int n, varray_value *in, varray_value *out) {\n    for (int i=0; i<n; i++) {\n        if (in[i].count==0) continue;\n        \n        value head = in[i].data[0]; // Choose a head that is not in any tail\n        if (_inanytail(n, in, head)) continue;\n\n        varray_valuewrite(out, head); // Add it to the linearization and remove from the lists\n        for (int j=0; j<n; j++) _remove(&in[j], head);\n        return true;\n    }\n    return false;\n}\n\n/** Initialize the varray from the parent class's linearization */\nstatic void _init(objectclass *parent, varray_value *out) {\n    if (parent->linearization.count) varray_valueadd(out, parent->linearization.data, parent->linearization.count);\n}\n\n/** Compute the linearization of a given class */\nbool _linearize(objectclass *klass, varray_value *out) {\n    // Add this class to the start of the list\n    varray_valuewrite(out, MORPHO_OBJECT(klass));\n    \n    if (klass->parents.count==0) return true;\n    int n=klass->parents.count+1;\n    \n    // Start with the linearizations of the parent classes & the list of parent classes themselves\n    varray_value lin[n];\n    for (int i=0; i<n; i++) varray_valueinit(&lin[i]);\n    for (int i=0; i<n-1; i++) _init(MORPHO_GETCLASS(klass->parents.data[i]), &lin[i]);\n    varray_valueadd(&lin[n-1], klass->parents.data, klass->parents.count); // Also add the parents to preserve their order\n    \n    bool success=true;\n    while (success && !_done(n, lin)) {\n        success=_merge(n, lin, out);\n    }\n\n    for (int i=0; i<n; i++) varray_valueclear(&lin[i]);\n    \n    return success;\n}\n\n/** Public wrapper function to compute linearization */\nbool class_linearize(objectclass *klass) {\n    klass->linearization.count=0;\n    return _linearize(klass, &klass->linearization);\n}\n\n/* **********************************************************************\n * Class veneer class\n * ********************************************************************** */\n\nMORPHO_BEGINCLASS(Class)\nMORPHO_METHOD(MORPHO_CLASS_METHOD, Object_class, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_RESPONDSTO_METHOD, Object_respondsto, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INVOKE_METHOD, Object_invoke, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, MORPHO_FN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectclasstype;\n\nvoid class_initialize(void) {\n    // objectclass is a core type so is intialized earlier\n    \n    // Locate the Object class to use as the parent class of Class\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value classclass=builtin_addclass(CLASS_CLASSNAME, MORPHO_GETCLASSDEFINITION(Class), objclass);\n    object_setveneerclass(OBJECT_CLASS, classclass);\n    \n    // No constructor function; classes are generated by the compiler\n    \n    // Class error messages\n    morpho_defineerror(CLASS_INVK, ERROR_HALT, CLASS_INVK_MSG);\n}\n"
  },
  {
    "path": "src/classes/clss.h",
    "content": "/** @file clss.h\n *  @author T J Atherton\n *\n *  @brief Defines class object type\n */\n\n#ifndef clss_h\n#define clss_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Class objects\n * ------------------------------------------------------- */\n\nextern objecttype objectclasstype;\n#define OBJECT_CLASS objectclasstype\n\ntypedef struct sobjectclass {\n    object obj;\n    struct sobjectclass *superclass; /** The class's superclass */\n    value name; /** Class name */\n    dictionary methods; /** Method dictionary */\n    varray_value parents; /** Classes this class inherits from */\n    varray_value children; /** Classes that inherit from this class */\n    varray_value linearization; /** Classes that inherit from this class */\n    int uid;\n} objectclass;\n\n/** Tests whether an object is a class */\n#define MORPHO_ISCLASS(val) object_istype(val, OBJECT_CLASS)\n\n/** Gets the object as a class */\n#define MORPHO_GETCLASS(val)   ((objectclass *) MORPHO_GETOBJECT(val))\n\n/** Gets the superclass */\n#define MORPHO_GETSUPERCLASS(val)   (MORPHO_GETCLASS(val)->superclass)\n\n/* -------------------------------------------------------\n * Class veneer class\n * ------------------------------------------------------- */\n\n#define CLASS_CLASSNAME                   \"Class\"\n\n/* -------------------------------------------------------\n * Class error messages\n * ------------------------------------------------------- */\n\n#define CLASS_INVK                        \"ClssInvk\"\n#define CLASS_INVK_MSG                    \"Cannot invoke method '%s' on a class.\"\n\n/* -------------------------------------------------------\n * Class interface\n * ------------------------------------------------------- */\n\nobjectclass *object_newclass(value name);\nobjectclass *morpho_lookupclass(value obj);\n\nbool class_linearize(objectclass *klass);\n\nvoid class_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/cmplx.c",
    "content": "/** @file complex.c\n *  @author D Hellstein and T J Atherton\n *\n *  @brief Complex number type\n */\n\n#include <string.h>\n#include <complex.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * Complex objects\n * ********************************************************************** */\n\nobjecttype objectcomplextype;\n\n/** Complex object definitions */\nsize_t objectcomplex_sizefn(object *obj) {\n    return sizeof(objectcomplex);\n}\n\nvoid objectcomplex_printfn(object *obj, void *v) {\n    complex_print(v, (objectcomplex *) obj);\n}\n\nint objectcomplex_cmpfn(object *a, object *b) {\n    objectcomplex *acomp = (objectcomplex *) a;\n    objectcomplex *bcomp = (objectcomplex *) b;\n    return (complex_isequal(acomp, bcomp)? MORPHO_EQUAL: MORPHO_NOTEQUAL);\n}\n\nobjecttypedefn objectcomplexdefn = {\n    .printfn=objectcomplex_printfn,\n    .markfn=NULL,\n    .freefn=NULL,\n    .sizefn=objectcomplex_sizefn,\n    .hashfn=NULL,\n    .cmpfn=objectcomplex_cmpfn\n};\n\n/** Creates a complex object */\nobjectcomplex *object_newcomplex(double real,double imag) {\n    objectcomplex *new = (objectcomplex *) object_new(sizeof(objectcomplex), OBJECT_COMPLEX);\n    \n    if (new) {\n        new->Z=MCBuild(real,imag);\n    }\n    \n    return new;\n}\n\n/* **********************************************************************\n * Other constructors\n * ********************************************************************** */\n\n/** Create complex number from a float */\nobjectcomplex *object_complexfromfloat(double val) {\n    objectcomplex *ret=object_newcomplex(val,0.0);\n    return ret;\n}\n\n/** Create complex number from a complex */\nobjectcomplex *object_complexfromcomplex(MorphoComplex val) {\n    objectcomplex *ret=object_newcomplex(creal(val),cimag(val));\n    return ret;\n}\n\n/** Clone a complex */\nobjectcomplex *object_clonecomplex(objectcomplex *in) {\n    objectcomplex *new = object_newcomplex(creal(in->Z),cimag(in->Z));\n    return new;\n}\n\n/** Clone a Complex Stored in a value*/\nvalue object_clonecomplexvalue(value val) {\n    value out = MORPHO_NIL;\n    if (MORPHO_ISCOMPLEX(val)) {\n        objectcomplex *c = MORPHO_GETCOMPLEX(val);\n        out=MORPHO_OBJECT(object_clonecomplex(c));\n    }\n    return out;\n}\n\n/* **********************************************************************\n * Complex operations\n * ********************************************************************* */\n\n/** @brief Gets a complex numbers real part */\nvoid complex_getreal(objectcomplex *c, double *value) {\n    *value = creal(c->Z);\n}\n\n/** @brief Gets a complex numbers imaginary part */\nvoid complex_getimag(objectcomplex *c, double *value) {\n    *value = cimag(c->Z);\n}\n\n/** @brief checks equality on two complex numbers */\nbool complex_isequal(objectcomplex *a, objectcomplex *b) {\n    return MCEq(a->Z,b->Z);\n}\n\n/** @brief checks equality between a complex number and a value */\nbool complex_isequaltonumber(objectcomplex *a, value b) {\n    if (MORPHO_ISNUMBER(b)){\n        double val;\n        morpho_valuetofloat(b,&val);\n        return MCEq(a->Z, MCBuild(val,0.0));\n    }\n    return false;\n}\n\n/** Prints a complex number */\nvoid complex_print(vm *v, objectcomplex *a) {\n    char sign = '+';\n    if (cimag(a->Z)<0) {\n        sign = '-';\n    }\n\n    double Zr = creal(a->Z), Zi = cimag(a->Z), R = cabs(a->Z);\n\n    double showZr = ( fabs(Zr) < MORPHO_RELATIVE_EPS*R ? 0 : Zr);\n    double showZi = ( fabs(Zi) < MORPHO_RELATIVE_EPS*R ? 0 : fabs(Zi));\n\n    morpho_printf(v, \"%g %c %gim\", showZr, sign, showZi);\n}\n\n/* **********************************************************************\n * Complex arithmetic\n * ********************************************************************* */\n\n/** performs out = a + b */\nvoid complex_add(objectcomplex *a, objectcomplex *b, objectcomplex *out){\n    out->Z = MCAdd(a->Z,b->Z);\n}\n\n/** performs out = a + b where a is not complex */\nvoid complex_add_real(objectcomplex *a, double b, objectcomplex *out){\n    out->Z = MCAdd(a->Z, MCBuild(b,0));\n}\n\n/** performs out = a - b  */\nvoid complex_sub(objectcomplex *a, objectcomplex *b, objectcomplex *out) {\n    out->Z = MCSub(a->Z, b->Z);\n}\n\n/** performs out = a * b */\nvoid complex_mul(objectcomplex *a, objectcomplex *b, objectcomplex *out){\n    out->Z = MCMul(a->Z, b->Z);\n}\n\n/** performs out = a * b where b is real */\nvoid complex_mul_real(objectcomplex *a, double b, objectcomplex *out){\n    out->Z = MCScale(a->Z, b);\n}\n\n/** performs out = a */\nvoid complex_copy(objectcomplex *a, objectcomplex *out) {\n    out->Z = a->Z;\n}\n\n/** performs out = a ^ b  where b is real*/\nvoid complex_power(objectcomplex *a, double exponent, objectcomplex *out){\n    out->Z = cpow(a->Z,MCBuild(exponent, 0));\n}\n\n/** performs out = a ^ b  for complex numbers*/\nvoid complex_cpower(objectcomplex *a, objectcomplex *b, objectcomplex *out){\n    out->Z = cpow(a->Z,b->Z);\n}\n\n/** performs out = a / b */\nvoid complex_div(objectcomplex *a, objectcomplex *b, objectcomplex *out){\n    out->Z = MCDiv(a->Z,b->Z);\n}\n\n/** performs out = 1/a */\nvoid complex_invert(objectcomplex *a, objectcomplex *out){\n    out->Z = MCDiv(MCBuild(1,0), a->Z);\n}\n\n/** performs out = conj(a) by negating the imaginary part */\nvoid complex_conj(objectcomplex *a, objectcomplex *out) {\n    out->Z = conj(a->Z);\n}\n\n/** calculates theta in the complex representation a = r e^{i theta}  */\nvoid complex_angle(objectcomplex *a, double *out){\n    *out = carg(a->Z);\n}\n\nvoid complex_abs(objectcomplex *a, double *out) {\n    *out = cabs(a->Z);\n}\n\n/* **********************************************************************\n * Builtin Mathematical Funtions For Complex Numbers\n * ********************************************************************* */\n\n// Macro for creating a value from a new complex that copies val \n#define RET_COMPLEX(val,out) \\\n    objectcomplex *new=NULL;\\\n    new = object_complexfromcomplex(val);\\\n    if (new) {\\\n        out=MORPHO_OBJECT(new);\\\n        morpho_bindobjects(v, 1, &out);\\\n    }\n\n// Macro for creating a value bool \n#define RET_DOUBLE(val,out) \\\n    out = MORPHO_FLOAT(val);\n\n\n#define COMPLEX_BUILTIN(fcn,type,MAKEVAL)\\\nvalue complex_builtin##fcn(vm * v, objectcomplex *c) {\\\n    value out = MORPHO_NIL;\\\n    type val = c##fcn(c->Z);\\\n    MAKEVAL(val,out)\\\n    return out;\\\n}\n\nvalue complex_builtinfabs(vm * v, objectcomplex *c) {\n    double val = cabs(c->Z);\n    return MORPHO_FLOAT(val);\n}\n\nCOMPLEX_BUILTIN(exp,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(log,MorphoComplex,RET_COMPLEX)\n\nvalue complex_builtinlog10(vm * v, objectcomplex *c) {\n    value out = MORPHO_NIL;\n    MorphoComplex val = MCScale(clog(c->Z), 1.0/log(10));\n    RET_COMPLEX(val,out)\n    return out;\n}\n\n\nCOMPLEX_BUILTIN(sin,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(cos,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(tan,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(asin,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(acos,MorphoComplex,RET_COMPLEX)\n\nCOMPLEX_BUILTIN(sinh,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(cosh,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(tanh,MorphoComplex,RET_COMPLEX)\nCOMPLEX_BUILTIN(sqrt,MorphoComplex,RET_COMPLEX)\n\nvalue complex_builtinfloor(vm * v, objectcomplex *c) {\n    value out = MORPHO_NIL;\n    MorphoComplex val = MCBuild(floor(creal(c->Z)),floor(cimag(c->Z)));\n    RET_COMPLEX(val,out)\n    return out;\n}\n\nvalue complex_builtinceil(vm * v, objectcomplex *c) {\n    value out = MORPHO_NIL;\n    MorphoComplex val = MCBuild(ceil(creal(c->Z)), ceil(cimag(c->Z)));\n    RET_COMPLEX(val,out)\n    return out;\n}\n\n#undef COMPLEX_BUILTIN\n#undef RET_COMPLEX\n#undef RET_DOUBLE\n\n#define COMPLEX_BUILTIN_BOOL(fcn,logicalop)\\\nvalue complex_builtin##fcn(objectcomplex *c) {\\\n    bool val = fcn(creal(c->Z)) logicalop fcn(cimag(c->Z));\\\n    return MORPHO_BOOL(val);\\\n}\n\nCOMPLEX_BUILTIN_BOOL(isfinite,&&)\nCOMPLEX_BUILTIN_BOOL(isinf,||)\nCOMPLEX_BUILTIN_BOOL(isnan,||)\n\n#undef COMPLEX_BUILTIN_BOOL\n\nvalue complex_builtinatan(vm *v, value c){\n    value out = MORPHO_NIL;\n    MorphoComplex val = catan(MORPHO_GETCOMPLEX(c)->Z);\n    objectcomplex *new = NULL;\n    new = object_complexfromcomplex(val);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    return out;\n}\n\nvalue complex_builtinatan2(vm *v, value c1, value c2){\n    value out = MORPHO_NIL;\n    MorphoComplex val=MCBuild(0,0);\n    \n    if (MORPHO_ISCOMPLEX(c1) && MORPHO_ISCOMPLEX(c2)) {\n        val = catan(MCDiv(MORPHO_GETCOMPLEX(c1)->Z, MORPHO_GETCOMPLEX(c2)->Z));\n    } else if (MORPHO_ISCOMPLEX(c1) && MORPHO_ISNUMBER(c2)) {\n        double num;\n        morpho_valuetofloat(c2,&num);\n        val = catan(MCScale(MORPHO_GETCOMPLEX(c1)->Z, 1.0/num));\n    } else if (MORPHO_ISNUMBER(c1) && MORPHO_ISCOMPLEX(c2)) {\n        double num;\n        morpho_valuetofloat(c1,&num);\n        val = catan(MCDiv(MCBuild(num,0), MORPHO_GETCOMPLEX(c2)->Z));\n    } else {\n        morpho_runtimeerror(v, COMPLEX_INVLDNARG);\n        return MORPHO_NIL;\n    }\n     \n    objectcomplex *new = NULL;\n    new = object_complexfromcomplex(val);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n\n/* **********************************************************************\n * Complex veneer class\n * ********************************************************************* */\n\n/** Constructs a Complex object */\nvalue complex_constructor(vm *v, int nargs, value *args) {\n    double real=0, imag=0;\n    objectcomplex *new=NULL;\n    value out=MORPHO_NIL;\n    // expect 2 aruments\n\n    if (nargs==2){\n        // make sure both are numbers and cast them to floats\n        if (MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n            morpho_valuetofloat(MORPHO_GETARG(args, 0), &real);\n        } else goto complex_constructor_error;\n        \n        if (MORPHO_ISNUMBER(MORPHO_GETARG(args, 1))) {\n            morpho_valuetofloat(MORPHO_GETARG(args, 1), &imag);\n        } else goto complex_constructor_error;\n\n    } else goto complex_constructor_error;\n\n    new = object_newcomplex(real, imag);\n    \n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n    \ncomplex_constructor_error:\n    morpho_runtimeerror(v, COMPLEX_CONSTRUCTOR);\n    \n    return MORPHO_NIL;\n}\n\n/** Gets the real part of a complex number */\nvalue Complex_getreal(vm *v, int nargs, value *args) {\n    objectcomplex *c=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n\n    value out = MORPHO_NIL;\n\tif (nargs>0){\n\t\tmorpho_runtimeerror(v, COMPLEX_INVLDNARG);\n\t\treturn out;\n\t}\n    \n    double real;\n    complex_getreal(c, &real);\n    out = MORPHO_FLOAT(real);\n    return out;\n}\n\n/** Gets the imaginary part of a complex number */\nvalue Complex_getimag(vm *v, int nargs, value *args) {\n    objectcomplex *c=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n\n    value out = MORPHO_NIL;\n\tif (nargs>0){\n\t\tmorpho_runtimeerror(v, COMPLEX_INVLDNARG);\n\t\treturn out;\n\t}\n    \n    double imag;\n    complex_getimag(c, &imag);\n    out = MORPHO_FLOAT(imag);\n    return out;\n}\n\n/** Prints a complex */\nvalue Complex_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISCOMPLEX(self)) return Object_print(v, nargs, args);\n    \n    objectcomplex *c=MORPHO_GETCOMPLEX(self);\n    complex_print(v, c);\n    return MORPHO_NIL;\n}\n\n/** Complex add */\nvalue Complex_add(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISCOMPLEX(MORPHO_GETARG(args, 0))) {\n        objectcomplex *b=MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0));\n        \n        objectcomplex *new = object_newcomplex(0, 0);\n        if (new) {\n            out=MORPHO_OBJECT(new);\n            complex_add(a, b, new);\n        }\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_newcomplex(0,0);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_add_real(a, val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Complex subtract */\nvalue Complex_sub(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISCOMPLEX(MORPHO_GETARG(args, 0))) {\n        objectcomplex *b=MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0));\n        \n        objectcomplex *new = object_newcomplex(0, 0);\n        if (new) {\n            out=MORPHO_OBJECT(new);\n            complex_sub(a, b, new);\n        }\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_newcomplex(0,0);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_add_real(a, -val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Right subtract */\nvalue Complex_subr(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_clonecomplex(a);\n            complex_mul_real(new,-1,new);\n\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_add_real(new, val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Complex multiply */\nvalue Complex_mul(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISCOMPLEX(MORPHO_GETARG(args, 0))) {\n        objectcomplex *b=MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0));\n        \n        objectcomplex *new = object_newcomplex(0, 0);\n        if (new) {\n            out=MORPHO_OBJECT(new);\n            complex_mul(a, b, new);\n        }\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_newcomplex(0,0);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_mul_real(a, val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Complex divide */\nvalue Complex_div(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISCOMPLEX(MORPHO_GETARG(args, 0))) {\n        objectcomplex *b=MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0));\n        \n        objectcomplex *new = object_newcomplex(0, 0);\n        if (new) {\n            out=MORPHO_OBJECT(new);\n            complex_div(a, b, new);\n        }\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_newcomplex(0,0);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_mul_real(a, 1.0/val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Complex right divide */\nvalue Complex_divr(vm *v, int nargs, value *args) {\n    // this gets called when we divide a nonobject (number) by a complex number\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n\n            objectcomplex *new = object_newcomplex(0,0);\n            complex_invert(a,new);\n            complex_mul_real(new,val,new);\n\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else UNREACHABLE(\"Number did not return float value\");\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    return out;\n}\n\n/** Complex exponentiation */\nvalue Complex_power(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISCOMPLEX(MORPHO_GETARG(args, 0))) {\n        // raise a complex number to a complex power\n        objectcomplex *b=MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0));\n        \n        objectcomplex *new = object_newcomplex(0, 0);\n        if (new) {\n            out=MORPHO_OBJECT(new);\n            complex_cpower(a, b, new);\n        }\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        // raise complex power to a number\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_newcomplex(0,0);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_power(a, val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Complex right exponentiation */\nvalue Complex_powerr(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISCOMPLEX(MORPHO_GETARG(args, 0))) {\n        // raise a complex number to a complex power\n        objectcomplex *b=MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0));\n        \n        objectcomplex *new = object_newcomplex(0, 0);\n        if (new) {\n            out=MORPHO_OBJECT(new);\n            complex_cpower(a, b, new);\n        }\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        // raise a number to a complex power\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectcomplex *new = object_newcomplex(val,0);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                complex_cpower(new, a, new);\n            }\n        }\n    } else morpho_runtimeerror(v, COMPLEX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n\n}\n\n/** Angle of a complex number  */\nvalue Complex_angle(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    double val;\n    complex_angle(a, &val);\n    return MORPHO_FLOAT(val);\n}\nvalue Complex_abs(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    double val;\n    complex_abs(a, &val);\n    return MORPHO_FLOAT(val);\n}\n\n/** Conjugate of a complex */\nvalue Complex_conjugate(vm *v, int nargs, value *args) {\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    objectcomplex *new = object_newcomplex(0,0);\n    if (new) {\n        complex_conj(a, new);\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n/** Clones a complex */\nvalue Complex_clone(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectcomplex *a=MORPHO_GETCOMPLEX(MORPHO_SELF(args));\n    objectcomplex *new=object_clonecomplex(a);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    return out;\n}\n\nMORPHO_BEGINCLASS(ComplexNum)\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Complex_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADD_METHOD, Complex_add, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUB_METHOD, Complex_sub, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MUL_METHOD, Complex_mul, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIV_METHOD, Complex_div, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADDR_METHOD, Complex_add, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUBR_METHOD, Complex_subr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MULR_METHOD, Complex_mul, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIVR_METHOD, Complex_divr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_POW_METHOD, Complex_power, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_POWR_METHOD, Complex_powerr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(COMPLEX_ANGLE_METHOD, Complex_angle, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(COMPLEX_CONJUGATE_METHOD, Complex_conjugate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(COMPLEX_REAL_METHOD, Complex_getreal, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(COMPLEX_IMAG_METHOD, Complex_getimag, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(COMPLEX_ABS_METHOD, Complex_abs, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Complex_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************* */\n\nvoid complex_initialize(void) {\n    // Define complex object type\n    objectcomplextype=object_addtype(&objectcomplexdefn);\n    \n    // Complex constructor function\n    morpho_addfunction(COMPLEX_CLASSNAME, COMPLEX_CLASSNAME \" (...)\", complex_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Define Complex class\n    value complexclass=builtin_addclass(COMPLEX_CLASSNAME, MORPHO_GETCLASSDEFINITION(ComplexNum), objclass);\n    object_setveneerclass(OBJECT_COMPLEX, complexclass);\n\n    // Complex error messages\n    morpho_defineerror(COMPLEX_CONSTRUCTOR, ERROR_HALT, COMPLEX_CONSTRUCTOR_MSG);\n    morpho_defineerror(COMPLEX_ARITHARGS, ERROR_HALT, COMPLEX_ARITHARGS_MSG);\n    morpho_defineerror(COMPLEX_INVLDNARG, ERROR_HALT, COMPLEX_INVLDNARG_MSG);\n}\n"
  },
  {
    "path": "src/classes/cmplx.h",
    "content": "/** @file cmplx.h\n *  @author D Hellstein and T J Atherton\n *\n *  @brief Veneer class over the objectcomplex type\n */\n\n#ifndef cmplx_h\n#define cmplx_h\n\n#include <stdio.h>\n#include <complex.h>\n#include \"classes.h\"\n#include \"platform.h\"\n\n/* -------------------------------------------------------\n * Complex objects\n * ------------------------------------------------------- */\n\nextern objecttype objectcomplextype;\n#define OBJECT_COMPLEX objectcomplextype\n\ntypedef struct {\n    object obj;\n    MorphoComplex Z;\n} objectcomplex;\n\n/** Creates a static complex number */\n#define MORPHO_STATICCOMPLEX(real,imag)      { .obj.type=OBJECT_COMPLEX, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .Z=MCBuild(real,imag)}\n\n/** Tests whether an object is a complex */\n#define MORPHO_ISCOMPLEX(val) object_istype(val, OBJECT_COMPLEX)\n\n/** Gets the object as a complex */\n#define MORPHO_GETCOMPLEX(val)   ((objectcomplex *) MORPHO_GETOBJECT(val))\n\n/** Gets the object as a C-style MorphoComplex */\n#define MORPHO_GETDOUBLECOMPLEX(val)   ((MorphoComplex) ((objectcomplex *) MORPHO_GETOBJECT(val))->Z)\n\n/** Creates a complex object */\nobjectcomplex *object_newcomplex(double real, double imag);\n\n/** Creates a new complex from an existing complex */\nobjectcomplex *object_clonecomplex(objectcomplex *array);\n\n/** Clones a value that holds a complex */\nvalue object_clonecomplexvalue(value val);\n/** creates a complex object from a real value */\nobjectcomplex *object_complexfromfloat(double val);\n\n/** tests the equality of two complex numbers */\nbool complex_isequal(objectcomplex *a, objectcomplex *b);\n\n/** tests equality between a complex number and a value */\nbool complex_isequaltonumber(objectcomplex *a, value b);\n\n/* -------------------------------------------------------\n * Complex class\n * ------------------------------------------------------- */\n\n#define COMPLEX_CLASSNAME                   \"Complex\"\n\n#define COMPLEX_CONJUGATE_METHOD            \"conj\"\n#define COMPLEX_ABS_METHOD                  \"abs\"\n#define COMPLEX_REAL_METHOD                 \"real\"\n#define COMPLEX_IMAG_METHOD                 \"imag\"\n#define COMPLEX_ANGLE_METHOD                \"angle\"\n\n/* -------------------------------------------------------\n * Complex error messages\n * ------------------------------------------------------- */\n\n#define COMPLEX_CONSTRUCTOR                \"CmplxCns\"\n#define COMPLEX_CONSTRUCTOR_MSG            \"Complex() constructor should be called with two floats\"\n\n#define COMPLEX_ARITHARGS                  \"CmplxInvldArg\"\n#define COMPLEX_ARITHARGS_MSG              \"Complex arithmetic methods expect a complex or number as their argument.\"\n\n#define COMPLEX_INVLDNARG                  \"CmpxArg\"\n#define COMPLEX_INVLDNARG_MSG              \"Complex Operation did not exect those arguments.\"\n\n/* -------------------------------------------------------\n * Complex interface\n * ------------------------------------------------------- */\n\nvoid complex_copy(objectcomplex *a, objectcomplex *out);\nvoid complex_add(objectcomplex *a, objectcomplex *b, objectcomplex *out);\nvoid complex_sub(objectcomplex *a, objectcomplex *b, objectcomplex *out);\nvoid complex_mul(objectcomplex *a, objectcomplex *b, objectcomplex *out);\nvoid complex_div(objectcomplex *a, objectcomplex *b, objectcomplex *out);\nvoid complex_conj(objectcomplex *a, objectcomplex *out);\nvoid complex_abs(objectcomplex *a, double *out);\nvoid complex_angle(objectcomplex *a, double *out);\nvoid complex_getreal(objectcomplex *c, double *value);\nvoid complex_getimag(objectcomplex *c, double *value);\n\nvoid complex_print(vm *v, objectcomplex *m);\n\n/* Built-in fucntions */\n\nvalue complex_builtinexp(vm *v, objectcomplex *c);\nvalue complex_builtinfabs(vm *v, objectcomplex *c);\nvalue complex_builtinexp(vm *v, objectcomplex *c);\nvalue complex_builtinlog(vm *v, objectcomplex *c);\nvalue complex_builtinlog10(vm *v, objectcomplex *c);\n\nvalue complex_builtinsin(vm *v, objectcomplex *c);\nvalue complex_builtincos(vm *v, objectcomplex *c);\nvalue complex_builtintan(vm *v, objectcomplex *c);\nvalue complex_builtinasin(vm *v, objectcomplex *c);\nvalue complex_builtinacos(vm *v, objectcomplex *c);\n\nvalue complex_builtinsinh(vm *v, objectcomplex *c);\nvalue complex_builtincosh(vm *v, objectcomplex *c);\nvalue complex_builtintanh(vm *v, objectcomplex *c);\nvalue complex_builtinsqrt(vm *v, objectcomplex *c);\n\nvalue complex_builtinfloor(vm *v, objectcomplex *c);\nvalue complex_builtinceil(vm *v, objectcomplex *c);\n\nvalue complex_builtinisfinite(objectcomplex *c);\nvalue complex_builtinisinf(objectcomplex *c);\nvalue complex_builtinisnan(objectcomplex *c);\n\nvalue complex_builtinatan(vm *v, value c);\nvalue complex_builtinatan2(vm *v, value c1, value c2);\n\n/* Complex methods */\n\nvalue Complex_getreal(vm *v, int nargs, value *args);\nvalue Complex_getimag(vm *v, int nargs, value *args);\nvalue Complex_angle(vm *v, int nargs, value *args);\nvalue Complex_conj(vm *v, int nargs, value *args);\n\nvoid complex_initialize(void);\n\n#endif /* complex_h */\n"
  },
  {
    "path": "src/classes/dict.c",
    "content": "/** @file dict.c\n *  @author T J Atherton\n *\n *  @brief Defines dictionary object type and Dictionary veneer class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * objectdictionary definitions\n * ********************************************************************** */\n\n/** Dictionary object definitions */\nvoid objectdictionary_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Dictionary>\");\n}\n\nvoid objectdictionary_freefn(object *obj) {\n    objectdictionary *dict = (objectdictionary *) obj;\n    dictionary_clear(&dict->dict);\n}\n\nvoid objectdictionary_markfn(object *obj, void *v) {\n    objectdictionary *c = (objectdictionary *) obj;\n    morpho_markdictionary(v, &c->dict);\n}\n\nsize_t objectdictionary_sizefn(object *obj) {\n    return sizeof(objectdictionary)+(((objectdictionary *) obj)->dict.capacity)*sizeof(dictionaryentry);\n}\n\nobjecttypedefn objectdictionarydefn = {\n    .printfn=objectdictionary_printfn,\n    .markfn=objectdictionary_markfn,\n    .freefn=objectdictionary_freefn,\n    .sizefn=objectdictionary_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Creates a new dictionary */\nobjectdictionary *object_newdictionary(void) {\n    objectdictionary *new = (objectdictionary *) object_new(sizeof(objectdictionary), OBJECT_DICTIONARY);\n\n    if (new) dictionary_init(&new->dict);\n\n    return new;\n}\n\n/* **********************************************************************\n * objectdictionary utility functions\n * ********************************************************************** */\n\n/** Extracts the dictionary from an objectdictionary. */\ndictionary *object_dictionary(objectdictionary *dict) {\n    return &dict->dict;\n}\n\n/** Iterates over dictionary; current implementation returns a sequence of keys */\nvalue dictionary_iterate(objectdictionary *dict, unsigned int n) {\n    unsigned int k=0;\n    for (unsigned int i=0; i<dict->dict.capacity; i++) {\n        if (!MORPHO_ISNIL(dict->dict.contents[i].key)) {\n            if (k==n) return dict->dict.contents[i].key;\n            k++;\n        }\n    }\n    return MORPHO_NIL;\n}\n\n/* **********************************************************************\n * Dictionary veneer class\n * ********************************************************************** */\n\n/** Dictionary constructor function */\nvalue dictionary_constructor(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectdictionary *new=object_newdictionary();\n\n    if (new) {\n        out=MORPHO_OBJECT(new);\n\n        for (unsigned int i=0; i+1<nargs; i+=2) {\n            dictionary_insert(&new->dict, MORPHO_GETARG(args, i), MORPHO_GETARG(args, i+1));\n        }\n\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\n/** Gets a dictionary entry */\nvalue Dictionary_getindex(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1) {\n        if(!dictionary_get(&slf->dict, MORPHO_GETARG(args, 0), &out)) {\n            morpho_runtimeerror(v, DICT_DCTKYNTFND);\n        }\n    }\n\n    return out;\n}\n\n/** Sets a dictionary entry */\nvalue Dictionary_setindex(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n\n    if (nargs==2) {\n        unsigned int capacity = slf->dict.capacity;\n\n        dictionary_insert(&slf->dict, MORPHO_GETARG(args, 0), MORPHO_GETARG(args, 1));\n\n        if (slf->dict.capacity!=capacity) morpho_resizeobject(v, (object *) slf, capacity*sizeof(dictionaryentry)+sizeof(objectdictionary), slf->dict.capacity*sizeof(dictionaryentry)+sizeof(objectdictionary));\n    } else morpho_runtimeerror(v, SETINDEX_ARGS);\n\n    return MORPHO_NIL;\n}\n\n/** Returns a Bool value for whether the Dictionary contains a given key */\nvalue Dictionary_contains(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    value out=MORPHO_FALSE;\n\n    if (nargs==1) {\n        if (dictionary_get(&slf->dict, MORPHO_GETARG(args, 0), &out)) out=MORPHO_TRUE;\n    }\n\n    return out;\n}\n\n/** Removes a dictionary entry with a given key */\nvalue Dictionary_remove(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    \n    if (nargs==1) {\n        dictionary_remove(&slf->dict, MORPHO_GETARG(args, 0));\n    }\n    \n    return MORPHO_NIL;\n}\n\n/** Clears a Dictionary */\nvalue Dictionary_clear(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    \n    dictionary_clear(&slf->dict);\n    \n    return MORPHO_NIL;\n}\n\n/** Prints a dictionary */\nvalue Dictionary_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISDICTIONARY(self)) return Object_print(v, nargs, args);\n    \n    objectdictionary *slf = MORPHO_GETDICTIONARY(self);\n\n    morpho_printf(v, \"{ \");\n    unsigned int k=0;\n    for (unsigned int i=0; i<slf->dict.capacity; i++) {\n        if (!MORPHO_ISNIL(slf->dict.contents[i].key)) {\n            if (k>0) morpho_printf(v, \" , \");\n            morpho_printvalue(v, slf->dict.contents[i].key);\n            morpho_printf(v, \" : \");\n            morpho_printvalue(v, slf->dict.contents[i].val);\n            k++;\n        }\n    }\n    morpho_printf(v, \" }\");\n\n    return MORPHO_NIL;\n}\n\n/** Counts number of items in dictionary */\nvalue Dictionary_count(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(slf->dict.count);\n}\n\n/** Enumerate protocol */\nvalue Dictionary_enumerate(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<0) out=MORPHO_INTEGER(slf->dict.count);\n        else out=dictionary_iterate(slf, n);\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n    return out;\n}\n\n/** Gets a list of keys */\nvalue Dictionary_keys(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    objectlist *list = object_newlist(slf->dict.count, NULL);\n    value out=MORPHO_NIL;\n\n    if (list) {\n        for (unsigned int i=0; i<slf->dict.capacity; i++) {\n            if (!MORPHO_ISNIL(slf->dict.contents[i].key)) {\n                list_append(list, slf->dict.contents[i].key);\n            }\n        }\n        out=MORPHO_OBJECT(list);\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\n/** Clones a dictionary */\nvalue Dictionary_clone(vm *v, int nargs, value *args) {\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args));\n    objectdictionary *new = object_newdictionary();\n    if (!new) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    value out=MORPHO_OBJECT(new);\n\n    dictionary_copy(&slf->dict, &new->dict);\n    morpho_bindobjects(v, 1, &out);\n\n    return out;\n}\n\n#define DICTIONARY_SETOP(op) \\\nvalue Dictionary_##op(vm *v, int nargs, value *args) { \\\n    objectdictionary *slf = MORPHO_GETDICTIONARY(MORPHO_SELF(args)); \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (nargs>0 && MORPHO_ISDICTIONARY(MORPHO_GETARG(args, 0))) { \\\n        objectdictionary *new = object_newdictionary(); \\\n        \\\n        if (new) { \\\n            objectdictionary *b =MORPHO_GETDICTIONARY(MORPHO_GETARG(args, 0)); \\\n            dictionary_##op(&slf->dict, &b->dict, &new->dict); \\\n            out=MORPHO_OBJECT(new); \\\n            morpho_bindobjects(v, 1, &out); \\\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); \\\n    } else morpho_runtimeerror(v, DICT_DCTSTARG); \\\n    \\\n    return out; \\\n}\n\nDICTIONARY_SETOP(union)\nDICTIONARY_SETOP(intersection)\nDICTIONARY_SETOP(difference)\n\nMORPHO_BEGINCLASS(Dictionary)\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Dictionary_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Dictionary_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CONTAINS_METHOD, Dictionary_contains, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(DICTIONARY_REMOVE_METHOD, Dictionary_remove, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(DICTIONARY_CLEAR_METHOD, Dictionary_clear, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Dictionary_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Dictionary_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Dictionary_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(DICTIONARY_KEYS_METHOD, Dictionary_keys, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Dictionary_clone, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_UNION_METHOD, Dictionary_union, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INTERSECTION_METHOD, Dictionary_intersection, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIFFERENCE_METHOD, Dictionary_difference, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADD_METHOD, Dictionary_union, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUB_METHOD, Dictionary_difference, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectdictionarytype;\n\nvoid dict_initialize(void) {\n    // Create dictionary object type\n    objectdictionarytype=object_addtype(&objectdictionarydefn);\n    \n    // Locate the Object class to use as the parent class of Dictionary\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Dictionary constructor function\n    morpho_addfunction(DICTIONARY_CLASSNAME, DICTIONARY_CLASSNAME \" (...)\", dictionary_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Create dictionary veneer class\n    value dictionaryclass=builtin_addclass(DICTIONARY_CLASSNAME, MORPHO_GETCLASSDEFINITION(Dictionary), objclass);\n    object_setveneerclass(OBJECT_DICTIONARY, dictionaryclass);\n    \n    // Dictionary error messages\n    morpho_defineerror(DICT_DCTKYNTFND, ERROR_HALT, DICT_DCTKYNTFND_MSG);\n    morpho_defineerror(DICT_DCTSTARG, ERROR_HALT, DICT_DCTSTARG_MSG);\n}\n"
  },
  {
    "path": "src/classes/dict.h",
    "content": "/** @file dict.h\n *  @author T J Atherton\n *\n *  @brief Defines dictionary object type and Dictionary veneer class\n */\n\n#ifndef dict_h\n#define dict_h\n\n#include \"object.h\"\n#include \"dictionary.h\"\n\n/* -------------------------------------------------------\n * Dictionary objects\n * ------------------------------------------------------- */\n\nextern objecttype objectdictionarytype;\n#define OBJECT_DICTIONARY objectdictionarytype\n\ntypedef struct {\n    object obj;\n    dictionary dict;\n} objectdictionary;\n\n/** Tests whether an object is a dictionary */\n#define MORPHO_ISDICTIONARY(val) object_istype(val, OBJECT_DICTIONARY)\n\n/** Gets the object as a dictionary */\n#define MORPHO_GETDICTIONARY(val)   ((objectdictionary *) MORPHO_GETOBJECT(val))\n\n/** Gets the object's underlying dictionary structure */\n#define MORPHO_GETDICTIONARYSTRUCT(val)   (&(((objectdictionary *) MORPHO_GETOBJECT(val))->dict))\n\nobjectdictionary *object_newdictionary(void);\n\n/* -------------------------------------------------------\n * Dictionary veneer class\n * ------------------------------------------------------- */\n\n#define DICTIONARY_CLASSNAME              \"Dictionary\"\n\n#define DICTIONARY_KEYS_METHOD            \"keys\"\n#define DICTIONARY_CONTAINS_METHOD        \"contains\"\n#define DICTIONARY_REMOVE_METHOD          \"remove\"\n#define DICTIONARY_CLEAR_METHOD           \"clear\"\n\n/* -------------------------------------------------------\n * Dictionary error messages\n * ------------------------------------------------------- */\n\n#define DICT_DCTKYNTFND                   \"DctKyNtFnd\"\n#define DICT_DCTKYNTFND_MSG               \"Key not found in dictionary.\"\n\n#define DICT_DCTSTARG                     \"DctStArg\"\n#define DICT_DCTSTARG_MSG                 \"Dictionary set methods (union, intersection, difference) expect a dictionary as the argument.\"\n\n/* -------------------------------------------------------\n * Dictionary interface\n * ------------------------------------------------------- */\n\nvoid dict_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/err.c",
    "content": "/** @file err.c\n *  @author T J Atherton\n *\n *  @brief Implements the Error class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\nstatic value error_tagproperty;\nstatic value error_messageproperty;\nstatic value error_errtag;\n\n/* **********************************************************************\n * Error class\n * ********************************************************************** */\n\n/** Initializer\n * In: 1. Error tag\n *   2. Default error message\n */\nvalue Error_init(vm *v, int nargs, value *args) {\n\n    if ((nargs==2) &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 1))) {\n\n        objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), error_tagproperty, MORPHO_GETARG(args, 0));\n        objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), error_messageproperty, MORPHO_GETARG(args, 1));\n\n    } else MORPHO_RAISE(v, ERROR_ARGS);\n\n    return MORPHO_NIL;\n}\n\n/** Extract the tag and message for an error */\nbool _err_extract(vm *v, int nargs, value *args, value *tag, value *msg) {\n    // If an instance, extract the tag and message from the object\n    if (MORPHO_ISINSTANCE(MORPHO_SELF(args))) {\n        objectinstance *slf = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n        \n        objectinstance_getpropertyinterned(slf, error_tagproperty, tag);\n        if (nargs==0) objectinstance_getpropertyinterned(slf, error_messageproperty, msg);\n    }\n    \n    if (nargs==1) {\n        if (!MORPHO_ISSTRING(*tag)) *tag = builtin_internsymbolascstring(ERROR_ERROR);\n        *msg=MORPHO_GETARG(args, 0);\n    } else if (nargs==2) {\n        *tag=MORPHO_GETARG(args, 0);\n        *msg=MORPHO_GETARG(args, 1);\n    }\n    \n    return (MORPHO_ISSTRING(*tag) && MORPHO_ISSTRING(*msg));\n}\n\n/** Throw an error */\nvalue Error_throw(vm *v, int nargs, value *args) {\n    value tag=MORPHO_NIL, msg=MORPHO_NIL;\n\n    if (_err_extract(v, nargs, args, &tag, &msg)) {\n        error err;\n        error_init(&err);\n        morpho_writeusererror(&err, MORPHO_GETCSTRING(tag), MORPHO_GETCSTRING(msg));\n        morpho_error(v, &err);\n        error_clear(&err);\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Raise a warning */\nvalue Error_warning(vm *v, int nargs, value *args) {\n    value tag=MORPHO_NIL, msg=MORPHO_NIL;\n\n    if (_err_extract(v, nargs, args, &tag, &msg)) {\n        error err;\n        error_init(&err);\n        morpho_writeusererror(&err, MORPHO_GETCSTRING(tag), MORPHO_GETCSTRING(msg));\n        morpho_warning(v, &err);\n        error_clear(&err);\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Print errors */\nvalue Error_print(vm *v, int nargs, value *args) {\n    morpho_printvalue(v, MORPHO_SELF(args));\n    \n    return MORPHO_SELF(args);\n}\n\nMORPHO_BEGINCLASS(Error)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, Error_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_THROW_METHOD, Error_throw, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_WARNING_METHOD, Error_warning, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Error_print, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nvoid err_initialize(void) {\n    // Locate the Object class to use as the parent class of Error\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Create Error class\n    builtin_addclass(ERROR_CLASSNAME, MORPHO_GETCLASSDEFINITION(Error), objclass);\n    \n    // Create labels for Error property names\n    error_tagproperty=builtin_internsymbolascstring(ERROR_TAG_PROPERTY);\n    error_messageproperty=builtin_internsymbolascstring(ERROR_MESSAGE_PROPERTY);\n    error_errtag=builtin_internsymbolascstring(ERROR_ERROR);\n    \n    // Error error messages\n    morpho_defineerror(ERROR_ARGS, ERROR_HALT, ERROR_ARGS_MSG);\n}\n"
  },
  {
    "path": "src/classes/err.h",
    "content": "/** @file err.h\n *  @author T J Atherton\n *\n *  @brief Defines Error veneer class\n */\n\n#ifndef err_h\n#define err_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Error class\n * ------------------------------------------------------- */\n\n#define ERROR_CLASSNAME                   \"Error\"\n\n#define ERROR_TAG_PROPERTY                \"tag\"\n#define ERROR_MESSAGE_PROPERTY            \"message\"\n\n/* -------------------------------------------------------\n * Error error messages\n * ------------------------------------------------------- */\n\n#define ERROR_ARGS                        \"ErrorArgs\"\n#define ERROR_ARGS_MSG                    \"Error much be called with a tag and a default message as arguments.\"\n\n/* -------------------------------------------------------\n * Error interface\n * ------------------------------------------------------- */\n\nvoid err_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/file.c",
    "content": "/** @file file.c\n *  @author T J Atherton\n *\n *  @brief Defines file object type as well as File and Folder classes\n */\n\n#include <stdio.h>\n#include <limits.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"file.h\"\n#include \"platform.h\"\n\n/** Store the current working directory (relative to the filing systems cwd) */\nstatic varray_char workingdir;\n\n/* **********************************************************************\n * File objects\n * ********************************************************************** */\n\nobjecttype objectfiletype;\n\n/** File object definitions */\nsize_t objectfile_sizefn(object *obj) {\n    return sizeof(objectfile);\n}\n\nvoid objectfile_markfn(object *obj, void *v) {\n    objectfile *file = (objectfile *) obj;\n    morpho_markvalue(v, file->filename);\n}\n\nvoid objectfile_freefn(object *obj) {\n    objectfile *file = (objectfile *) obj;\n    if (file->f) fclose(file->f);\n}\n\nvoid objectfile_printfn(object *obj, void *v) {\n    objectfile *file = (objectfile *) obj;\n    morpho_printf(v, \"<File '\");\n    morpho_printvalue(v, file->filename);\n    morpho_printf(v, \"'>\");\n}\n\nobjecttypedefn objectfiledefn = {\n    .printfn=objectfile_printfn,\n    .markfn=objectfile_markfn,\n    .freefn=objectfile_freefn,\n    .sizefn=objectfile_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Creates a file object */\nobjectfile *object_newfile(value filename, FILE *f) {\n    objectfile *new = (objectfile *) object_new(sizeof(objectfile), OBJECT_FILE);\n    \n    if (new) {\n        new->filename=filename;\n        new->f=f;\n    }\n    \n    return new;\n}\n\n/* **********************************************************************\n * File handling utility functions\n * ********************************************************************** */\n\n#include <errno.h>\n\n/* Determine the size of a file */\nbool file_getsize(FILE *f, size_t *s) {\n    long int curr, size;\n    curr=ftell(f);\n    if (fseek(f, 0L, SEEK_END)!=0) return false;\n    size = ftell(f);\n    if (fseek(f, curr, SEEK_SET)!=0) return false;\n    if (s) *s = size;\n    return true;\n}\n\n/* Gets the current file handle */\nFILE *file_getfile(value obj) {\n    if (MORPHO_ISFILE(obj)) return MORPHO_GETFILE(obj)->f;\n    return NULL;\n}\n\n/** Sets the current file handle */\nvoid file_setfile(value obj, FILE *f) {\n    if (MORPHO_ISFILE(obj)) MORPHO_GETFILE(obj)->f=f;\n}\n\n/** Sets the global current working directory (relative to the filing systems cwd.\n * @param[in] path - path to working directory. Any file name at the end is stripped */\nvoid file_setworkingdirectory(const char *path) {\n    int length = (int) strlen(path);\n    int dirmarker = 0;\n    \n    /* Working backwards, find the last directory separator */\n    for ( dirmarker=length; dirmarker>=0; dirmarker--) {\n        if (path[dirmarker]=='\\\\' || path[dirmarker]=='/') break;\n    }\n    \n    workingdir.count=0; /* Clear any prior working directory */\n    if (dirmarker>0) {\n        varray_charadd(&workingdir, (char *) path, dirmarker);\n        varray_charwrite(&workingdir, '\\0');\n    }\n}\n\n/** Gets the relative path for a given file */\nvoid file_relativepath(const char *fname, varray_char *name) {\n    /* Check the fname passed isn't a global file reference (i.e. starts with / or ~) */\n    if (fname[0]!='~' && fname[0]!='/' && \n        !(fname[0]!='\\0' && fname[1]==':')) {\n        if (workingdir.count>0) {\n            for (unsigned int i=0; i<workingdir.count && workingdir.data[i]!='\\0'; i++) {\n                varray_charwrite(name, workingdir.data[i]);\n            }\n            varray_charwrite(name, '/');\n        }\n    }\n    varray_charadd(name, (char *) fname, (int) strlen(fname));\n    varray_charwrite(name, '\\0'); // Ensure string is null terminated.\n}\n\n/** Open a file relative to the current working directory (usually the script directory) */\nFILE *file_openrelative(const char *fname, const char *mode) {\n    varray_char name;\n    varray_charinit(&name);\n    \n    file_relativepath(fname, &name);\n    \n    FILE *out=fopen(name.data, mode);\n    \n    varray_charclear(&name);\n    \n    return out;\n}\n\n/** Reads a line using a given buffer */\nint file_readlineintovarray(FILE *f, varray_char *string) {\n    int ic;\n    \n    for (ic=getc(f); ic!=EOF && ic!='\\n'; ic=getc(f)) {\n        varray_charwrite(string, (char) ic);\n    }\n    \n    if (ic!=EOF || string->count>0) {\n        varray_charwrite(string, '\\0');\n    }\n    \n    return ic;\n}\n\n/** Reads a whole file into a buffer */\nbool file_readintovarray(FILE *f, varray_char *string) {\n    size_t size;\n    \n    if (!file_getsize(f, &size)) return false;\n    if (size>INT_MAX) return false;\n    \n    if (varray_charresize(string, (int) size+1)) {\n        size_t nread=fread(string->data, sizeof(char), size, f);\n        string->data[nread]='\\0';\n    }\n    \n    return true;\n}\n\n/** Reads a line using a given buffer */\nvalue file_readlineusingvarray(FILE *f, varray_char *string) {\n    int ic=file_readlineintovarray(f, string);\n    \n    if (ic!=EOF || string->count>0) {\n        return object_stringfromvarraychar(string);\n    }\n    \n    return MORPHO_NIL;\n}\n\n/* **********************************************************************\n * File class\n * ********************************************************************** */\n\n/** File constructor\n * In: 1. a file name\n *   2. (optional) a string giving the requested status, e.g. \"wr+\"\n */\nvalue file_constructor(vm *v, int nargs, value *args) {\n    objectfile *new=NULL;\n    value out=MORPHO_NIL;\n    value filename=MORPHO_NIL;\n    char *fname=NULL;\n    char *cmode = \"r\";\n    \n    if (nargs>0) {\n        if (MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n            fname=MORPHO_GETCSTRING(MORPHO_GETARG(args, 0));\n        } else MORPHO_RAISE(v, FILE_FILENAMEARG);\n        \n        if (nargs>1) {\n            if (MORPHO_ISSTRING(MORPHO_GETARG(args, 1))) {\n                char *mode=MORPHO_GETCSTRING(MORPHO_GETARG(args, 1));\n                switch (mode[0]) {\n                    case 'r': cmode=\"r\"; break;\n                    case 'w': cmode=\"w\"; break;\n                    case 'a': cmode=\"a\"; break;\n                    default: MORPHO_RAISE(v, FILE_MODE);\n                }\n            } else MORPHO_RAISE(v, FILE_MODE);\n        }\n    } else MORPHO_RAISE(v, FILE_NEEDSFILENAME);\n    \n    if (fname) {\n        FILE *f = file_openrelative(fname, cmode);\n        if (!f) MORPHO_RAISEVARGS(v, FILE_OPENFAILED, fname);\n        \n        filename = object_stringfromcstring(fname, strlen(fname));\n        new = object_newfile(filename, f);\n    }\n    \n    if (new) {\n        out=MORPHO_OBJECT(new);\n        value bind[] = { filename, out };\n        morpho_bindobjects(v, 2, bind);\n    }\n    \n    return out;\n}\n\n/** Close a file  */\nvalue File_close(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    if (f) {\n        fclose(f);\n        file_setfile(MORPHO_SELF(args), NULL);\n    }\n    \n    return MORPHO_NIL;\n}\n\n/** Get the contents of a file as an array */\nvalue File_lines(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (f) {\n        varray_value lines;\n        varray_valueinit(&lines);\n        \n        varray_char string;\n        varray_charinit(&string);\n        \n        do {\n            value line = file_readlineusingvarray(f, &string);\n            if (!MORPHO_ISNIL(line)) varray_valuewrite(&lines, line);\n            string.count=0;\n        } while (!feof(f));\n        \n        varray_charclear(&string);\n        \n        out=MORPHO_OBJECT(object_arrayfromvarrayvalue(&lines));\n        \n        varray_valuewrite(&lines, out); // Tuck onto end of lines to bind all at once\n        morpho_bindobjects(v, lines.count, lines.data);\n        varray_valueclear(&lines);\n    }\n    \n    return out;\n}\n\n/** Reads the whole file into a string */\nvalue File_readall(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    size_t size;\n    value out = MORPHO_NIL;\n    if (f && file_getsize(f, &size)) {\n        objectstring *new=object_stringwithsize(size);\n        if (new) {\n            size_t nbytes = fread(new->string, sizeof(char), size, f);\n            new->length=nbytes;\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n    }\n    return out;\n}\n\n/** Read a line  */\nvalue File_readline(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    \n    if (f) {\n        varray_char string;\n        varray_charinit(&string);\n        \n        out = file_readlineusingvarray(f, &string);\n        morpho_bindobjects(v, 1, &out);\n        varray_charclear(&string);\n    }\n    return out;\n}\n\n/** Reads a single character  */\nvalue File_readchar(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    if (f) {\n        int ic=getc(f);\n        if (ic!=EOF) {\n            char c=(char) ic;\n            value out=object_stringfromcstring(&c, 1);\n            morpho_bindobjects(v, 1, &out);\n            return out;\n        }\n    }\n    return MORPHO_NIL;\n}\n\n/** Write to a file  */\nvalue File_write(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    if (f) {\n        for (unsigned int i=0; i<nargs; i++) {\n            if (MORPHO_ISSTRING(MORPHO_GETARG(args, i))) {\n                char *line = MORPHO_GETCSTRING(MORPHO_GETARG(args, i));\n                if (fputs(line, f)==EOF) MORPHO_RAISE(v, FILE_WRITEFAIL);\n                if (fputc('\\n', f)==EOF) MORPHO_RAISE(v, FILE_WRITEFAIL);\n            } else MORPHO_RAISE(v, FILE_WRITEARGS);\n        }\n    }\n    \n    return MORPHO_NIL;\n}\n\n/** Get the path of a file relative to the CWD */\nvalue File_relativepath(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    value fname = MORPHO_GETFILE(MORPHO_SELF(args))->filename;\n    varray_char path;\n    varray_charinit(&path);\n    \n    if (MORPHO_ISSTRING(fname)) {\n        file_relativepath(MORPHO_GETCSTRING(fname), &path);\n        \n        out = object_stringfromvarraychar(&path);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    varray_charclear(&path);\n    return out;\n}\n\n/** Get the filename */\nvalue File_filename(vm *v, int nargs, value *args) {\n    return MORPHO_GETFILE(MORPHO_SELF(args))->filename;\n}\n\n/** Detects whether we're at the end of the file  */\nvalue File_eof(vm *v, int nargs, value *args) {\n    FILE *f=file_getfile(MORPHO_SELF(args));\n    if (f && feof(f)) return MORPHO_TRUE;\n    return MORPHO_FALSE;\n}\n\nMORPHO_BEGINCLASS(File)\nMORPHO_METHOD(FILE_CLOSE, File_close, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_LINES, File_lines, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_READALL, File_readall, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_READLINE, File_readline, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_READCHAR, File_readchar, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_WRITE, File_write, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_RELATIVEPATH, File_relativepath, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_FILENAME, File_filename, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FILE_EOF, File_eof, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Folder objects\n * ********************************************************************** */\n\n/** Detect whether a resource is a folder  */\nvalue Folder_isfolder(vm *v, int nargs, value *args) {\n    value ret = MORPHO_FALSE;\n    if (nargs==1 && MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        varray_char name;\n        varray_charinit(&name);\n        file_relativepath(MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)), &name);\n        \n        if (platform_isdirectory(name.data)) ret=MORPHO_TRUE;\n        \n        varray_charclear(&name);\n    } else morpho_runtimeerror(v, FOLDER_EXPCTPATH);\n    \n    return ret;\n}\n\n/** Return the contents of a folder  */\nvalue Folder_contents(vm *v, int nargs, value *args) {\n    value ret = MORPHO_NIL;\n    if (nargs==1 && MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        varray_char name;\n        varray_charinit(&name);\n        file_relativepath(MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)), &name);\n\n        size_t size = platform_maxpathsize();\n        char buffer[size];\n        \n        MorphoDirContents contents;\n        if (platform_directorycontentsinit(&contents, name.data)) {\n            varray_value list;\n            varray_valueinit(&list);\n            \n            while (platform_directorycontents(&contents, buffer, size)) {\n                value entry = object_stringfromcstring(buffer, strlen(buffer));\n                if (MORPHO_ISSTRING(entry)) varray_valuewrite(&list, entry);\n            };\n            \n            platform_directorycontentsclear(&contents);\n            \n            objectlist *clist = object_newlist(list.count, list.data);\n            if (clist) {\n                ret = MORPHO_OBJECT(clist);\n                varray_valuewrite(&list, ret);\n                morpho_bindobjects(v, list.count, list.data);\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n            \n            varray_valueclear(&list);\n        } else morpho_runtimeerror(v, FOLDER_NTFLDR);\n        \n        varray_charclear(&name);\n    } else morpho_runtimeerror(v, FOLDER_EXPCTPATH);\n    \n    return ret;\n}\n\nMORPHO_BEGINCLASS(Folder)\nMORPHO_METHOD(FOLDER_ISFOLDER, Folder_isfolder, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FOLDER_CONTENTS, Folder_contents, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nvoid file_initialize(void) {\n    varray_charinit(&workingdir);\n    \n    objectfiletype=object_addtype(&objectfiledefn);\n    \n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    morpho_addfunction(FILE_CLASSNAME, FILE_CLASSNAME \" (...)\", file_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    value fileclass=builtin_addclass(FILE_CLASSNAME, MORPHO_GETCLASSDEFINITION(File), objclass);\n    object_setveneerclass(OBJECT_FILE, fileclass);\n    \n    builtin_addclass(FOLDER_CLASSNAME, MORPHO_GETCLASSDEFINITION(Folder), MORPHO_NIL);\n    \n    morpho_defineerror(FILE_OPENFAILED, ERROR_HALT, FILE_OPENFAILED_MSG);\n    morpho_defineerror(FILE_NEEDSFILENAME, ERROR_HALT, FILE_NEEDSFILENAME_MSG);\n    morpho_defineerror(FILE_FILENAMEARG, ERROR_HALT, FILE_FILENAMEARG_MSG);\n    morpho_defineerror(FILE_MODE, ERROR_HALT, FILE_MODE_MSG);\n    morpho_defineerror(FILE_WRITEARGS, ERROR_HALT, FILE_WRITEARGS_MSG);\n    morpho_defineerror(FILE_WRITEFAIL, ERROR_HALT, FILE_WRITEFAIL_MSG);\n    \n    morpho_defineerror(FOLDER_EXPCTPATH, ERROR_HALT, FOLDER_EXPCTPATH_MSG);\n    morpho_defineerror(FOLDER_NTFLDR, ERROR_HALT, FOLDER_NTFLDR_MSG);\n    \n    morpho_addfinalizefn(file_finalize);\n}\n\nvoid file_finalize(void) {\n    varray_charclear(&workingdir);\n}\n"
  },
  {
    "path": "src/classes/file.h",
    "content": "/** @file file.h\n *  @author T J Atherton\n *\n *  @brief Defines file object type as well as File and Folder classes\n */\n\n#ifndef file_h\n#define file_h\n\n#include <stdio.h>\n#include \"object.h\"\n#include \"morpho.h\"\n\n/* -------------------------------------------------------\n * File objects\n * ------------------------------------------------------- */\n\nextern objecttype objectfiletype;\n#define OBJECT_FILE objectfiletype\n\ntypedef struct {\n    object obj;\n    value filename;\n    FILE *f;\n} objectfile;\n\n/** Tests whether an object is a file */\n#define MORPHO_ISFILE(val) object_istype(val, OBJECT_FILE)\n\n/** Gets the object as an matrix */\n#define MORPHO_GETFILE(val)   ((objectfile *) MORPHO_GETOBJECT(val))\n\n/* -------------------------------------------------------\n * File class\n * ------------------------------------------------------- */\n\n#define FILE_CLASSNAME    \"File\"\n\n#define FILE_CLOSE        \"close\"\n#define FILE_LINES        \"lines\"\n#define FILE_READALL      \"readall\"\n#define FILE_READLINE     \"readline\"\n#define FILE_READCHAR     \"readchar\"\n#define FILE_WRITE        \"write\"\n#define FILE_EOF          \"eof\"\n#define FILE_RELATIVEPATH \"relativepath\"\n#define FILE_FILENAME     \"filename\"\n\n#define FILE_READMODE     \"read\"\n#define FILE_WRITEMODE    \"write\"\n#define FILE_APPENDMODE   \"append\"\n\n/* -------------------------------------------------------\n * Folder class\n * ------------------------------------------------------- */\n\n#define FOLDER_CLASSNAME  \"Folder\"\n\n#define FOLDER_ISFOLDER   \"isfolder\"\n#define FOLDER_CONTENTS   \"contents\"\n\n/* -------------------------------------------------------\n * File error messages\n * ------------------------------------------------------- */\n\n#define FILE_OPENFAILED                   \"FlOpnFld\"\n#define FILE_OPENFAILED_MSG               \"Couldn't open file '%s'.\"\n\n#define FILE_FILENAMEARG                  \"FlNmArgs\"\n#define FILE_FILENAMEARG_MSG              \"First argument to File must be a filename.\"\n\n#define FILE_NEEDSFILENAME                \"FlNmMssng\"\n#define FILE_NEEDSFILENAME_MSG            \"Filename missing.\"\n\n#define FILE_MODE                         \"FlMode\"\n#define FILE_MODE_MSG                     \"Second argument to File should be 'read', 'write' or 'append'.\"\n\n#define FILE_WRITEARGS                    \"FlWrtArgs\"\n#define FILE_WRITEARGS_MSG                \"Arguments to File.write must be strings.\"\n\n#define FILE_WRITEFAIL                    \"FlWrtFld\"\n#define FILE_WRITEFAIL_MSG                \"Write to file failed.\"\n\n#define FOLDER_EXPCTPATH                  \"FldrExpctPth\"\n#define FOLDER_EXPCTPATH_MSG              \"Folder methods expect a path as an argument.\"\n\n#define FOLDER_NTFLDR                     \"NtFldr\"\n#define FOLDER_NTFLDR_MSG                 \"Not a folder.\"\n\n/* -------------------------------------------------------\n * File interface\n * ------------------------------------------------------- */\n\nbool file_getsize(FILE *f, size_t *s);\nvoid file_setworkingdirectory(const char *script);\nFILE *file_openrelative(const char *fname, const char *mode);\nint file_readlineintovarray(FILE *f, varray_char *string);\nbool file_readintovarray(FILE *f, varray_char *string);\n\n/* Initialization/finalization */\nvoid file_initialize(void);\nvoid file_finalize(void);\n\n#endif /* file_h */\n"
  },
  {
    "path": "src/classes/flt.c",
    "content": "/** @file flt.c\n *  @author T J Atherton\n *\n *  @brief Veneer class for float values\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"format.h\"\n\nvalue Value_format(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n\n    if (nargs==1 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        varray_char str;\n        varray_charinit(&str);\n        \n        if (format_printtobuffer(MORPHO_SELF(args),\n                            MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)),\n                                 &str)) {\n            \n            out = object_stringfromvarraychar(&str);\n            if (MORPHO_ISOBJECT(out)) morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, VALUE_INVLDFRMT);\n        \n        varray_charclear(&str);\n    } else {\n        morpho_runtimeerror(v, VALUE_FRMTARG);\n    }\n    \n    return out;\n}\n\n/* **********************************************************************\n * Float veneer class\n * ********************************************************************** */\n\nMORPHO_BEGINCLASS(Float)\nMORPHO_METHOD(MORPHO_CLASS_METHOD, Object_class, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_RESPONDSTO_METHOD, Object_respondsto, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INVOKE_METHOD, Object_invoke, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, MORPHO_FN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_FORMAT_METHOD, Value_format, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nvoid float_initialize(void) {\n    // Create Float veneer class\n    value floatclass=builtin_addclass(FLOAT_CLASSNAME, MORPHO_GETCLASSDEFINITION(Float), MORPHO_NIL);\n    value_setveneerclass(MORPHO_FLOAT(0.0), floatclass);\n    \n    morpho_defineerror(VALUE_FRMTARG, ERROR_HALT, VALUE_FRMTARG_MSG);\n    morpho_defineerror(VALUE_INVLDFRMT, ERROR_HALT, VALUE_INVLDFRMT_MSG);\n}\n"
  },
  {
    "path": "src/classes/flt.h",
    "content": "/** @file flt.h\n *  @author T J Atherton\n *\n *  @brief Veneer class for float values\n */\n\n#ifndef float_h\n#define float_h\n\n/* -------------------------------------------------------\n * Float veneer class\n * ------------------------------------------------------- */\n\n#define FLOAT_CLASSNAME \"Float\"\n\n/* -------------------------------------------------------\n * Float error messages\n * ------------------------------------------------------- */\n\n#define VALUE_FRMTARG                     \"FrmtArg\"\n#define VALUE_FRMTARG_MSG                 \"Format method requires a format string as its argument.\"\n\n#define VALUE_INVLDFRMT                   \"InvldFrmt\"\n#define VALUE_INVLDFRMT_MSG               \"Invalid format string.\"\n\n/* -------------------------------------------------------\n * Float interface\n * ------------------------------------------------------- */\n\n/** Method for format strings */\nvalue Value_format(vm *v, int nargs, value *args);\n\nvoid float_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/function.c",
    "content": "/** @file function.c\n *  @author T J Atherton\n *\n *  @brief Implement objectfunctions and the Function veneer class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * objectfunction definitions\n * ********************************************************************** */\n\nvoid objectfunction_freefn(object *obj) {\n    objectfunction *func = (objectfunction *) obj;\n    morpho_freeobject(func->name);\n    varray_optionalparamclear(&func->opt);\n    object_functionclear(func);\n    signature_clear(&func->sig);\n}\n\nvoid objectfunction_markfn(object *obj, void *v) {\n    objectfunction *f = (objectfunction *) obj;\n    morpho_markvalue(v, f->name);\n    morpho_markvarrayvalue(v, &f->konst);\n}\n\nsize_t objectfunction_sizefn(object *obj) {\n    return sizeof(objectfunction);\n}\n\nvoid objectfunction_printfn(object *obj, void *v) {\n    objectfunction *f = (objectfunction *) obj;\n    if (f) morpho_printf(v, \"<fn %s>\", (MORPHO_ISNIL(f->name) ? \"\" : MORPHO_GETCSTRING(f->name)));\n}\n\nobjecttypedefn objectfunctiondefn = {\n    .printfn=objectfunction_printfn,\n    .markfn=objectfunction_markfn,\n    .freefn=objectfunction_freefn,\n    .sizefn=objectfunction_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * objectfunction utility functions\n * ********************************************************************** */\n\n/** @brief Initializes a new function */\nvoid object_functioninit(objectfunction *func) {\n    func->entry=0;\n    func->name=MORPHO_NIL;\n    func->nargs=0;\n    func->nopt=0;\n    func->parent=NULL;\n    func->creg=-1;\n    func->nregs=0;\n    varray_valueinit(&func->konst);\n    varray_varray_upvalueinit(&func->prototype);\n    signature_init(&func->sig);\n}\n\n/** @brief Clears a function */\nvoid object_functionclear(objectfunction *func) {\n    varray_valueclear(&func->konst);\n    /** Clear the upvalue prototypes */\n    for (unsigned int i=0; i<func->prototype.count; i++) {\n        varray_upvalueclear(&func->prototype.data[i]);\n    }\n    varray_varray_upvalueclear(&func->prototype);\n    signature_clear(&func->sig);\n}\n\n/** @brief Creates a new function */\nobjectfunction *object_newfunction(indx entry, value name, objectfunction *parent, unsigned int nargs) {\n    objectfunction *new = (objectfunction *) object_new(sizeof(objectfunction), OBJECT_FUNCTION);\n\n    if (new) {\n        object_functioninit(new);\n        new->entry=entry;\n        new->name=object_clonestring(name);\n        new->nargs=nargs;\n        new->varg=-1; // No vargs\n        new->parent=parent;\n        new->klass=NULL;\n        varray_optionalparaminit(&new->opt);\n    }\n\n    return new;\n}\n\n/** Gets the parent of a function */\nobjectfunction *object_getfunctionparent(objectfunction *func) {\n    return func->parent;\n}\n\n/** Gets the name of a function */\nvalue object_getfunctionname(objectfunction *func) {\n    return func->name;\n}\n\n/** Gets the constant table associated with a function */\nvarray_value *object_functiongetconstanttable(objectfunction *func) {\n    if (func) {\n        return &func->konst;\n    }\n    return NULL;\n}\n\n/** Adds an upvalue prototype to a function\n * @param[in]  func   function object to add to\n * @param[in]  v      a varray of upvalues that will be copied into the function\n *                    definition.\n * @param[out] ix     index of the closure created\n * @returns true on success */\nbool object_functionaddprototype(objectfunction *func, varray_upvalue *v, indx *ix) {\n    bool success=false;\n    varray_upvalue new;\n    varray_upvalueinit(&new);\n    success=varray_upvalueadd(&new, v->data, v->count);\n    if (success) varray_varray_upvalueadd(&func->prototype, &new, 1);\n    if (success && ix) *ix = (indx) func->prototype.count-1;\n    return success;\n}\n\n/** Returns the number of positional arguments (including a variadic arg if any) */\nint function_countpositionalargs(objectfunction *func) {\n    return func->nargs + (func->varg>=0 ? 1 : 0);\n}\n\n/** Returns the number of optional arguments */\nint function_countoptionalargs(objectfunction *func) {\n    return func->nopt;\n}\n\n/** Does a function have variadic args? */\nbool function_hasvargs(objectfunction *func) {\n    return (func->varg>=0);\n}\n\n/** Sets the parameter number of a variadic argument */\nvoid function_setvarg(objectfunction *func, int varg) {\n    func->varg=varg;\n}\n\n/** Sets that a function must be enclosed */\nvoid function_setclosure(objectfunction *func, int creg) {\n    func->creg=creg;\n}\n\n/** Checks if a function is enclosed */\nbool function_isclosure(objectfunction *func) {\n    return (func->creg>=0);\n}\n\n/** Sets the signature of a function\n * @param[in]  func   function object\n * @param[in]  signature list of types for each parameter (length from func->nargs) */\nvoid function_setsignature(objectfunction *func, value *signature) {\n    signature_set(&func->sig, function_countpositionalargs(func), signature);\n}\n\n/** Returns true if any of the parameters are typed */\nbool function_hastypedparameters(objectfunction *func) {\n    return signature_istyped(&func->sig);\n}\n\n/* **********************************************************************\n * Function veneer class\n * ********************************************************************** */\n\nvalue Function_tostring(vm *v, int nargs, value *args) {\n    objectfunction *func=MORPHO_GETFUNCTION(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    varray_char buffer;\n    varray_charinit(&buffer);\n\n    varray_charadd(&buffer, \"<fn \", 4);\n    morpho_printtobuffer(v, func->name, &buffer);\n    varray_charwrite(&buffer, '>');\n\n    out = object_stringfromvarraychar(&buffer);\n    if (MORPHO_ISSTRING(out)) {\n        morpho_bindobjects(v, 1, &out);\n    }\n    varray_charclear(&buffer);\n\n    return out;\n}\n\nMORPHO_BEGINCLASS(Function)\nMORPHO_METHOD(MORPHO_TOSTRING_METHOD, Function_tostring, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectfunctiontype;\n\nvoid function_initialize(void) {\n    // Create function object type\n    objectfunctiontype=object_addtype(&objectfunctiondefn);\n    \n    // Locate the Object class to use as the parent class of Function\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Create function veneer class\n    value functionclass=builtin_addclass(FUNCTION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Function), objclass);\n    object_setveneerclass(OBJECT_FUNCTION, functionclass);\n    \n    // No constructor as objectfunctions are generated by the compiler\n    \n    // Function error messages\n}\n"
  },
  {
    "path": "src/classes/function.h",
    "content": "/** @file function.h\n *  @author T J Atherton\n *\n *  @brief Defines function object type and Function veneer class\n */\n\n#ifndef function_h\n#define function_h\n\n#include \"object.h\"\n#include \"signature.h\"\n\n/* -------------------------------------------------------\n * Function objects\n * ------------------------------------------------------- */\n\nextern objecttype objectfunctiontype;\n#define OBJECT_FUNCTION objectfunctiontype\n\ntypedef struct {\n    value symbol; /** Symbol associated with the variable */\n    indx def; /** Default value as constant */\n    indx reg; /** Associated register */\n} optionalparam;\n\nDECLARE_VARRAY(optionalparam, optionalparam)\n\n/** A function object */\ntypedef struct sobjectfunction {\n    object obj;\n    int nargs; // Number of positional parameters\n    int nopt; // Number of optional parameters\n    int varg; // The parameter number of a variadic parameter. TODO: Rationalize\n    value name;\n    indx entry;\n    int creg; // Closure register\n    struct sobjectfunction *parent;\n    int nregs;\n    objectclass *klass; // Parent class for methods\n    varray_value konst;\n    varray_varray_upvalue prototype;\n    varray_optionalparam opt;\n    signature sig;\n} objectfunction;\n\n/** Gets an objectfunction from a value */\n#define MORPHO_GETFUNCTION(val)   ((objectfunction *) MORPHO_GETOBJECT(val))\n\n/** Tests whether an object is a function */\n#define MORPHO_ISFUNCTION(val) object_istype(val, OBJECT_FUNCTION)\n\n/* -------------------------------------------------------\n * Function veneer class\n * ------------------------------------------------------- */\n\n#define FUNCTION_CLASSNAME \"Function\"\n\n/* -------------------------------------------------------\n * Function error messages\n * ------------------------------------------------------- */\n\n/* -------------------------------------------------------\n * Function interface\n * ------------------------------------------------------- */\n\nvoid object_functioninit(objectfunction *func);\nvoid object_functionclear(objectfunction *func);\nbool object_functionaddprototype(objectfunction *func, varray_upvalue *v, indx *ix);\nobjectfunction *object_getfunctionparent(objectfunction *func);\nvalue object_getfunctionname(objectfunction *func);\nvarray_value *object_functiongetconstanttable(objectfunction *func);\nobjectfunction *object_newfunction(indx entry, value name, objectfunction *parent, unsigned int nargs);\nint function_countpositionalargs(objectfunction *func);\nint function_countoptionalargs(objectfunction *func);\nbool function_hasvargs(objectfunction *func);\nvoid function_setvarg(objectfunction *func, int varg);\nvoid function_setclosure(objectfunction *func, int creg);\nbool function_isclosure(objectfunction *func);\n\nvoid function_setsignature(objectfunction *func, value *signature);\nbool function_hastypedparameters(objectfunction *func);\n\nvoid objectfunction_printfn(object *obj, void *v);\n\nvoid function_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/instance.c",
    "content": "/** @file instance.c\n *  @author T J Atherton\n *\n *  @brief Implements objectinstance and the Object base class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * objectinstance definitions\n * ********************************************************************** */\n\n/** Instance object definitions */\nvoid objectinstance_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<%s>\", MORPHO_GETCSTRING(((objectinstance *) obj)->klass->name));\n}\n\nvoid objectinstance_markfn(object *obj, void *v) {\n    objectinstance *c = (objectinstance *) obj;\n    morpho_markdictionary(v, &c->fields);\n}\n\nvoid objectinstance_freefn(object *obj) {\n    objectinstance *instance = (objectinstance *) obj;\n    dictionary_clear(&instance->fields);\n}\n\nsize_t objectinstance_sizefn(object *obj) {\n    return sizeof(objectinstance);\n}\n\nobjecttypedefn objectinstancedefn = {\n    .printfn=objectinstance_printfn,\n    .markfn=objectinstance_markfn,\n    .freefn=objectinstance_freefn,\n    .sizefn=objectinstance_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Create an instance */\nobjectinstance *object_newinstance(objectclass *klass) {\n    objectinstance *new= (objectinstance *) object_new(sizeof(objectinstance), OBJECT_INSTANCE);\n\n    if (new) {\n        new->klass=klass;\n        dictionary_init(&new->fields);\n    }\n\n    return new;\n}\n\n/* **********************************************************************\n * objectinstance utility functions\n * ********************************************************************** */\n\n/* @brief Inserts a value into a property\n * @param obj   the object\n * @param key   key to use @warning: This MUST have been previously interned into a symboltable\n *                                   e.g. with builtin_internsymbol\n * @param val   value to use\n * @returns true on success  */\nbool objectinstance_setproperty(objectinstance *obj, value key, value val) {\n    return dictionary_insertintern(&obj->fields, key, val);\n}\n\n/* @brief Gets a value into a property\n * @param obj   the object\n * @param key   key to use\n * @param[out] val   stores the value\n * @returns true on success  */\nbool objectinstance_getproperty(objectinstance *obj, value key, value *val) {\n    return dictionary_get(&obj->fields, key, val);\n}\n\n/* @brief Interned property lookup\n * @param obj   the object\n * @param key   key to use @warning: This MUST have been previously interned into a symboltable\n *                                   e.g. with builtin_internsymbol\n * @param[out] val   stores the value\n * @returns true on success  */\nbool objectinstance_getpropertyinterned(objectinstance *obj, value key, value *val) {\n    return dictionary_getintern(&obj->fields, key, val);\n}\n\n/* **********************************************************************\n * Object veneer class\n * ********************************************************************** */\n\n/** Sets an object property */\nvalue Object_getindex(vm *v, int nargs, value *args) {\n    value self=MORPHO_SELF(args);\n    value out=MORPHO_NIL;\n    \n    if (MORPHO_ISINSTANCE(self)) {\n        if (nargs==1 &&\n            MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n            if (!dictionary_get(&MORPHO_GETINSTANCE(self)->fields, MORPHO_GETARG(args, 0), &out)) {\n                morpho_runtimeerror(v, VM_OBJECTLACKSPROPERTY, MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)));\n            }\n        } else morpho_runtimeerror(v, GETINDEX_ARGS);\n    } else morpho_runtimeerror(v, OBJECT_NOPRP);\n\n    return out;\n}\n\n/** Gets an object property */\nvalue Object_setindex(vm *v, int nargs, value *args) {\n    value self=MORPHO_SELF(args);\n\n    if (MORPHO_ISINSTANCE(self)) {\n        if (nargs==2 &&\n            MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n            dictionary_insert(&MORPHO_GETINSTANCE(self)->fields, MORPHO_GETARG(args, 0), MORPHO_GETARG(args, 1));\n        } else morpho_runtimeerror(v, SETINDEX_ARGS);\n    } else morpho_runtimeerror(v, OBJECT_NOPRP);\n\n    return MORPHO_NIL;\n}\n\n/** Find the object's class */\nvalue Object_class(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    value out = MORPHO_NIL;\n\n    objectclass *klass=morpho_lookupclass(self);\n    if (klass) out = MORPHO_OBJECT(klass);\n    \n    return out;\n}\n\n/** Find the object's superclass */\nvalue Object_super(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    \n    objectclass *klass=morpho_lookupclass(self);\n    \n    return (klass && klass->superclass ? MORPHO_OBJECT(klass->superclass) : MORPHO_NIL);\n}\n\n/** Checks if an object responds to a method */\nvalue Object_respondsto(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    \n    objectclass *klass=morpho_lookupclass(self);\n\n    if (nargs == 0) {\n        value out = MORPHO_NIL;\n        objectlist *new = object_newlist(0, NULL);\n        if (new) {\n            list_resize(new, klass->methods.count);\n            for (unsigned int i=0; i<klass->methods.capacity; i++) {\n                if (MORPHO_ISSTRING(klass->methods.contents[i].key)) {\n                    list_append(new, klass->methods.contents[i].key);\n                }\n            }\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        return out;\n\n    } else if (nargs==1 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        return MORPHO_BOOL(dictionary_get(&klass->methods, MORPHO_GETARG(args, 0), NULL));\n    } else MORPHO_RAISE(v, RESPONDSTO_ARG);\n\n    return MORPHO_FALSE;\n}\n\n/** Checks if an object has a property */\nvalue Object_has(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISINSTANCE(self)) return MORPHO_FALSE;\n\n    if (nargs == 0) {\n        value out = MORPHO_NIL;\n        objectlist *new = object_newlist(0, NULL);\n        if (new) {\n            objectinstance *slf = MORPHO_GETINSTANCE(self);\n            list_resize(new, MORPHO_GETINSTANCE(self)->fields.count);\n            for (unsigned int i=0; i<slf->fields.capacity; i++) {\n                if (MORPHO_ISSTRING(slf->fields.contents[i].key)) {\n                    list_append(new, slf->fields.contents[i].key);\n                }\n            }\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        return out;\n\n    } else if (nargs==1 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        return MORPHO_BOOL(dictionary_get(&MORPHO_GETINSTANCE(self)->fields, MORPHO_GETARG(args, 0), NULL));\n        \n    } else MORPHO_RAISE(v, HAS_ARG);\n    \n    return MORPHO_FALSE;\n}\n\n/** Invoke a method */\nvalue Object_invoke(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    value out=MORPHO_NIL;\n\n    objectclass *klass=morpho_lookupclass(self);\n    \n    if (klass && nargs>0 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        value fn;\n        if (dictionary_get(&klass->methods, MORPHO_GETARG(args, 0), &fn)) {\n            morpho_invoke(v, self, fn, nargs-1, &MORPHO_GETARG(args, 1), &out);\n        } else morpho_runtimeerror(v, VM_OBJECTLACKSPROPERTY, MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)));\n    } else morpho_runtimeerror(v, VM_INVALIDARGS, 1, 0);\n\n    return out;\n}\n\n/** Generic print */\nvalue Object_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    objectclass *klass=NULL;\n    if (MORPHO_ISCLASS(self)) {\n        klass=MORPHO_GETCLASS(self);\n        morpho_printf(v, \"@%s\", (MORPHO_ISSTRING(klass->name) ? MORPHO_GETCSTRING(klass->name): \"Object\"));\n    } else if (MORPHO_ISINSTANCE(self)) {\n        klass=MORPHO_GETINSTANCE(self)->klass;\n        if (klass) morpho_printf(v, \"<%s>\", (MORPHO_ISSTRING(klass->name) ? MORPHO_GETCSTRING(klass->name): \"Object\") );\n    } else {\n        morpho_printvalue(v, self);\n    }\n    return MORPHO_NIL;\n}\n\n/** Count number of properties */\nvalue Object_count(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n\n    if (MORPHO_ISINSTANCE(self)) {\n        objectinstance *obj = MORPHO_GETINSTANCE(self);\n        return MORPHO_INTEGER(obj->fields.count);\n    } else if (MORPHO_ISCLASS(self)) {\n        return MORPHO_INTEGER(0);\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Enumerate protocol */\nvalue Object_enumerate(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (MORPHO_ISINSTANCE(self)) {\n            dictionary *dict= &MORPHO_GETINSTANCE(self)->fields;\n\n            if (n<0) {\n                out=MORPHO_INTEGER(dict->count);\n            } else if (n<dict->count) {\n                unsigned int k=0;\n                for (unsigned int i=0; i<dict->capacity; i++) {\n                    if (!MORPHO_ISNIL(dict->contents[i].key)) {\n                        if (k==n) return dict->contents[i].key;\n                        k++;\n                    }\n                }\n            } else morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n        } else if (MORPHO_ISCLASS(self)) {\n            if (n<0) out = MORPHO_INTEGER(0);\n        }\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n     return out;\n}\n\n/** Generic serializer */\nvalue Object_serialize(vm *v, int nargs, value *args) {\n    return MORPHO_NIL;\n}\n\n/** Generic clone */\nvalue Object_clone(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    value out = MORPHO_NIL;\n\n    if (MORPHO_ISINSTANCE(self)) {\n        objectinstance *instance = MORPHO_GETINSTANCE(self);\n        objectinstance *new = object_newinstance(instance->klass);\n        if (new) {\n            dictionary_copy(&instance->fields, &new->fields);\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n    } else {\n        morpho_runtimeerror(v, OBJECT_CANTCLONE);\n    }\n\n    return out;\n}\n\nvalue Object_linearization(vm *v, int nargs, value *args) {\n    value slf=MORPHO_SELF(args);\n    objectclass *klass=NULL;\n    \n    if (MORPHO_ISCLASS(slf)) klass=MORPHO_GETCLASS(slf);\n    else if (MORPHO_ISINSTANCE(slf)) klass=MORPHO_GETINSTANCE(slf)->klass;\n    else return MORPHO_NIL;\n    \n    value out = MORPHO_NIL;\n    \n    objectlist *new = object_newlist(klass->linearization.count, klass->linearization.data);\n    if (new) {\n        out = MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\nMORPHO_BEGINCLASS(Object)\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Object_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Object_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLASS_METHOD, Object_class, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUPER_METHOD, Object_super, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_RESPONDSTO_METHOD, Object_respondsto, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_HAS_METHOD, Object_has, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INVOKE_METHOD, Object_invoke, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Object_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Object_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SERIALIZE_METHOD, Object_serialize, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Object_clone, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_LINEARIZATION_METHOD, Object_linearization, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectinstancetype;\n\nvoid instance_initialize(void) {\n    // Create instance object type\n    objectinstancetype=object_addtype(&objectinstancedefn);\n    \n    // Create Object and set as base class\n    value objclass=builtin_addclass(OBJECT_CLASSNAME, MORPHO_GETCLASSDEFINITION(Object), MORPHO_NIL);\n    morpho_setbaseclass(objclass);\n    \n    // Object error messages\n    morpho_defineerror(OBJECT_CANTCLONE, ERROR_HALT, OBJECT_CANTCLONE_MSG);\n    morpho_defineerror(OBJECT_IMMUTABLE, ERROR_HALT, OBJECT_IMMUTABLE_MSG);\n    morpho_defineerror(OBJECT_NOPRP, ERROR_HALT, OBJECT_NOPRP_MSG);\n    morpho_defineerror(ENUMERATE_ARGS, ERROR_HALT, ENUMERATE_ARGS_MSG);\n    morpho_defineerror(GETINDEX_ARGS, ERROR_HALT, GETINDEX_ARGS_MSG);\n    morpho_defineerror(SETINDEX_ARGS, ERROR_HALT, SETINDEX_ARGS_MSG);\n    morpho_defineerror(RESPONDSTO_ARG, ERROR_HALT, RESPONDSTO_ARG_MSG);\n    morpho_defineerror(HAS_ARG, ERROR_HALT, HAS_ARG_MSG);\n    morpho_defineerror(ISMEMBER_ARG, ERROR_HALT, ISMEMBER_ARG_MSG);\n}\n"
  },
  {
    "path": "src/classes/instance.h",
    "content": "/** @file instance.h\n *  @author T J Atherton\n *\n *  @brief Defines instance object type and Object base class\n */\n\n#ifndef instance_h\n#define instance_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Instance objects\n * ------------------------------------------------------- */\n\nextern objecttype objectinstancetype;\n#define OBJECT_INSTANCE objectinstancetype\n\ntypedef struct {\n    object obj;\n    objectclass *klass;\n    dictionary fields;\n} objectinstance;\n\n/** Tests whether an object is a class */\n#define MORPHO_ISINSTANCE(val) object_istype(val, OBJECT_INSTANCE)\n\n/** Gets the object as a class */\n#define MORPHO_GETINSTANCE(val)   ((objectinstance *) MORPHO_GETOBJECT(val))\n\nobjectinstance *object_newinstance(objectclass *klass);\n\n/* -------------------------------------------------------\n * Object veneer class\n * ------------------------------------------------------- */\n\n#define OBJECT_CLASSNAME \"Object\"\n\n/* -------------------------------------------------------\n * Instance error messages\n * ------------------------------------------------------- */\n\n#define OBJECT_CANTCLONE                  \"ObjCantClone\"\n#define OBJECT_CANTCLONE_MSG              \"Cannot clone this object.\"\n\n#define OBJECT_IMMUTABLE                  \"ObjImmutable\"\n#define OBJECT_IMMUTABLE_MSG              \"Cannot modify this object.\"\n\n#define OBJECT_NOPRP                      \"ObjNoPrp\"\n#define OBJECT_NOPRP_MSG                  \"Object does not provide properties.\"\n\n#define GETINDEX_ARGS                     \"IndxArgs\"\n#define GETINDEX_ARGS_MSG                 \"Index method expects a String property name as its argument.\"\n\n#define SETINDEX_ARGS                     \"SetIndxArgs\"\n#define SETINDEX_ARGS_MSG                 \"Setindex method expects an index and a value as arguments.\"\n\n#define ENUMERATE_ARGS                    \"EnmrtArgs\"\n#define ENUMERATE_ARGS_MSG                \"Enumerate method expects a single integer argument.\"\n\n#define RESPONDSTO_ARG                    \"RspndsToArg\"\n#define RESPONDSTO_ARG_MSG                \"Method respondsto expects a single string argument or no argrument.\"\n\n#define HAS_ARG                           \"HasArg\"\n#define HAS_ARG_MSG                       \"Method has expects a single string argument or no argument.\"\n\n#define ISMEMBER_ARG                      \"IsMmbrArg\"\n#define ISMEMBER_ARG_MSG                  \"Method ismember expects a single argument.\"\n\n/* -------------------------------------------------------\n * Instance interface\n * ------------------------------------------------------- */\n\n/* Expose a few methods for veneer classes */\nvalue Object_class(vm *v, int nargs, value *args);\nvalue Object_respondsto(vm *v, int nargs, value *args);\nvalue Object_print(vm *v, int nargs, value *args);\nvalue Object_invoke(vm *v, int nargs, value *args);\n\nbool objectinstance_setproperty(objectinstance *obj, value key, value val);\nbool objectinstance_getproperty(objectinstance *obj, value key, value *val);\nbool objectinstance_getpropertyinterned(objectinstance *obj, value key, value *val);\n\nvoid instance_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/int.c",
    "content": "/** @file int.c\n *  @author T J Atherton\n *\n *  @brief Veneer class for float values\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * int utility functions\n * ********************************************************************** */\n\n/* **********************************************************************\n * Int veneer class\n * ********************************************************************** */\n\nMORPHO_BEGINCLASS(Int)\nMORPHO_METHOD(MORPHO_CLASS_METHOD, Object_class, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_RESPONDSTO_METHOD, Object_respondsto, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INVOKE_METHOD, Object_invoke, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, MORPHO_FN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_FORMAT_METHOD, Value_format, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nvoid int_initialize(void) {\n    // Create Int veneer class\n    value intclass=builtin_addclass(INT_CLASSNAME, MORPHO_GETCLASSDEFINITION(Int), MORPHO_NIL);\n    value_setveneerclass(MORPHO_INTEGER(1), intclass);\n}\n"
  },
  {
    "path": "src/classes/int.h",
    "content": "/** @file int.h\n *  @author T J Atherton\n *\n *  @brief Veneer class for integer values\n */\n\n#ifndef int_h\n#define int_h\n\n/* -------------------------------------------------------\n * Int veneer class\n * ------------------------------------------------------- */\n\n#define INT_CLASSNAME \"Int\"\n\n/* -------------------------------------------------------\n * Int error messages\n * ------------------------------------------------------- */\n\n/* -------------------------------------------------------\n * Int interface\n * ------------------------------------------------------- */\n\nvoid int_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/invocation.c",
    "content": "/** @file invocation.c\n *  @author T J Atherton\n *\n *  @brief Implements the Invocation class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * objectinvocation definitions\n * ********************************************************************** */\n\n/** Invocation object definitions */\nvoid objectinvocation_printfn(object *obj, void *v) {\n    objectinvocation *c = (objectinvocation *) obj;\n    morpho_printvalue(v, c->receiver);\n    morpho_printf(v, \".\");\n    morpho_printvalue(v, c->method);\n}\n\nvoid objectinvocation_markfn(object *obj, void *v) {\n    objectinvocation *c = (objectinvocation *) obj;\n    morpho_markvalue(v, c->receiver);\n    morpho_markvalue(v, c->method);\n}\n\nsize_t objectinvocation_sizefn(object *obj) {\n    return sizeof(objectinvocation);\n}\n\nobjecttypedefn objectinvocationdefn = {\n    .printfn=objectinvocation_printfn,\n    .markfn=objectinvocation_markfn,\n    .freefn=NULL,\n    .sizefn=objectinvocation_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * objectinvocation utility functions\n * ********************************************************************** */\n\n/** Create a new invocation */\nobjectinvocation *object_newinvocation(value receiver, value method) {\n    objectinvocation *new = (objectinvocation *) object_new(sizeof(objectinvocation), OBJECT_INVOCATION);\n\n    if (new) {\n        new->receiver=receiver;\n        new->method=method;\n    }\n\n    return new;\n}\n\n/* **********************************************************************\n * Invocation veneer class\n * ********************************************************************** */\n\n/** Creates a new invocation object */\nvalue invocation_constructor(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n\n    if (nargs==2) {\n        value receiver = MORPHO_GETARG(args, 0);\n        value selector = MORPHO_GETARG(args, 1);\n        \n        if (!MORPHO_ISOBJECT(receiver) || !MORPHO_ISSTRING(selector)) {\n            morpho_runtimeerror(v, INVOCATION_ARGS);\n            return MORPHO_NIL;\n        }\n        \n        value method = MORPHO_NIL;\n        \n        objectclass *klass=morpho_lookupclass(receiver);\n        \n        if (dictionary_get(&klass->methods, selector, &method)) {\n            objectinvocation *new = object_newinvocation(receiver, method);\n\n            if (new) {\n                out = MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        }\n        \n    } else morpho_runtimeerror(v, INVOCATION_ARGS);\n\n    return out;\n}\n\n/** Converts to a string for string interpolation */\nvalue Invocation_tostring(vm *v, int nargs, value *args) {\n    objectinvocation *inv=MORPHO_GETINVOCATION(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    varray_char buffer;\n    varray_charinit(&buffer);\n    \n    morpho_printtobuffer(v, inv->receiver, &buffer);\n    varray_charwrite(&buffer, '.');\n    morpho_printtobuffer(v, inv->method, &buffer);\n    \n    out = object_stringfromvarraychar(&buffer);\n    if (MORPHO_ISSTRING(out)) {\n        morpho_bindobjects(v, 1, &out);\n    }\n    varray_charclear(&buffer);\n\n    return out;\n}\n\n/** Clones a range */\nvalue Invocation_clone(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    value out = MORPHO_NIL;\n    if (!MORPHO_ISINVOCATION(self)) return MORPHO_NIL;\n    \n    objectinvocation *slf = MORPHO_GETINVOCATION(self);\n    objectinvocation *new = object_newinvocation(slf->receiver, slf->method);\n    \n    if (new) {\n        out = MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    \n    return out;\n}\n\nMORPHO_BEGINCLASS(Invocation)\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_TOSTRING_METHOD, Invocation_tostring, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Invocation_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectinvocationtype;\n\nvoid invocation_initialize(void) {\n    // Create invocation object type\n    objectinvocationtype=object_addtype(&objectinvocationdefn);\n    \n    // Locate the Object class to use as the parent class of Invocation\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Invocation constructor function\n    morpho_addfunction(INVOCATION_CLASSNAME, INVOCATION_CLASSNAME \" (...)\", invocation_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Create invocation veneer class\n    value invocationclass=builtin_addclass(INVOCATION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Invocation), objclass);\n    object_setveneerclass(OBJECT_INVOCATION, invocationclass);\n    \n    // Invocation error messages\n    morpho_defineerror(INVOCATION_ARGS, ERROR_HALT, INVOCATION_ARGS_MSG);\n    morpho_defineerror(INVOCATION_METHOD, ERROR_HALT, INVOCATION_METHOD_MSG);\n}\n"
  },
  {
    "path": "src/classes/invocation.h",
    "content": "/** @file invocation.h\n *  @author T J Atherton\n *\n *  @brief Defines invocation object type and Invocation class\n */\n\n/** Invocations (or bound methods) are created by the runtime when a method is accessed from an object without a call, e.g.\n    \n        var inv = Object.clone\n \n    An invocation can then be called at a later point, and behaves as other callable objects like functions\n \n        var a = inv()\n*/\n\n#ifndef invocation_h\n#define invocation_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Invocation objects\n * ------------------------------------------------------- */\n\nextern objecttype objectinvocationtype;\n#define OBJECT_INVOCATION objectinvocationtype\n\ntypedef struct {\n    object obj;\n    value receiver;\n    value method;\n} objectinvocation;\n\n/** Tests whether an object is an invocation */\n#define MORPHO_ISINVOCATION(val) object_istype(val, OBJECT_INVOCATION)\n\n/** Gets the object as an invocation */\n#define MORPHO_GETINVOCATION(val)   ((objectinvocation *) MORPHO_GETOBJECT(val))\n\nobjectinvocation *object_newinvocation(value receiver, value method);\n\n/* -------------------------------------------------------\n * Invocation veneer class\n * ------------------------------------------------------- */\n\n#define INVOCATION_CLASSNAME              \"Invocation\"\n\n/* -------------------------------------------------------\n * Invocation error messages\n * ------------------------------------------------------- */\n\n#define INVOCATION_ARGS                   \"InvocationArgs\"\n#define INVOCATION_ARGS_MSG               \"Invocation must be called with an object and a method name as arguments.\"\n\n#define INVOCATION_METHOD                 \"InvocationMethod\"\n#define INVOCATION_METHOD_MSG             \"Method not found.\"\n\n/* -------------------------------------------------------\n * Invocation interface\n * ------------------------------------------------------- */\n\nvoid invocation_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/json.c",
    "content": "/** @file json.c\n *  @author T J Atherton\n *\n *  @brief JSON class\n *  @details Aims to be compliant with RFC 8259, tested against https://github.com/nst/JSONTestSuite\n *           Currently passes all except \"n_multidigit_number_then_00.json\"\n */\n\n#include <ctype.h>\n#include \"morpho.h\"\n#include \"classes.h\"\n\n#include \"common.h\"\n#include \"parse.h\"\n#include \"dictionary.h\"\n#include \"json.h\"\n\n/* **********************************************************************\n * JSON lexer\n * ********************************************************************** */\n\n/* -------------------------------------------------------\n * JSON token types and process functions\n * ------------------------------------------------------- */\n\nbool json_lexstring(lexer *l, token *tok, error *err);\nbool json_lexnumber(lexer *l, token *tok, error *err);\n\nenum {\n    JSON_LEFTCURLYBRACE,\n    JSON_RIGHTCURLYBRACE,\n    JSON_LEFTSQUAREBRACE,\n    JSON_RIGHTSQUAREBRACE,\n    JSON_COMMA,\n    JSON_COLON,\n    JSON_MINUS,\n    JSON_QUOTE,\n    JSON_TRUE,\n    JSON_FALSE,\n    JSON_NULL,\n    JSON_STRING,\n    JSON_NUMBER,\n    JSON_FLOAT,\n    JSON_EOF\n};\n\ntokendefn jsontokens[] = {\n    { \"{\",          JSON_LEFTCURLYBRACE         , NULL },\n    { \"}\",          JSON_RIGHTCURLYBRACE        , NULL },\n    { \"[\",          JSON_LEFTSQUAREBRACE        , NULL },\n    { \"]\",          JSON_RIGHTSQUAREBRACE       , NULL },\n    { \",\",          JSON_COMMA                  , NULL },\n    { \":\",          JSON_COLON                  , NULL },\n    { \"-\",          JSON_MINUS                  , json_lexnumber },\n    { \"\\\"\",         JSON_QUOTE                  , json_lexstring },\n    { \"true\",       JSON_TRUE                   , NULL },\n    { \"false\",      JSON_FALSE                  , NULL },\n    { \"null\",       JSON_NULL                   , NULL },\n    { \"\",           TOKEN_NONE                  , NULL }\n};\n\n/** Skip over JSON whitespace */\nbool json_lexwhitespace(lexer *l, token *tok, error *err) {\n    for (;;) {\n        char c = lex_peek(l);\n        \n        switch (c) {\n            case '\\n':\n                lex_newline(l); // V Intentional fallthrough\n            case ' ':\n            case '\\t':\n            case '\\r':\n                lex_advance(l);\n                break;\n            default:\n                return true;\n        }\n    }\n    return true;\n}\n\n/** Record JSON strings as a token */\nbool json_lexstring(lexer *l, token *tok, error *err) {\n    while (lex_peek(l) != '\"' && !lex_isatend(l)) {\n        if (lex_peek(l)=='\\\\') lex_advance(l); // Detect an escaped character\n\n        lex_advance(l);\n    }\n    \n    if (lex_isatend(l)) {\n        morpho_writeerrorwithid(err, LEXER_UNTERMINATEDSTRING, NULL, tok->line, tok->posn);\n        return false;\n    }\n    \n    lex_advance(l); // Advance over final quote\n    lex_recordtoken(l, JSON_STRING, tok);\n    \n    return true;\n}\n\n/** Record JSON numbers as a token */\nbool json_lexnumber(lexer *l, token *tok, error *err) {\n    bool hasexp=false;\n    tokentype type = JSON_NUMBER;\n    \n    // Detect if we are missing digits (ie an isolated '-')\n    char c = lex_peek(l);\n    if (c=='0') {\n        lex_advance(l);\n        if (lex_isdigit(lex_peek(l))) goto json_lexnumberinvld; // Cannot follow '0' by digits.\n    }\n    \n    if (lex_isdigit(c)) {\n        // Advance through initial digits\n        while (lex_isdigit(lex_peek(l)) && !lex_isatend(l)) lex_advance(l);\n    } else goto json_lexnumberinvld;\n    \n    // Detect fractional separator\n    if (lex_peek(l)=='.') {\n        lex_advance(l);\n        type = JSON_FLOAT;\n        \n        // Digits are required after fractional separator\n        if (!lex_isdigit(lex_peek(l))) goto json_lexnumberinvld;\n        while (lex_isdigit(lex_peek(l)) && !lex_isatend(l)) lex_advance(l);\n    };\n    \n    if (lex_peek(l)=='e' || lex_peek(l)=='E') {\n        lex_advance(l); hasexp=true; type = JSON_FLOAT;\n    }\n    if (lex_peek(l)=='+' || lex_peek(l)=='-') {\n        if (hasexp) lex_advance(l); else goto json_lexnumberinvld; // Only allow +/- after exp\n    }\n    \n    // Digits are required after exponent\n    if (hasexp && !lex_isdigit(lex_peek(l))) goto json_lexnumberinvld;\n    while (lex_isdigit(lex_peek(l)) && !lex_isatend(l)) lex_advance(l);\n    \n    lex_recordtoken(l, type, tok);\n    \n    return true;\n\njson_lexnumberinvld:\n    morpho_writeerrorwithid(err, JSON_NMBRFRMT, NULL, tok->line, tok->posn);\n    return false;\n}\n\n/** Lexer token preprocessor function */\nbool json_lexpreprocess(lexer *l, token *tok, error *err) {\n    char c = lex_peek(l);\n    if (lex_isdigit(c)) return json_lexnumber(l, tok, err);\n    return false;\n}\n\n/* -------------------------------------------------------\n * Initialize a JSON lexer\n * ------------------------------------------------------- */\n\nvoid json_initializelexer(lexer *l, char *src) {\n    lex_init(l, src, 0);\n    lex_settokendefns(l, jsontokens);\n    lex_setprefn(l, json_lexpreprocess);\n    lex_setwhitespacefn(l, json_lexwhitespace);\n    lex_seteof(l, JSON_EOF);\n}\n\n/* **********************************************************************\n * JSON parser\n * ********************************************************************** */\n\n/* -------------------------------------------------------\n * Output structure\n * ------------------------------------------------------- */\n\n/** Type for output of the type function */\ntypedef struct {\n    value out;\n    varray_value *objects;\n} jsonoutput;\n\n/** Place a value into the opaque output structure */\nvoid json_setoutput(void *out, value v) {\n    jsonoutput *output = (jsonoutput *) out;\n    output->out=v;\n    if (output->objects && MORPHO_ISOBJECT(v)) varray_valuewrite(output->objects, v);\n}\n\n/** Retrieve output value from opaque output structure */\nvalue json_getoutput(jsonoutput *out) {\n    return out->out;\n}\n\n/** Initializes the output structure with an optional output varray.\n @warning: The output array must have been previously initialized */\nvoid json_outputinit(jsonoutput *out, varray_value *output) {\n    out->out=MORPHO_NIL;\n    out->objects=output;\n    if (output) out->objects->count=0;\n}\n\n/* -------------------------------------------------------\n * JSON parse functions\n * ------------------------------------------------------- */\n\nbool json_parsevalue(parser *p, void *out);\n\n/** Parses a string */\nbool json_parsestring(parser *p, void *out) {\n    bool success=false;\n    varray_char str;\n    varray_charinit(&str);\n    \n    const char *input = p->previous.start;\n    unsigned int length = p->previous.length;\n    \n    for (unsigned int i=1; i<length-1; i++) {\n        if (iscntrl((unsigned char) input[i]) && input[i]<='\\x1f') { // RFC 8259 mandates that ctrl characters are 0x00 - 0x1f\n            parse_error(p, true, PARSE_UNESCPDCTRL);\n            goto json_parsestring_cleanup;\n        } else if (input[i]!='\\\\') {\n            varray_charwrite(&str, input[i]);\n        } else {\n            i++;\n            switch (input[i]) {\n                case '\\\"': varray_charwrite(&str, '\\\"'); break;\n                case '\\\\': varray_charwrite(&str, '\\\\'); break;\n                case '/': varray_charwrite(&str, '/'); break;\n                case 'b': varray_charwrite(&str, '\\b'); break;\n                case 'f': varray_charwrite(&str, '\\f'); break;\n                case 'n': varray_charwrite(&str, '\\n'); break;\n                case 'r': varray_charwrite(&str, '\\r'); break;\n                case 't': varray_charwrite(&str, '\\t'); break;\n                case 'u':\n                {\n                    if (!parse_codepointfromhex(p, &input[i+1], 4, false, &str)) goto json_parsestring_cleanup;\n                    i+=4;\n                }\n                    break;\n                default:\n                    parse_error(p, true, PARSE_STRESC);\n                    goto json_parsestring_cleanup;\n            }\n        }\n    }\n    \n    value new = object_stringfromvarraychar(&str);\n    if (MORPHO_ISOBJECT(new)) {\n        success=true;\n        json_setoutput(out, new);\n    } else {\n        parse_error(p, true, ERROR_ALLOCATIONFAILED);\n    }\n    \njson_parsestring_cleanup:\n    varray_charclear(&str);\n    return success;\n}\n\n/** Parses an integer */\nbool json_parsenumber(parser *p, void *out) {\n    long f;\n    if (!parse_tokentointeger(p, &f)) return false;\n    \n    json_setoutput(out, MORPHO_INTEGER((int) f));\n    \n    return true;\n}\n\n/** Parses an floating point value */\nbool json_parsefloat(parser *p, void *out) {\n    double f;\n    if (!parse_tokentodouble(p, &f)) return false;\n    \n    json_setoutput(out, MORPHO_FLOAT((double) f));\n    \n    return true;\n}\n\n/** Parses the true identifier */\nbool json_parsetrue(parser *p, void *out) {\n    json_setoutput(out, MORPHO_TRUE);\n    return true;\n}\n\n/** Parses the false identifier */\nbool json_parsefalse(parser *p, void *out) {\n    json_setoutput(out, MORPHO_FALSE);\n    return true;\n}\n\n/** Parses the null identifier */\nbool json_parsenull(parser *p, void *out) {\n    json_setoutput(out, MORPHO_NIL);\n    return true;\n}\n\n/** Parse an 'object', which is really a dictionary */\nbool json_parseobject(parser *p, void *out) {\n    objectdictionary *new = object_newdictionary();\n    if (!new) {\n        parse_error(p, true, ERROR_ALLOCATIONFAILED);\n        return false;\n    }\n    \n    while (!parse_checktoken(p, JSON_RIGHTCURLYBRACE) &&\n           !parse_checktoken(p, JSON_EOF)) {\n        //value key=MORPHO_NIL, val=MORPHO_NIL;\n        jsonoutput key = *(jsonoutput *) out, val = *(jsonoutput *) out;\n        \n        /* Parse the key/value pair */\n        if (parse_checktoken(p, JSON_STRING)) {\n            if (!json_parsevalue(p, &key)) goto json_parseobjectcleanup;\n        } else {\n            parse_error(p, true, JSON_OBJCTKEY);\n            goto json_parseobjectcleanup;\n        }\n        \n        if (!parse_checkrequiredtoken(p, JSON_COLON, PARSE_DCTSPRTR)) goto json_parseobjectcleanup;\n        if (!json_parsevalue(p, &val)) goto json_parseobjectcleanup;\n        \n        dictionary_insert(&new->dict, json_getoutput(&key), json_getoutput(&val));\n        \n        if (!parse_checktoken(p, JSON_RIGHTCURLYBRACE)) {\n            if (!parse_checkrequiredtoken(p, JSON_COMMA, PARSE_DCTSPRTR)) goto json_parseobjectcleanup;\n            if (parse_checkdisallowedtoken(p, JSON_RIGHTCURLYBRACE, JSON_BLNKELMNT)) goto json_parseobjectcleanup;\n        }\n    }\n    \n    if (!parse_checkrequiredtoken(p, JSON_RIGHTCURLYBRACE, PARSE_DCTTRMNTR)) goto json_parseobjectcleanup;\n    \n    json_setoutput(out, MORPHO_OBJECT(new));\n    \n    return true;\n    \njson_parseobjectcleanup:\n    if (new) object_free((object *) new);\n    return false;\n}\n\n/** Parses an array, e.g. [ 1, 2, 3 ] */\nbool json_parsearray(parser *p, void *out) {\n    objectlist *new = object_newlist(0, NULL);\n    if (!new) {\n        parse_error(p, true, ERROR_ALLOCATIONFAILED);\n        return false;\n    }\n\n    while (!parse_checktoken(p, JSON_RIGHTSQUAREBRACE) &&\n           !parse_checktoken(p, JSON_EOF)) {\n        if (parse_checkdisallowedtoken(p, JSON_COMMA, JSON_BLNKELMNT)) goto json_parsearraycleanup;\n        jsonoutput v = *(jsonoutput *) out;\n        \n        if (json_parsevalue(p, &v)) {\n            list_append(new, json_getoutput(&v));\n        } else goto json_parsearraycleanup;\n        \n        if (!parse_checktoken(p, JSON_RIGHTSQUAREBRACE)) {\n            if (!parse_checkrequiredtoken(p, JSON_COMMA, PARSE_MSSNGCOMMA)) goto json_parsearraycleanup;\n            if (parse_checkdisallowedtoken(p, JSON_RIGHTSQUAREBRACE, JSON_BLNKELMNT)) goto json_parsearraycleanup;\n        }\n    }\n    \n    if (!parse_checkrequiredtoken(p, JSON_RIGHTSQUAREBRACE, PARSE_MSSNGSQBRC)) goto json_parsearraycleanup;\n    \n    json_setoutput(out, MORPHO_OBJECT(new));\n    return true;\n    \njson_parsearraycleanup:\n    if (new) object_free((object *) new);\n    return false;\n}\n\n/** Parses a json value using the parse table */\nbool json_parsevalue(parser *p, void *out) {\n    if (!parse_incrementrecursiondepth(p)) return false; // Increment and check\n    \n    bool success=parse_precedence(p, PREC_ASSIGN, out);\n    \n    parse_decrementrecursiondepth(p);\n    return success;\n}\n\n/** Base JSON parse type */\nbool json_parseelement(parser *p, void *out) {\n    if (parse_checkdisallowedtoken(p, JSON_EOF, JSON_BLNKELMNT)) return false;\n    \n    bool success=json_parsevalue(p, out);\n    \n    if (success && p->current.type!=JSON_EOF) {\n        parse_error(p, false, JSON_EXTRNSTOK);\n        return false;\n    }\n    return success;\n}\n\n/* -------------------------------------------------------\n * JSON parse table\n * ------------------------------------------------------- */\n\nparserule json_rules[] = {\n    PARSERULE_PREFIX(JSON_LEFTCURLYBRACE, json_parseobject),\n    PARSERULE_PREFIX(JSON_LEFTSQUAREBRACE, json_parsearray),\n    PARSERULE_PREFIX(JSON_STRING, json_parsestring),\n    PARSERULE_PREFIX(JSON_NUMBER, json_parsenumber),\n    PARSERULE_PREFIX(JSON_FLOAT, json_parsefloat),\n    PARSERULE_PREFIX(JSON_TRUE, json_parsetrue),\n    PARSERULE_PREFIX(JSON_FALSE, json_parsefalse),\n    PARSERULE_PREFIX(JSON_NULL, json_parsenull),\n    PARSERULE_UNUSED(TOKEN_NONE)\n};\n\n/* -------------------------------------------------------\n * Initialize a JSON parser\n * ------------------------------------------------------- */\n\n/** Initializes a parser to parse JSON */\nvoid json_initializeparser(parser *p, lexer *l, error *err, void *out) {\n    parse_init(p, l, err, out);\n    parse_setbaseparsefn(p, json_parseelement);\n    parse_setparsetable(p, json_rules);\n    parse_setskipnewline(p, false, TOKEN_NONE);\n}\n\n/* **********************************************************************\n * Interface to JSON parser\n * ********************************************************************** */\n\n/** @brief Parses JSON into a value.\n    @param[in] in - source string\n    @param[in] err - error block to fill out on failure\n    @param[out] out - value on succes\n    @param[out] objects - [optional] a varray filled out with all objects generated in parsing\n    @returns true on success, false otherwise */\nbool json_parse(char *in, error *err, value *out, varray_value *objects) {\n    varray_value obj;\n    varray_valueinit(&obj);\n    \n    jsonoutput output;\n    json_outputinit(&output, &obj);\n    \n    lexer l;\n    json_initializelexer(&l, in);\n\n    parser p;\n    json_initializeparser(&p, &l, err, &output);\n    \n    bool success=parse(&p);\n    \n    parse_clear(&p);\n    lex_clear(&l);\n    \n    if (success) {\n        *out = json_getoutput(&output);\n        if (objects) varray_valueadd(objects, obj.data, obj.count);\n    } else { // Free any objects that were already allocated\n        for (unsigned int i=0; i<obj.count; i++) morpho_freeobject(obj.data[i]);\n    }\n    \n    varray_valueclear(&obj);\n    \n    return success;\n}\n\n/* **********************************************************************\n * JSON output\n * ********************************************************************** */\n\nbool json_valuetovarraychar(vm *v, value in, varray_char *out) {\n    bool success=false;\n    if (MORPHO_ISINTEGER(in) ||\n        MORPHO_ISFLOAT(in) ||\n        MORPHO_ISBOOL(in)) {\n        success=morpho_printtobuffer(v, in, out);\n    } else if (MORPHO_ISNIL(in)) {\n        success=varray_charadd(out, JSON_NULL_LABEL, strlen(JSON_NULL_LABEL));\n    } else if (MORPHO_ISSTRING(in)) {\n        objectstring *str = MORPHO_GETSTRING(in);\n        \n        success=varray_charadd(out, \"\\\"\", 1);\n        \n        for (char *c = str->string; c<str->string+str->length; ) {\n            int nbytes = morpho_utf8numberofbytes(c);\n            if (!nbytes) return false;\n            \n            if (nbytes==1 && iscntrl((unsigned char) *c)) {\n                switch (*c) {\n                    case '\\b': success=varray_charadd(out, \"\\\\b\", 2); break;\n                    case '\\f': success=varray_charadd(out, \"\\\\f\", 2); break;\n                    case '\\n': success=varray_charadd(out, \"\\\\n\", 2); break;\n                    case '\\r': success=varray_charadd(out, \"\\\\r\", 2); break;\n                    case '\\t': success=varray_charadd(out, \"\\\\t\", 2); break;\n                    case '\\\\': success=varray_charadd(out, \"\\\\\\\\\", 2); break;\n                    default: {\n                        char temp[128];\n                        int n = snprintf(temp, 128, \"\\\\u%04x\", (int) *c);\n                        success=varray_charadd(out, temp, n);\n                    }\n                }\n            } else {\n                success=varray_charadd(out, c, nbytes);\n            }\n            \n            c+=nbytes;\n        }\n        \n        success=varray_charadd(out, \"\\\"\", 1);\n        \n    } else if (MORPHO_ISLIST(in)) {\n        objectlist *lst = MORPHO_GETLIST(in);\n        varray_charadd(out, \"[\", 1);\n        \n        for (int i=0; i<lst->val.count; i++)  {\n            if (!json_valuetovarraychar(v, lst->val.data[i], out)) return false;\n            if (i<lst->val.count-1) varray_charadd(out, \",\", 1);\n        }\n        \n        success=varray_charadd(out, \"]\", 1);\n    } else if (MORPHO_ISDICTIONARY(in)) {\n        objectdictionary *dict = MORPHO_GETDICTIONARY(in);\n        varray_charadd(out, \"{\", 1);\n        \n        for (unsigned int i=0, k=0; i<dict->dict.capacity; i++) {\n            value key = dict->dict.contents[i].key;\n            if (MORPHO_ISNIL(key)) continue;\n            \n            if (!MORPHO_ISSTRING(key)) success=varray_charadd(out, \"\\\"\", 1);\n            if (!json_valuetovarraychar(v, key, out)) return false;\n            if (!MORPHO_ISSTRING(key)) success=varray_charadd(out, \"\\\"\", 1);\n            \n            success=varray_charadd(out, \":\", 1);\n            \n            if (!json_valuetovarraychar(v, dict->dict.contents[i].val, out)) return false;\n            \n            if (k<dict->dict.count-1) varray_charadd(out, \",\", 1);\n            \n            k++;\n        }\n        \n        success=varray_charadd(out, \"}\", 1);\n    }\n    return success;\n}\n\nbool json_tostring(vm *v, value in, value *out) {\n    bool success=false;\n    varray_char str;\n    varray_charinit(&str);\n    \n    success=json_valuetovarraychar(v, in, &str);\n    \n    if (success) {\n        *out=object_stringfromvarraychar(&str);\n        if (!MORPHO_ISSTRING(*out)) {\n            morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n            success=false;\n        }\n    }\n    \n    varray_charclear(&str);\n    return success;\n}\n\n/* **********************************************************************\n * JSON class\n * ********************************************************************** */\n\nvalue JSON_parse(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    if (nargs==1 && MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        char *src = MORPHO_GETCSTRING(MORPHO_GETARG(args, 0));\n        varray_value objects;\n        varray_valueinit(&objects);\n        error err;\n        error_init(&err);\n        \n        if (json_parse(src, &err, &out, &objects)) {\n            morpho_bindobjects(v, objects.count, objects.data);\n        } else {\n            morpho_runtimeerror(v, err.id);\n        }\n        varray_valueclear(&objects);\n    } else morpho_runtimeerror(v, JSON_PRSARGS);\n    \n    return out;\n}\n\nvalue JSON_tostring(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    \n    if (nargs==1) {\n        if (json_tostring(v, MORPHO_GETARG(args, 0), &out)) {\n            morpho_bindobjects(v, 1, &out);\n        }\n    }\n    \n    return out;\n}\n\nMORPHO_BEGINCLASS(JSON)\nMORPHO_METHOD(JSON_PARSEMETHOD, JSON_parse, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_TOSTRING_METHOD, JSON_tostring, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization/finalization\n * ********************************************************************** */\n\nvoid json_initialize(void) {\n    // Locate the Object class to use as the parent class of JSON\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // JSON class\n    builtin_addclass(JSON_CLASSNAME, MORPHO_GETCLASSDEFINITION(JSON), objclass);\n    \n    morpho_defineerror(JSON_OBJCTKEY, ERROR_PARSE, JSON_OBJCTKEY_MSG);\n    morpho_defineerror(JSON_PRSARGS, ERROR_PARSE, JSON_PRSARGS_MSG);\n    morpho_defineerror(JSON_EXTRNSTOK, ERROR_PARSE, JSON_EXTRNSTOK_MSG);\n    morpho_defineerror(JSON_NMBRFRMT, ERROR_PARSE, JSON_NMBRFRMT_MSG);\n    morpho_defineerror(JSON_BLNKELMNT, ERROR_PARSE, JSON_BLNKELMNT_MSG);\n}\n"
  },
  {
    "path": "src/classes/json.h",
    "content": "/** @file json.h\n *  @author T J Atherton\n *\n *  @brief JSON parser\n */\n\n#ifndef json_h\n#define json_h\n\n#include \"object.h\"\n#include \"dictionary.h\"\n\n#define JSON_NULL_LABEL             \"null\"\n\n/* -------------------------------------------------------\n * JSON class\n * ------------------------------------------------------- */\n\n#define JSON_CLASSNAME              \"JSON\"\n\n#define JSON_PARSEMETHOD            \"parse\"\n\n/* -------------------------------------------------------\n * JSON error messages\n * ------------------------------------------------------- */\n\n#define JSON_OBJCTKEY                    \"JSONObjctKey\"\n#define JSON_OBJCTKEY_MSG                \"JSON object keys must be strings.\"\n\n#define JSON_EXTRNSTOK                   \"JSONExtrnsTkn\"\n#define JSON_EXTRNSTOK_MSG               \"Extraneous token after JSON element.\"\n\n#define JSON_PRSARGS                     \"JSONPrsArgs\"\n#define JSON_PRSARGS_MSG                 \"Method 'parse' requires a string as the argument.\"\n\n#define JSON_NMBRFRMT                    \"JSONNmbrFrmt\"\n#define JSON_NMBRFRMT_MSG                \"Improperly formatted number.\"\n\n#define JSON_BLNKELMNT                   \"JSONBlnkElmnt\"\n#define JSON_BLNKELMNT_MSG               \"Blank element.\"\n\n/* -------------------------------------------------------\n * JSON interface\n * ------------------------------------------------------- */\n\n/* Parse JSON into a value */\nbool json_parse(char *in, error *err, value *out, varray_value *objects);\n\nvoid json_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/list.c",
    "content": "/** @file list.c\n *  @author T J Atherton\n *\n *  @brief Implements the List class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * objectlist definitions\n * ********************************************************************** */\n\nvoid objectlist_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<List>\");\n}\n\nvoid objectlist_freefn(object *obj) {\n    objectlist *list = (objectlist *) obj;\n    varray_valueclear(&list->val);\n}\n\nvoid objectlist_markfn(object *obj, void *v) {\n    objectlist *c = (objectlist *) obj;\n    morpho_markvarrayvalue(v, &c->val);\n}\n\nsize_t objectlist_sizefn(object *obj) {\n    return sizeof(objectlist)+sizeof(value) *\n            ((objectlist *) obj)->val.capacity;\n}\n\nobjecttypedefn objectlistdefn = {\n    .printfn=objectlist_printfn,\n    .markfn=objectlist_markfn,\n    .freefn=objectlist_freefn,\n    .sizefn=objectlist_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Creates a new list */\nobjectlist *object_newlist(unsigned int nval, value *val) {\n    objectlist *new = (objectlist *) object_new(sizeof(objectlist), OBJECT_LIST);\n\n    if (new) {\n        varray_valueinit(&new->val);\n        if (nval>0) {\n            if (val) varray_valueadd(&new->val, val, nval);\n            else varray_valueresize(&new->val, nval);\n        }\n    }\n\n    return new;\n}\n\n/* **********************************************************************\n * objectlist utility functions\n * ********************************************************************** */\n\n/** Resizes a list */\nbool list_resize(objectlist *list, int size) {\n    return varray_valueresize(&list->val, size);\n}\n\n/** Appends an item to a list */\nvoid list_append(objectlist *list, value v) {\n    varray_valuewrite(&list->val, v);\n}\n\n/** Returns the length of a list */\nunsigned int list_length(objectlist *list) {\n    return list->val.count;\n}\n\n/** Removes an element from a list\n * @param[in] list a list object\n * @param[in] indx position to insert\n * @param[in] nval number of values to insert\n * @param[in] vals the entries to insert\n * @returns true on success */\nbool list_insert(objectlist *list, int indx, int nval, value *vals) {\n    int i = indx;\n    while (i<0) i+=list->val.count+1;\n    if (i>list->val.count) return false;\n    if (nval>list->val.capacity-list->val.count) if (!list_resize(list, list->val.count+nval)) return false;\n\n    memmove(list->val.data+i+nval, list->val.data+i, sizeof(value)*(list->val.count-i));\n    memcpy(list->val.data+i, vals, sizeof(value)*nval);\n\n    list->val.count+=nval;\n\n    return true;\n}\n\n/** Removes an element from a list\n * @param[in] list a list object\n * @param[in] val the entry to remove\n * @returns true on success */\nbool list_remove(objectlist *list, value val) {\n    /* Find the element */\n    for (unsigned int i=0; i<list->val.count; i++) {\n        if (MORPHO_ISEQUAL(list->val.data[i], val)) { /* Remove it if we're not at the end of the list */\n            if (i<list->val.count-1) memmove(list->val.data+i, list->val.data+i+1, sizeof(value)*(list->val.count-i-1));\n            list->val.count--;\n            return true;\n        }\n    }\n\n    return false;\n}\n\n/** Gets an element from a list\n * @param[in] list a list object\n * @param[in] i the index (may be negative)\n * @param[in] out filled out on exit if index is in bounds\n * @returns true on success */\nbool list_getelement(objectlist *list, int i, value *out) {\n    if (!(i>=-(int) list->val.count && i<(int) list->val.count)) return false;\n    if (i>=0) *out=list->val.data[i];\n    else *out=list->val.data[list->val.count+i];\n    return true;\n}\n\n/** Sort function for list_sort */\nint list_sortfunction(const void *a, const void *b) {\n    value l=*(value *) a, r=*(value *) b;\n    return -morpho_extendedcomparevalue(l, r);\n}\n\n/** Sort the contents of a list */\nvoid list_sort(objectlist *list) {\n    qsort(list->val.data, list->val.count, sizeof(value), list_sortfunction);\n}\n\nstatic vm *list_sortwithfn_vm;\nstatic value list_sortwithfn_fn;\nstatic bool list_sortwithfn_err;\n\n/** Sort function for list_sort */\nint list_sortfunctionwfn(const void *a, const void *b) {\n    value args[2] = {*(value *) a, *(value *) b};\n    value ret;\n\n    if (morpho_call(list_sortwithfn_vm, list_sortwithfn_fn, 2, args, &ret)) {\n        if (MORPHO_ISINTEGER(ret)) return MORPHO_GETINTEGERVALUE(ret);\n        if (MORPHO_ISFLOAT(ret)) return morpho_comparevalue(MORPHO_FLOAT(0), ret);\n    }\n\n    list_sortwithfn_err=true;\n    return 0;\n}\n\n/** Sort the contents of a list */\nbool list_sortwithfn(vm *v, value fn, objectlist *list) {\n    list_sortwithfn_vm=v;\n    list_sortwithfn_fn=fn;\n    list_sortwithfn_err=false;\n    qsort(list->val.data, list->val.count, sizeof(value), list_sortfunctionwfn);\n    return !list_sortwithfn_err;\n}\n\n/** Sort function for list_order */\ntypedef struct {\n    unsigned int indx;\n    value val;\n} listorderstruct;\n\n/** Sort function for list_order */\nint list_orderfunction(const void *a, const void *b) {\n    return -morpho_comparevalue(((listorderstruct *) a)->val, ((listorderstruct *) b)->val);\n}\n\n/* Returns a list of indices giving the ordering of a list */\nobjectlist *list_order(objectlist *list) {\n    listorderstruct *order = MORPHO_MALLOC(list->val.count*sizeof(listorderstruct));\n    objectlist *new = NULL;\n\n    if (order) {\n        for (unsigned int i=0; i<list->val.count; i++) {\n            order[i].indx=i;\n            order[i].val=list->val.data[i];\n        }\n        qsort(order, list->val.count, sizeof(listorderstruct), list_orderfunction);\n\n        new=object_newlist(list->val.count, NULL);\n        if (new) {\n            for (unsigned int i=0; i<list->val.count; i++) {\n                new->val.data[i]=MORPHO_INTEGER(order[i].indx);\n            }\n            new->val.count=list->val.count;\n        }\n\n        MORPHO_FREE(order);\n    }\n    return new;\n}\n\n/** Reverses a list in place */\nvoid list_reverse(objectlist *list) {\n    unsigned int hlen = list->val.count / 2;\n    for (unsigned int i=0; i<hlen; i++) {\n        value swp = list->val.data[i];\n        unsigned int j=list->val.count-i-1;\n        list->val.data[i]=list->val.data[j];\n        list->val.data[j]=swp;\n    }\n}\n\n/** Tests if a value is a member of a list */\nbool list_ismember(objectlist *list, value v) {\n    for (unsigned int i=0; i<list->val.count; i++) {\n        if (MORPHO_ISEQUAL(list->val.data[i], v)) return true;\n    }\n    return false;\n}\n\n/** Clones a list */\nobjectlist *list_clone(objectlist *list) {\n    return object_newlist(list->val.count, list->val.data);\n}\n\n/** Concatenates two lists */\nobjectlist *list_concatenate(objectlist *a, objectlist *b) {\n    objectlist *new=object_newlist(a->val.count+b->val.count, NULL);\n\n    if (new) {\n        if (new->val.data) {\n            memcpy(new->val.data, a->val.data, sizeof(value)*a->val.count);\n            memcpy(new->val.data+a->val.count, b->val.data, sizeof(value)*b->val.count);\n        }\n        new->val.count=a->val.count+b->val.count;\n    }\n\n    return new;\n}\n\n/** Rolls a list by a number of elements */\nobjectlist *list_roll(objectlist *a, int nplaces) {\n    objectlist *new=object_newlist(a->val.count, NULL);\n    \n    if (new) {\n        new->val.count=a->val.count;\n        unsigned int N = a->val.count;\n        int n = abs(nplaces);\n        if (n>N) n = n % N;\n        unsigned int Np = N - n; // Number of elements to roll\n        \n        if (nplaces<0) {\n            memcpy(new->val.data, a->val.data+n, sizeof(value)*Np);\n            memcpy(new->val.data+Np, a->val.data, sizeof(value)*n);\n        } else {\n            memcpy(new->val.data+n, a->val.data, sizeof(value)*Np);\n            if (n>0) memcpy(new->val.data, a->val.data+Np, sizeof(value)*n);\n        }\n    }\n\n    return new;\n}\n\n/** Loop function for enumerable initializers */\nstatic bool list_enumerableinitializer(vm *v, indx i, value val, void *ref) {\n    objectlist *list = (objectlist *) ref;\n    list_append(list, val);\n    return true;\n}\n\n/* Constructs a new list of a given size with a generic interface */\nvoid list_sliceconstructor(unsigned int *slicesize, unsigned int ndim, value* out){\n    objectlist *list = object_newlist(slicesize[0], NULL);\n    list->val.count = slicesize[0];\n    *out = MORPHO_OBJECT(list);\n}\n\n/* Checks that a list is indexed with only one value with a generic interface */\nbool list_slicedim(value * a, unsigned int ndim){\n    if (ndim>1||ndim<0) return false;\n    return true;\n}\n\n/* Copies data from list a at position indx to list out at position newindx with a generic interface */\nobjectarrayerror list_slicecopy(value * a,value * out, unsigned int ndim, unsigned int *indx,unsigned int *newindx){\n    value data;\n    objectlist *outList = MORPHO_GETLIST(*out);\n\n    if (list_getelement(MORPHO_GETLIST(*a),indx[0],&data)){\n        outList->val.data[newindx[0]] = data;\n    } else return ARRAY_OUTOFBOUNDS;\n    return ARRAY_OK;\n}\n\n/** Generate sets/tuples and return as a list of lists */\nvalue list_generatetuples(vm *v, objectlist *list, unsigned int n, tuplemode mode) {\n    unsigned int nval=list->val.count;\n    unsigned int work[2*n];\n    value tuple[n];\n    morpho_tuplesinit(list->val.count, n, work, mode);\n    objectlist *new = object_newlist(0, NULL);\n    if (!new) goto list_generatetuples_cleanup;\n\n    while (morpho_tuples(nval, list->val.data, n, work, mode, tuple)) {\n        objectlist *el = object_newlist(n, tuple);\n        if (el) {\n            list_append(new, MORPHO_OBJECT(el));\n        } else {\n            goto list_generatetuples_cleanup;\n        }\n    }\n\n    list_append(new, MORPHO_OBJECT(new));\n    morpho_bindobjects(v, new->val.count, new->val.data);\n    new->val.count--; // And pop it back off\n\n    return MORPHO_OBJECT(new);\n\nlist_generatetuples_cleanup:\n    morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n\n    if (new) { // Deallocate partially created list\n        for (unsigned int i=0; i<new->val.count; i++) {\n            value el=new->val.data[i];\n            if (MORPHO_ISOBJECT(el)) object_free(MORPHO_GETOBJECT(el));\n        }\n        object_free((object *) new);\n    }\n\n    return MORPHO_NIL;\n}\n\n/* **********************************************************************\n * List veneer class\n * ********************************************************************** */\n\n/** Constructor function for Lists */\nvalue list_constructor(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    value init=MORPHO_NIL;\n    objectlist *new=NULL;\n\n    if (nargs==1) {\n        value x = MORPHO_GETARG(args, 0);\n        if (MORPHO_ISRANGE(MORPHO_GETARG(args, 0))) {\n            init = x; // Enumerate will handle this\n            new = object_newlist(0, NULL);\n        } else if (MORPHO_ISTUPLE(MORPHO_GETARG(args, 0))) {\n            new = object_newlist(MORPHO_GETTUPLELENGTH(x), MORPHO_GETTUPLEVALUES(x));\n        }\n    }\n    \n    if (!new) new = object_newlist(nargs, args+1);\n\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n\n        if (!MORPHO_ISNIL(init)) {\n            builtin_enumerateloop(v, init, list_enumerableinitializer, new);\n        }\n    }\n\n    return out;\n}\n\n/** Append an element to a list */\nvalue List_append(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    unsigned int capacity = slf->val.capacity;\n\n    varray_valueadd(&slf->val, args+1, nargs);\n\n    if (slf->val.capacity!=capacity) morpho_resizeobject(v, (object *) slf, capacity*sizeof(value)+sizeof(objectlist), slf->val.capacity*sizeof(value)+sizeof(objectlist));\n\n    return MORPHO_SELF(args);\n}\n\n/** Remove a element from a list */\nvalue List_remove(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    if (nargs==1) {\n        if (!list_remove(slf, MORPHO_GETARG(args, 0))) morpho_runtimeerror(v, LIST_ENTRYNTFND);\n    } else morpho_runtimeerror(v, VM_INVALIDARGS, 1, nargs);\n\n    return MORPHO_NIL;\n}\n\n/** Inserts an element at a specified position */\nvalue List_insert(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    if (nargs>=2) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int indx = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            if (!list_insert(slf, indx, nargs-1, &MORPHO_GETARG(args, 1))) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        }\n    } else morpho_runtimeerror(v, VM_INVALIDARGS, 2, nargs);\n\n    return MORPHO_NIL;\n}\n\n/** Pops an element from the end of a list */\nvalue List_pop(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (slf->val.count>0) {\n        if (nargs>0 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int indx = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            \n            if (indx<0) indx+=slf->val.count;\n            if (indx<0 || indx>slf->val.count) {\n                morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n                return MORPHO_NIL;\n            }\n            \n            out=slf->val.data[indx];\n            memmove(slf->val.data+indx, slf->val.data+indx+1, sizeof(value)*(slf->val.count-indx-1));\n        } else {\n            out=slf->val.data[slf->val.count-1];\n        }\n        slf->val.count--;\n    }\n\n    return out;\n}\n\n/** Get an element */\nvalue List_getindex(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n            if (!list_getelement(slf, i, &out)) {\n                morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n            }\n        } else {\n            objectarrayerror err = getslice(&MORPHO_SELF(args),&list_slicedim,&list_sliceconstructor,&list_slicecopy,nargs,&MORPHO_GETARG(args, 0),&out);\n            if (err!=ARRAY_OK) MORPHO_RAISE(v, array_to_list_error(err) );\n            if (MORPHO_ISOBJECT(out)){\n                morpho_bindobjects(v,1,&out);\n            } else MORPHO_RAISE(v, VM_NONNUMINDX);\n\n        }\n    } else MORPHO_RAISE(v, LIST_NUMARGS)\n\n    return out;\n}\n\n/** Sets an element */\nvalue List_setindex(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    if (nargs==2) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            if (i<slf->val.count) slf->val.data[i]=MORPHO_GETARG(args, 1);\n            else morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n        } else morpho_runtimeerror(v, SETINDEX_ARGS);\n    } else morpho_runtimeerror(v, SETINDEX_ARGS);\n\n    return MORPHO_SELF(args);\n}\n\n/** Print a list */\nvalue List_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISLIST(self)) return Object_print(v, nargs, args);\n    \n    objectlist *lst=MORPHO_GETLIST(self);\n\n    morpho_printf(v, \"[ \");\n    for (unsigned int i=0; i<lst->val.count; i++) {\n        morpho_printvalue(v, lst->val.data[i]);\n        if (i<lst->val.count-1) morpho_printf(v, \", \");\n    }\n    morpho_printf(v, \" ]\");\n\n    return MORPHO_NIL;\n}\n\n/** Convert a list to a string */\nvalue List_tostring(vm *v, int nargs, value *args) {\n    objectlist *lst=MORPHO_GETLIST(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    varray_char buffer;\n    varray_charinit(&buffer);\n\n    varray_charadd(&buffer, \"[ \", 2);\n    for (unsigned int i=0; i<lst->val.count; i++) {\n        morpho_printtobuffer(v, lst->val.data[i], &buffer);\n        if (i<lst->val.count-1) varray_charadd(&buffer, \", \", 2);\n    }\n    varray_charadd(&buffer, \" ]\", 2);\n\n    out = object_stringfromvarraychar(&buffer);\n    if (MORPHO_ISSTRING(out)) {\n        morpho_bindobjects(v, 1, &out);\n    }\n    varray_charclear(&buffer);\n\n    return out;\n}\n\n/** Enumerate members of a list */\nvalue List_enumerate(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<0) {\n            out=MORPHO_INTEGER(slf->val.count);\n        } else if (n<slf->val.count) {\n            return slf->val.data[n];\n        } else {\n            morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n        }\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n    return out;\n}\n\n/** Get number of entries */\nvalue List_count(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(slf->val.count);\n}\n\n/** Generate a list of n-tuples from a list  */\nvalue List_tuples(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    unsigned int n=2;\n\n    if (nargs>0 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        if (n<2) n=2;\n    }\n\n    return list_generatetuples(v, slf, n, MORPHO_TUPLEMODE);\n}\n\n/** Generate a list of n-tuples from a list  */\nvalue List_sets(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    unsigned int n=2;\n\n    if (nargs>0 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        if (n<2) n=2;\n        if (n>slf->val.capacity) n = slf->val.capacity;\n    }\n\n    return list_generatetuples(v, slf, n, MORPHO_SETMODE);\n}\n\n/** Clones a list */\nvalue List_clone(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    objectlist *new = list_clone(slf);\n    if (!new) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    value out = MORPHO_OBJECT(new);\n    morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\n/** Joins two lists together  */\nvalue List_join(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISLIST(MORPHO_GETARG(args, 0))) {\n        objectlist *operand = MORPHO_GETLIST(MORPHO_GETARG(args, 0));\n        objectlist *new = list_concatenate(slf, operand);\n\n        if (new) {\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n\n    } else morpho_runtimeerror(v, LIST_ADDARGS);\n\n    return out;\n}\n\n/** Roll a list */\nvalue List_roll(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 &&\n        MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        int roll;\n        morpho_valuetoint(MORPHO_GETARG(args, 0), &roll);\n        \n        objectlist *new = list_roll(slf, roll);\n\n        if (new) {\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n\n    } else morpho_runtimeerror(v, LIST_ADDARGS);\n\n    return out;\n}\n\n/** Sorts a list */\nvalue XList_sort(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    if (nargs==0) {\n        list_sort(slf);\n    } else if (nargs==1 && MORPHO_ISCALLABLE(MORPHO_GETARG(args, 0))) {\n        if (!list_sortwithfn(v, MORPHO_GETARG(args, 0), slf)) {\n            morpho_runtimeerror(v, LIST_SRTFN);\n        }\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Sorts a list */\nvalue List_sort(vm *v, int nargs, value *args) {\n    list_sort(MORPHO_GETLIST(MORPHO_SELF(args)));\n    return MORPHO_NIL;\n}\n\nvalue List_sort_fn(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    if (!list_sortwithfn(v, MORPHO_GETARG(args, 0), slf)) {\n        morpho_runtimeerror(v, LIST_SRTFN);\n    }\n    return MORPHO_NIL;\n}\n\n/** Returns a list of indices that would sort the list self */\nvalue List_order(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    objectlist *new=list_order(slf);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n\n    return out;\n}\n\n/** Returns a list with the order reversed */\nvalue List_reverse(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n    \n    list_reverse(slf);\n    \n    return MORPHO_NIL;\n}\n\n/** Tests if a list has a value as a member */\nvalue List_ismember(vm *v, int nargs, value *args) {\n    objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args));\n\n    if (nargs==1) {\n        return MORPHO_BOOL(list_ismember(slf, MORPHO_GETARG(args, 0)));\n    } else morpho_runtimeerror(v, ISMEMBER_ARG, 1, nargs);\n\n    return MORPHO_NIL;\n}\n\nMORPHO_BEGINCLASS(List)\nMORPHO_METHOD(MORPHO_APPEND_METHOD, List_append, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_REMOVE_METHOD, List_remove, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_INSERT_METHOD, List_insert, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_POP_METHOD, List_pop, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, List_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, List_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, List_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_TOSTRING_METHOD, List_tostring, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, List_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, List_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_TUPLES_METHOD, List_tuples, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_SETS_METHOD, List_sets, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, List_clone, BUILTIN_FLAGSEMPTY),\n//MORPHO_METHOD(MORPHO_ADD_METHOD, List_add, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_JOIN_METHOD, List_join, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ROLL_METHOD, List_roll, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD_SIGNATURE(LIST_SORT_METHOD, \"()\", List_sort, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD_SIGNATURE(LIST_SORT_METHOD, \"(_)\", List_sort_fn, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_ORDER_METHOD, List_order, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_REVERSE_METHOD, List_reverse, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_ISMEMBER_METHOD, List_ismember, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CONTAINS_METHOD, List_ismember, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectlisttype;\n\nvoid list_initialize(void) {\n    // Define list objecttype\n    objectlisttype=object_addtype(&objectlistdefn);\n    \n    // Locate the Object class to use as the parent class of List\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Define List class\n    value listclass=builtin_addclass(LIST_CLASSNAME, MORPHO_GETCLASSDEFINITION(List), objclass);\n    object_setveneerclass(OBJECT_LIST, listclass);\n    \n    // List constructor function\n    morpho_addfunction(LIST_CLASSNAME, LIST_CLASSNAME \" (...)\", list_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // List error messages\n    morpho_defineerror(LIST_ENTRYNTFND, ERROR_HALT, LIST_ENTRYNTFND_MSG);\n    morpho_defineerror(LIST_ADDARGS, ERROR_HALT, LIST_ADDARGS_MSG);\n    morpho_defineerror(LIST_SRTFN, ERROR_HALT, LIST_SRTFN_MSG);\n    morpho_defineerror(LIST_ARGS, ERROR_HALT, LIST_ARGS_MSG);\n    morpho_defineerror(LIST_NUMARGS, ERROR_HALT, LIST_NUMARGS_MSG);\n}\n"
  },
  {
    "path": "src/classes/list.h",
    "content": "/** @file list.h\n *  @author T J Atherton\n *\n *  @brief Defines list object type and List class\n */\n\n#ifndef list_h\n#define list_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * List object type\n * ------------------------------------------------------- */\n\nextern objecttype objectlisttype;\n#define OBJECT_LIST objectlisttype\n\ntypedef struct {\n    object obj;\n    varray_value val;\n} objectlist;\n\n/** Tests whether an object is a list */\n#define MORPHO_ISLIST(val) object_istype(val, OBJECT_LIST)\n\n/** Gets the object as a list */\n#define MORPHO_GETLIST(val)   ((objectlist *) MORPHO_GETOBJECT(val))\n\n/** Create a static list - you must initialize the list separately */\n#define MORPHO_STATICLIST      { .obj.type=OBJECT_LIST, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .val.count=0, .val.capacity=0, .val.data=NULL }\n\nobjectlist *object_newlist(unsigned int nval, value *val);\n\n/* -------------------------------------------------------\n * List veneer class\n * ------------------------------------------------------- */\n\n#define LIST_CLASSNAME                    \"List\"\n\n#define LIST_ISMEMBER_METHOD              \"ismember\"\n#define LIST_SORT_METHOD                  \"sort\"\n#define LIST_ORDER_METHOD                 \"order\"\n#define LIST_POP_METHOD                   \"pop\"\n#define LIST_INSERT_METHOD                \"insert\"\n#define LIST_REMOVE_METHOD                \"remove\"\n#define LIST_TUPLES_METHOD                \"tuples\"\n#define LIST_SETS_METHOD                  \"sets\"\n#define LIST_REVERSE_METHOD               \"reverse\"\n\n/* -------------------------------------------------------\n * List error messages\n * ------------------------------------------------------- */\n\n#define LIST_ENTRYNTFND                   \"EntryNtFnd\"\n#define LIST_ENTRYNTFND_MSG               \"Entry not found.\"\n\n#define LIST_ADDARGS                      \"LstAddArgs\"\n#define LIST_ADDARGS_MSG                  \"Add method requires a list.\"\n\n#define LIST_SRTFN                        \"LstSrtFn\"\n#define LIST_SRTFN_MSG                    \"List sort function must return an integer.\"\n\n#define LIST_ARGS                         \"LstArgs\"\n#define LIST_ARGS_MSG                     \"Lists must be called with integer dimensions as arguments.\"\n\n#define LIST_NUMARGS                      \"LstNumArgs\"\n#define LIST_NUMARGS_MSG                  \"Lists can only be indexed with one argument.\"\n\n/* -------------------------------------------------------\n * List interface\n * ------------------------------------------------------- */\n\nvalue List_getindex(vm *v, int nargs, value *args);\n\nbool list_resize(objectlist *list, int size);\nvoid list_append(objectlist *list, value v);\nunsigned int list_length(objectlist *list);\nbool list_getelement(objectlist *list, int i, value *out);\nvoid list_sort(objectlist *list);\nobjectlist *list_clone(objectlist *list);\n\nvoid list_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/metafunction.c",
    "content": "/** @file metafunction.c\n *  @author T J Atherton\n *\n *  @brief Implement objectmetafunctions and the Metafunction veneer class\n */\n\n#include <limits.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * Metafunction opcodes\n * ********************************************************************** */\n\nenum {\n    MF_RESOLVE,\n    MF_FAIL,\n    MF_CHECKNARGSNEQ,\n    MF_CHECKNARGSLT,\n    MF_CHECKVALUE,\n    MF_CHECKOBJECT,\n    MF_CHECKINSTANCE,\n    MF_BRANCH,\n    MF_BRANCHNARGS,\n    MF_BRANCHVALUETYPE,\n    MF_BRANCHOBJECTTYPE,\n    MF_BRANCHINSTANCE\n};\n\n/* **********************************************************************\n * objectmetafunction definitions\n * ********************************************************************** */\n\nvoid objectmetafunction_freefn(object *obj) {\n    objectmetafunction *f = (objectmetafunction *) obj;\n    morpho_freeobject(f->name);\n    varray_valueclear(&f->fns);\n    metafunction_clearinstructions(f);\n}\n\nvoid objectmetafunction_markfn(object *obj, void *v) {\n    objectmetafunction *f = (objectmetafunction *) obj;\n    morpho_markvalue(v, f->name); // Mark the name\n    \n    for (int i=0; i<f->resolver.count; i++) { // Mark any functions in the resolver\n        mfinstruction *instr = &f->resolver.data[i];\n        if (instr->opcode==MF_RESOLVE) morpho_markvalue(v,instr->data.resolvefn);\n    }\n}\n\nsize_t objectmetafunction_sizefn(object *obj) {\n    objectmetafunction *f = (objectmetafunction *) obj;\n    return sizeof(objectmetafunction)+sizeof(value)*f->fns.count;\n}\n\nvoid objectmetafunction_printfn(object *obj, void *v) {\n    objectmetafunction *f = (objectmetafunction *) obj;\n    if (f) morpho_printf(v, \"<fn %s>\", (MORPHO_ISNIL(f->name) ? \"\" : MORPHO_GETCSTRING(f->name)));\n}\n\nobjecttypedefn objectmetafunctiondefn = {\n    .printfn=objectmetafunction_printfn,\n    .markfn=objectmetafunction_markfn,\n    .freefn=objectmetafunction_freefn,\n    .sizefn=objectmetafunction_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * objectmetafunction utility functions\n * ********************************************************************** */\n\n/** Creates a new metafunction */\nobjectmetafunction *object_newmetafunction(value name) {\n    objectmetafunction *new = (objectmetafunction *) object_new(sizeof(objectmetafunction), OBJECT_METAFUNCTION);\n\n    if (new) {\n        new->name=MORPHO_NIL;\n        if (MORPHO_ISSTRING(name)) new->name=object_clonestring(name);\n        new->klass=NULL; \n        varray_valueinit(&new->fns);\n        varray_mfinstructioninit(&new->resolver);\n    }\n\n    return new;\n}\n\n/** Clone a metafunction */\nobjectmetafunction *metafunction_clone(objectmetafunction *f) {\n    objectmetafunction *new = object_newmetafunction(f->name);\n    \n    if (new) {\n        varray_valueadd(&new->fns, f->fns.data, f->fns.count);\n    }\n    \n    return new;\n}\n\n/** Wraps a function in a metafunction */\nbool metafunction_wrap(value name, value fn, value *out) {\n    if (!MORPHO_ISCALLABLE(fn)) return false;\n    \n    objectmetafunction *mf = object_newmetafunction(name);\n    if (!mf) return false;\n    \n    metafunction_add(mf, fn);\n    *out = MORPHO_OBJECT(mf);\n    \n    return true;\n}\n\n/** Adds a function to a metafunction */\nbool metafunction_add(objectmetafunction *f, value fn) {\n    return varray_valuewrite(&f->fns, fn);\n}\n\n/** Extracts a type from a value */\nbool metafunction_typefromvalue(value v, value *out) {\n    objectclass *clss = NULL;\n    \n    if (MORPHO_ISINSTANCE(v)) {\n        clss=MORPHO_GETINSTANCE(v)->klass;\n    } else if (MORPHO_ISOBJECT(v)) {\n        clss = object_getveneerclass(MORPHO_GETOBJECT(v)->type);\n    } else clss = value_getveneerclass(v);\n    \n    if (clss) *out = MORPHO_OBJECT(clss);\n    return clss;\n}\n\n/** Checks if val matches a given type */\nbool metafunction_matchtype(value type, value val) {\n    value match;\n    if (!metafunction_typefromvalue(val, &match)) return false;\n    \n    if (MORPHO_ISNIL(type) || // If type is unset, we always match\n        MORPHO_ISEQUAL(type, match)) return true; // Or if the types are the same\n    \n    return false;\n}\n\n/** Sets the parent class of a metafunction */\nvoid metafunction_setclass(objectmetafunction *f, objectclass *klass) {\n    f->klass=klass;\n}\n\n/** Returns a metafunction's class if any */\nobjectclass *metafunction_class(objectmetafunction *f) {\n    return f->klass;\n}\n\n/** Finds whether an implementation f occurs in a metafunction */\nbool metafunction_matchfn(objectmetafunction *fn, value f) {\n    for (int i=0; i<fn->fns.count; i++) if (MORPHO_ISEQUAL(fn->fns.data[i], f)) return true;\n    return false;\n}\n\n/** Checks if a metafunction matches a given list of implementations */\nbool metafunction_matchset(objectmetafunction *fn, int n, value *fns) {\n    for (int i=0; i<n; i++) {\n        if (!metafunction_matchfn(fn, fns[i])) return false;\n    }\n    return true;\n}\n\nsignature *metafunction_getsignature(value fn) {\n    if (MORPHO_ISFUNCTION(fn)) {\n        return &MORPHO_GETFUNCTION(fn)->sig;\n    } else if (MORPHO_ISBUILTINFUNCTION(fn)) {\n        return &MORPHO_GETBUILTINFUNCTION(fn)->sig;\n    } else if (MORPHO_ISCLOSURE(fn)) {\n        return &MORPHO_GETCLOSURE(fn)->func->sig;\n    }\n    return NULL;\n}\n\nvalue _getname(value fn) {\n    if (MORPHO_ISFUNCTION(fn)) {\n        return MORPHO_GETFUNCTION(fn)->name;\n    } else if (MORPHO_ISBUILTINFUNCTION(fn)) {\n        return MORPHO_GETBUILTINFUNCTION(fn)->name;\n    } else if (MORPHO_ISCLOSURE(fn)) {\n        return MORPHO_GETCLOSURE(fn)->func->name;\n    }\n    return MORPHO_NIL;\n}\n\n/* **********************************************************************\n * Fast metafunction resolver\n * ********************************************************************** */\n\nDEFINE_VARRAY(mfinstruction, mfinstruction);\n\n#define MFINSTRUCTION_EMPTY -1\n\n#define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY }\n#define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY }\n#define MFINSTRUCTION_OPTARGS { .opcode=MF_OPTARGS, .branch=MFINSTRUCTION_EMPTY }\n#define MFINSTRUCTION_CHECKNARGS(op, n, brnch) { .opcode=op, .narg=n, .branch=brnch }\n#define MFINSTRUCTION_CHECKTYPE(op, n, t, brnch) { .opcode=op, .data.tindx=t, .narg=n, .branch=brnch }\n#define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch }\n#define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch }\n#define MFINSTRUCTION_BRANCHTABLE(op, n, table, brnch) { .opcode=op, .narg=n, .data.btable=table, .branch=brnch }\n    \ntypedef struct {\n    signature *sig; /** Signature of the target */\n    value fn; /** The target */\n    int indx; /** Used to sort */\n} mfresult;\n\ntypedef struct {\n    int count;\n    mfresult *rlist;\n} mfset;\n\n/** Static intiializer for the mfset */\n#define MFSET(c, l) { .count=c, .rlist=l }\n\nDECLARE_VARRAY(mfset, mfset)\nDEFINE_VARRAY(mfset, mfset)\n\ntypedef struct {\n    objectmetafunction *fn;\n    varray_int checked; // Stack of checked parameters\n    error err;\n} mfcompiler;\n\n/** Initialize the metafunction compiler */\nvoid mfcompiler_init(mfcompiler *c, objectmetafunction *fn) {\n    c->fn=fn;\n    varray_intinit(&c->checked);\n    error_init(&c->err);\n}\n\n/** Clear the metafunction compiler */\nvoid mfcompiler_clear(mfcompiler *c, objectmetafunction *fn) {\n    varray_intclear(&c->checked);\n    error_clear(&c->err);\n}\n\n/** Report an error during metafunction compilation */\nvoid mfcompiler_error(mfcompiler *c, errorid id) {\n    morpho_writeerrorwithid(&c->err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE);\n}\n\n/** Pushes a parameter check onto the stack*/\nvoid mfcompiler_pushcheck(mfcompiler *c, int i) {\n    varray_intwrite(&c->checked, i);\n}\n\n/** Pops a parameter check from the stack*/\nint mfcompiler_popcheck(mfcompiler *c) {\n    c->checked.count--;\n    return c->checked.data[c->checked.count];\n}\n\n/** Tests if a parameter has been checked according to the check stack */\nbool mfcompiler_ischecked(mfcompiler *c, int i) {\n    for (int j=0; j<c->checked.count; j++) if (i==c->checked.data[j]) return true;\n    return false;\n}\n\nvoid _mfcompiler_disassemblebranchtable(mfinstruction *instr, mfindx i) {\n    for (int k=0; k<instr->data.btable.count; k++) {\n        printf(\"        %i -> %i\\n\", k, i+instr->data.btable.data[k]+1);\n    }\n}\n\n/** Disassemble */\nvoid mfcompiler_disassemble(mfcompiler *c) {\n    int ninstr = c->fn->resolver.count;\n    morpho_printvalue(NULL, MORPHO_OBJECT(c->fn));\n    printf(\":\\n\");\n    for (int i=0; i<ninstr; i++) {\n        mfinstruction *instr = &c->fn->resolver.data[i];\n        printf(\"%5i : \", i) ;\n        switch(instr->opcode) {\n            case MF_CHECKNARGSNEQ: {\n                printf(\"checknargsneq %i -> (%i)\", instr->narg, i+instr->branch+1);\n                break;\n            }\n            case MF_CHECKNARGSLT: {\n                printf(\"checknargslt %i -> (%i)\", instr->narg, i+instr->branch+1);\n                break;\n            }\n            case MF_CHECKVALUE: {\n                objectclass *klass=value_veneerclassfromtype(instr->data.tindx);\n                printf(\"checkvalue (%i) [%s] -> (%i)\", instr->narg,  MORPHO_GETCSTRING(klass->name), i+instr->branch+1);\n                break;\n            }\n            case MF_CHECKOBJECT: {\n                objectclass *klass=object_getveneerclass(instr->data.tindx);\n                printf(\"checkobject (%i) [%s] -> (%i)\", instr->narg, MORPHO_GETCSTRING(klass->name), i+instr->branch+1);\n                break;\n            }\n            case MF_CHECKINSTANCE: {\n                printf(\"checkinstance (%i) [%i] -> (%i)\", instr->narg, instr->data.tindx, i+instr->branch+1);\n                break;\n            }\n            case MF_BRANCH: {\n                printf(\"branch -> (%i)\", i+instr->branch+1);\n                break;\n            }\n            case MF_BRANCHNARGS: {\n                printf(\"branchnargs (%i) -> (%i)\\n\", instr->narg, i+instr->branch+1);\n                _mfcompiler_disassemblebranchtable(instr, i);\n                break;\n            }\n            case MF_BRANCHVALUETYPE: {\n                printf(\"branchvalue (%i) -> (%i)\\n\", instr->narg, i+instr->branch+1);\n                for (int k=0; k<instr->data.btable.count; k++) {\n                    if (instr->data.btable.data[k]==0) continue;\n                    objectclass *klass=value_veneerclassfromtype(k);\n                    printf(\"        %i [%s] -> %i\\n\", k, (klass ? MORPHO_GETCSTRING(klass->name) : \"\"), i+instr->data.btable.data[k]+1);\n                }\n                break;\n            }\n            case MF_BRANCHOBJECTTYPE: {\n                printf(\"branchobjtype (%i) -> (%i)\\n\", instr->narg, i+instr->branch+1);\n                for (int k=0; k<instr->data.btable.count; k++) {\n                    if (instr->data.btable.data[k]==0) continue;\n                    objectclass *klass=object_getveneerclass(k);\n                    printf(\"        %i [%s] -> %i\\n\", k, (klass ? MORPHO_GETCSTRING(klass->name) : \"\"), i+instr->data.btable.data[k]+1);\n                }\n                break;\n            }\n            case MF_BRANCHINSTANCE: {\n                printf(\"branchinstance (%i) -> (%i)\\n\", instr->narg, i+instr->branch+1);\n                _mfcompiler_disassemblebranchtable(instr, i);\n                break;\n            }\n            case MF_RESOLVE: {\n                printf(\"resolve \");\n                signature *sig = metafunction_getsignature(instr->data.resolvefn);\n                printf(\" \");\n                if (sig) signature_print(sig);\n                break;\n            }\n            case MF_FAIL: printf(\"fail\"); break;\n        }\n        printf(\"\\n\");\n    }\n}\n\n/** Counts the range of parameters for the function call */\nvoid mfcompile_countparams(mfcompiler *c, mfset *set, int *min, int *max) {\n    int imin=INT_MAX, imax=INT_MIN;\n    for (int i=0; i<set->count; i++) {\n        int nparams;\n        signature_paramlist(set->rlist[i].sig, &nparams, NULL);\n        if (nparams<imin) imin=nparams;\n        if (nparams>imax) imax=nparams;\n    }\n    if (min) *min = imin;\n    if (max) *max = imax;\n}\n\n/** Places the various outcomes for a parameter into a dictionary */\nbool mfcompile_outcomes(mfcompiler *c, mfset *set, int i, dictionary *out) {\n    for (int k=0; k<set->count; k++) { // Loop over outcomes\n        value type;\n        if (!signature_getparamtype(set->rlist[k].sig, i, &type)) continue;\n        if (!dictionary_insert(out, (MORPHO_ISNIL(type) ? MORPHO_FALSE : type), MORPHO_NIL)) return false;\n    }\n    return true;\n}\n\n/** Find the parameter number that has most variety in types */\nbool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) {\n    varray_int count;\n    varray_intinit(&count);\n    \n    dictionary dict;\n    dictionary_init(&dict);\n    \n    // Loop over parameters, counting the number of outcomes.\n    while (true) {\n        mfcompile_outcomes(c, set, count.count, &dict);\n        if (!dict.count) break;\n        varray_intwrite(&count, dict.count);\n        dictionary_clear(&dict); // Not needed if dict.count was zero\n    };\n    \n    // Find the parameter that has most variability that has not already been checked\n    int max=0, maxindx=-1;\n    for (int i=0; i<count.count; i++) {\n        if (mfcompiler_ischecked(c, i)) continue;\n        if (count.data[i]>max) { max=count.data[i]; maxindx=i; }\n    }\n    \n    varray_intclear(&count);\n    \n    if (maxindx<0) return false;\n    if (best) *best = maxindx;\n    \n    return true;\n}\n\nmfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) {\n    return varray_mfinstructionwrite(&c->fn->resolver, instr);\n}\n\nmfindx mfcompile_currentinstruction(mfcompiler *c) {\n    return c->fn->resolver.count-1;\n}\n\nmfindx mfcompile_nextinstruction(mfcompiler *c) {\n    return c->fn->resolver.count;\n}\n\nvoid mfcompile_setbranch(mfcompiler *c, mfindx i, mfindx branch) {\n    if (i>=c->fn->resolver.count) return;\n    c->fn->resolver.data[i].branch=branch;\n}\n\nvoid mfcompile_replaceinstruction(mfcompiler *c, mfindx i, mfinstruction instr) {\n    if (i>=c->fn->resolver.count) return;\n    c->fn->resolver.data[i] = instr;\n}\n\nenum {\n    MF_VENEERVALUE,\n    MF_INSTANCE,\n    MF_VENEEROBJECT,\n    MF_ANY\n};\n\n/** Detects the kind of type */\nint _detecttype(value type, int *tindx) {\n    if (MORPHO_ISCLASS(type)) {\n        objectclass *klass = MORPHO_GETCLASS(type);\n        if (object_veneerclasstotype(klass, tindx)) {\n            return MF_VENEEROBJECT;\n        } else if (value_veneerclasstotype(klass, tindx)) {\n            return MF_VENEERVALUE;\n        } else {\n            if (tindx) *tindx=klass->uid;\n            return MF_INSTANCE;\n        }\n    }\n    return MF_ANY;\n}\n\nmfindx mfcompile_fail(mfcompiler *c);\nmfindx mfcompile_resolve(mfcompiler *c, mfresult *res);\nmfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i);\nmfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max);\nmfindx mfcompile_set(mfcompiler *c, mfset *set);\n\n/** Inserts a fail instruction */\nmfindx mfcompile_fail(mfcompiler *c) {\n    mfinstruction fail = MFINSTRUCTION_FAIL;\n    return mfcompile_insertinstruction(c, fail);\n}\n\n/** Checks a parameter i for type */\nmfindx mfcompile_check(mfcompiler *c, int i, value type) {\n    int tindx;\n    int opcode[MF_ANY] = { MF_CHECKVALUE, MF_CHECKINSTANCE, MF_CHECKOBJECT };\n    int k=_detecttype(type, &tindx);\n    \n    if (k==MF_ANY) return MFINSTRUCTION_EMPTY;\n        \n    mfinstruction check = MFINSTRUCTION_CHECKTYPE(opcode[k], i, tindx, 0);\n    return mfcompile_insertinstruction(c, check);\n}\n\n/** Compiles a single result */\nmfindx mfcompile_resolve(mfcompiler *c, mfresult *res) {\n    mfindx start = mfcompile_nextinstruction(c);\n    \n    // Check all arguments have been resolved\n    signature *sig = res->sig;\n    for (int i=0; i<sig->types.count; i++) {\n        if (MORPHO_ISNIL(sig->types.data[i]) ||\n            mfcompiler_ischecked(c, i)) continue;\n        \n        mfcompile_check(c, i, sig->types.data[i]);\n    }\n    \n    mfindx end = mfcompile_nextinstruction(c);\n    \n    mfinstruction instr = MFINSTRUCTION_RESOLVE(res->fn);\n    mfcompile_insertinstruction(c, instr);\n    \n    if (start!=end) {\n        mfindx fail = mfcompile_fail(c);\n        for (mfindx i=start; i<end; i++) mfcompile_setbranch(c, i, fail-start-1);\n    }\n        \n    return start;\n}\n\n/** Compile a branch table from a sorted set */\nvoid mfcompile_branchtable(mfcompiler *c, mfset *set, mfindx bindx, varray_int *btable) {\n    int k=0;\n    // Values with negative indices shouldn't be included in the branch table\n    while (k<set->count && set->rlist[k].indx<0) k++;\n    \n    // Deal with each outcome\n    while (k<set->count) {\n        int indx=set->rlist[k].indx, n=0;\n        while (k+n<set->count && set->rlist[k+n].indx==indx) n++;\n        \n        mfset out = MFSET(n, &set->rlist[k]);\n        \n        // Set the branch point\n        btable->data[indx]=mfcompile_currentinstruction(c)-bindx;\n        mfcompile_set(c, &out);\n        \n        k+=n;\n    }\n}\n\nvoid _insertchildren(dictionary *dict, value v) {\n    if (!MORPHO_ISCLASS(v) ||\n        dictionary_get(dict, v, NULL)) return;\n    \n    dictionary_insert(dict, v, MORPHO_NIL); // Insert the class\n    objectclass *klass = MORPHO_GETCLASS(v); // and its children\n    for (int i=0; i<klass->children.count; i++) _insertchildren(dict, klass->children.data[i]);\n}\n\nbool _resolve(objectclass *klass, dictionary *types, value *out) {\n    for (int k=0; k<klass->linearization.count; k++) {\n        if (dictionary_get(types, klass->linearization.data[k], NULL)) {\n            *out = klass->linearization.data[k];\n            return true;\n        }\n    }\n    return false;\n}\n\nint _maxindx(dictionary *dict) {\n    int indx=0, maxindx=0;\n    for (int i=0; i<dict->capacity; i++) {\n        _detecttype(dict->contents[i].key, &indx);\n        if (indx>maxindx) maxindx=indx;\n    }\n    return maxindx;\n}\n\nint _mfresultsortfn (const void *a, const void *b) {\n    mfresult *aa = (mfresult *) a, *bb = (mfresult *) b;\n    \n    int ai = aa->indx, bi = bb->indx;\n    if (aa->sig->varg) ai=-1; // Ensure vargs end up first\n    if (bb->sig->varg) bi=-1;\n    \n    return ai-bi;\n}\n\n/** Constructs a dispatch table from the set of implementations */\nmfindx mfcompile_dispatchtable(mfcompiler *c, mfset *set, int i, int otype, int opcode) {\n    dictionary types, children;\n    dictionary_init(&types); // Keep track of the available types provided by the implementation\n    dictionary_init(&children); // and all of their children\n    \n    // Extract the type index for each member of the set\n    for (int k=0; k<set->count; k++) {\n        value type;\n        if (!signature_getparamtype(set->rlist[k].sig, i, &type)) UNREACHABLE(\"Incorrect parameter type\");\n        if (_detecttype(type, &set->rlist[k].indx)==otype) {\n            dictionary_insert(&types, type, MORPHO_NIL);\n            _insertchildren(&children, type);\n        } else set->rlist[k].indx=-1; // Exclude from the branch table\n    }\n    \n    // Sort the set on the type index\n    qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn);\n    \n    // Create the branch table\n    int maxindx=_maxindx(&children);\n    varray_int btable;\n    varray_intinit(&btable);\n    for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0);\n    \n    // Insert the branch instruction\n    mfinstruction instr = MFINSTRUCTION_BRANCHTABLE(opcode, i, btable, 0);\n    mfindx bindx = mfcompile_insertinstruction(c, instr);\n    \n    // Fail if an object type isn't in the table\n    mfcompile_fail(c);\n    \n    // Compile the branch table\n    mfcompile_branchtable(c, set, bindx, &btable);\n    \n    // Fix branch table to include child classes\n    for (int i=0; i<children.capacity; i++) {\n        int ifrom, ito;\n        value mapto;\n        if (_detecttype(children.contents[i].key, &ifrom)==otype &&\n            _resolve(MORPHO_GETCLASS(children.contents[i].key), &types, &mapto) &&\n            _detecttype(mapto, &ito)==otype) {\n            btable.data[ifrom]=btable.data[ito];\n        }\n    }\n    \n    // Clear temporary data structures\n    dictionary_clear(&types);\n    dictionary_clear(&children);\n    \n    return bindx;\n}\n\n/** Branch table on object type */\nmfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) {\n    return mfcompile_dispatchtable(c, set, i, MF_VENEEROBJECT, MF_BRANCHOBJECTTYPE);\n}\n\n/** Branch table on value type */\nmfindx mfcompile_dispatchveneervalue(mfcompiler *c, mfset *set, int i) {\n    return mfcompile_dispatchtable(c, set, i, MF_VENEERVALUE, MF_BRANCHVALUETYPE);\n}\n\n/** Branch table on instance type */\nmfindx mfcompile_dispatchinstance(mfcompiler *c, mfset *set, int i) {\n    return mfcompile_dispatchtable(c, set, i, MF_INSTANCE, MF_BRANCHINSTANCE);\n}\n\n/** Handle implementations that accept any type */\nmfindx mfcompile_dispatchany(mfcompiler *c, mfset *set, int i) {\n    mfresult rlist[set->count];\n    int n=0;\n    \n    // Find implementations that accept any type\n    for (int k=0; k<set->count; k++) {\n        value type;\n        if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY;\n        if (_detecttype(type, &set->rlist[k].indx)==MF_ANY) {\n            rlist[n] = set->rlist[k]; n++;\n        }\n    }\n    \n    mfindx bindx = mfcompile_nextinstruction(c);\n    \n    mfset anyset = MFSET(n, rlist);\n    mfcompile_set(c, &anyset);\n    \n    return bindx;\n}\n\n/** Fixes a fallthrough fail */\nvoid mfcompile_fixfallthrough(mfcompiler *c, mfindx i, mfindx branchto) {\n    mfinstruction instr = MFINSTRUCTION_BRANCH(branchto-i-1);\n    mfcompile_replaceinstruction(c, i, instr);\n}\n\ntypedef mfindx (mfcompile_dispatchfn) (mfcompiler *c, mfset *set, int i);\n\n/** Attempts to dispatch based on a parameter i */\nmfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) {\n    mfcompiler_pushcheck(c, i);\n    int typecount[MF_ANY+1] = { 0, 0, 0, 0};\n    \n    // Determine what types are present\n    for (int k=0; k<set->count; k++) {\n        value type;\n        if (!signature_getparamtype(set->rlist[k].sig, i, &type)) continue;\n        typecount[_detecttype(type, NULL)]++;\n    }\n    \n    mfindx bindx[MF_ANY+1];\n    mfcompile_dispatchfn *dfn[MF_ANY+1] = { mfcompile_dispatchveneervalue,\n                                            mfcompile_dispatchinstance,\n                                            mfcompile_dispatchveneerobj,\n                                            mfcompile_dispatchany};\n    \n    // Cycle through all value types, building a chain of branchtables\n    int n=0;\n    for (int j=0; j<=MF_ANY; j++) {\n        if (typecount[j] && dfn[j]) {\n            bindx[n]=(dfn[j]) (c, set, i);\n            if (n>0) mfcompile_setbranch(c, bindx[n-1], bindx[n]-bindx[n-1]-1);\n            n++;\n        }\n    }\n    \n    if (typecount[MF_ANY]) { // Fix branch table fallthroughs to point to any\n        for (int j=0; j<n-1; j++) {\n            if (bindx[j]!=MFINSTRUCTION_EMPTY) mfcompile_fixfallthrough(c, bindx[j]+1, bindx[n-1]);\n        }\n    }\n    \n    mfcompiler_popcheck(c);\n    return bindx[0];\n}\n\n/** Compiles an argument number check for a single result */\nmfindx mfcompile_checknarg(mfcompiler *c, mfresult *res) {\n    mfinstruction instr = MFINSTRUCTION_CHECKNARGS((signature_isvarg(res->sig) ? MF_CHECKNARGSLT : MF_CHECKNARGSNEQ ), signature_countparams(res->sig), 0);\n    mfindx bindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction\n    \n    mfcompile_resolve(c, res);\n    \n    return bindx;\n}\n\n/** Attempts to dispatch based on the number of arguments */\nmfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) {\n    mfindx bindx = MFINSTRUCTION_EMPTY;\n    \n    // Sort the set into order given by number of parameters; resolution with varg is always first\n    for (int k=0; k<set->count; k++) {\n        set->rlist[k].indx=signature_countparams(set->rlist[k].sig);\n    }\n    qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn);\n    \n    if (set->count==2) {\n        for (int i=1; i>=0; i--) {\n            bindx = mfcompile_checknarg(c, &set->rlist[i]);\n            \n            mfindx eindx = mfcompile_currentinstruction(c);\n            mfcompile_setbranch(c, bindx, eindx-bindx);\n        }\n        mfcompile_fail(c);\n    } else { // If more than two options, generate a branch table\n        varray_int btable;\n        varray_intinit(&btable);\n        for (int i=0; i<=max; i++) varray_intwrite(&btable, 0);\n        \n        // Insert the branch table instruction\n        mfinstruction table = MFINSTRUCTION_BRANCHNARG(btable, 0);\n        bindx = mfcompile_insertinstruction(c, table);\n        \n        // Immediately follow by a fail instruction if this falls through\n        mfindx fail = mfcompile_fail(c);\n        \n        // Compile the branch table\n        mfcompile_branchtable(c, set, bindx, &btable);\n        \n        // Correct branch table for varg resolution\n        if (set->rlist[0].sig->varg) {\n            mfindx varg = bindx+1;\n            int nmin = set->rlist[0].sig->types.count-1; // varg can match this many args or more\n            for (int i=nmin; i<btable.count; i++) {\n                if (btable.data[i]==fail-1) btable.data[i]=varg;\n            }\n            \n            mfcompile_setbranch(c, bindx, varg); // Correct branchargs branch destination to point to the varg resolution\n        }\n    }\n    return bindx;\n}\n\n/** Attempts to discriminate between a list of possible signatures */\nmfindx mfcompile_set(mfcompiler *c, mfset *set) {\n    if (set->count==1) return mfcompile_resolve(c, set->rlist);\n    \n    int min, max; // Count the range of possible parameters\n    mfcompile_countparams(c, set, &min, &max);\n    \n    // Dispatch on the number of parameters if it's in doubt\n    if (min!=max) return mfcompile_dispatchonnarg(c, set, min, max);\n    \n    // If just one parameter, dispatch on it\n    if (min==1 && !mfcompiler_ischecked(c, 0)) {\n        return mfcompile_dispatchonparam(c, set, 0);\n    }\n    \n    int best;\n    if (mfcompile_countoutcomes(c, set, &best)) return mfcompile_dispatchonparam(c, set, best);\n    \n    mfcompiler_error(c, METAFUNCTION_CMPLAMBGS);\n    return MFINSTRUCTION_EMPTY;\n}\n\n/** Clears the compiled code from a given metafunction */\nvoid metafunction_clearinstructions(objectmetafunction *fn) {\n    for (int i=0; i<fn->resolver.count; i++) {\n        mfinstruction *mf = &fn->resolver.data[i];\n        if (mf->opcode>=MF_BRANCHNARGS && mf->opcode<=MF_BRANCHINSTANCE) varray_intclear(&mf->data.btable);\n    }\n    varray_mfinstructionclear(&fn->resolver);\n}\n\n/** Compiles the metafunction resolver */\nbool metafunction_compile(objectmetafunction *fn, error *err) {\n    mfset set;\n    set.count = fn->fns.count;\n    if (!set.count) return false;\n    \n    mfresult rlist[set.count];\n    set.rlist=rlist;\n    for (int i=0; i<set.count; i++) {\n        rlist[i].sig=metafunction_getsignature(fn->fns.data[i]);\n        rlist[i].fn=fn->fns.data[i];\n    }\n    \n    mfcompiler compiler;\n    mfcompiler_init(&compiler, fn);\n    \n    mfcompile_set(&compiler, &set);\n    //mfcompiler_disassemble(&compiler);\n    \n    bool success=!morpho_checkerror(&compiler.err);\n    if (!success && err) *err=compiler.err;\n    \n    mfcompiler_clear(&compiler, fn);\n    \n    return success;\n}\n\n/** Attempt to find the desired class uid in the linearization of a given class */\nbool _finduidinlinearization(objectclass *klass, int uid) {\n    for (int k=0; k<klass->linearization.count; k++) {\n        if (MORPHO_GETCLASS(klass->linearization.data[k])->uid == uid) return true;\n    }\n    return false;\n}\n\n/** Execute the metafunction's resolver \n @param[in] fn - the metafunction to resolve\n @param[in] nargs - number of positional arguments\n @param[in] args - positional arguments @warning: the first user-visible argument should be in the zero position\n @param[out] err - error block to be filled out\n @param[out] out - resolved function\n @returns true if the metafunction was successfully resolved */\nbool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, error *err, value *out) {\n    if (!fn->resolver.data &&\n        !metafunction_compile(fn, err)) return false;\n    mfinstruction *pc = fn->resolver.data;\n    if (!pc) return false;\n    \n    do {\n        switch(pc->opcode) {\n            case MF_CHECKNARGSNEQ:\n                if (nargs!=pc->narg) pc+=pc->branch;\n                break;\n            case MF_CHECKNARGSLT:\n                if (nargs<pc->narg) pc+=pc->branch;\n                break;\n            case MF_CHECKVALUE: {\n                if (!MORPHO_ISOBJECT(args[pc->narg])) {\n                    int tindx = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]);\n                    if (pc->data.tindx!=tindx) pc+=pc->branch;\n                } else pc+=pc->branch;\n            }\n                break;\n            case MF_CHECKOBJECT: {\n                if (MORPHO_ISOBJECT(args[pc->narg])) {\n                    int tindx = (int) MORPHO_GETOBJECTTYPE(args[pc->narg]);\n                    if (pc->data.tindx!=tindx) pc+=pc->branch;\n                } else pc+=pc->branch;\n            }\n                break;\n            case MF_CHECKINSTANCE: {\n                if (MORPHO_ISINSTANCE(args[pc->narg])) {\n                    objectclass *klass = MORPHO_GETINSTANCE(args[pc->narg])->klass;\n                    \n                    if (!(klass->uid==pc->data.tindx ||\n                        _finduidinlinearization(klass, pc->data.tindx))) {\n                        pc+=pc->branch;\n                    }\n                } else pc+=pc->branch;\n            }\n                break;\n            case MF_BRANCH:\n                pc+=pc->branch;\n                break;\n            case MF_BRANCHNARGS:\n                if (nargs<pc->data.btable.count) {\n                    pc+=pc->data.btable.data[nargs];\n                } else pc+=pc->branch;\n                break;\n            case MF_BRANCHVALUETYPE: {\n                if (!MORPHO_ISOBJECT(args[pc->narg])) {\n                    int type = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]);\n                    if (type<pc->data.btable.count) pc+=pc->data.btable.data[type];\n                } else pc+=pc->branch;\n            }\n                break;\n            case MF_BRANCHOBJECTTYPE: {\n                if (MORPHO_ISOBJECT(args[pc->narg])) {\n                    int type = MORPHO_GETOBJECTTYPE(args[pc->narg]);\n                    if (type<pc->data.btable.count) pc+=pc->data.btable.data[type];\n                } else pc+=pc->branch;\n            }\n                break;\n            case MF_BRANCHINSTANCE: {\n                if (MORPHO_ISINSTANCE(args[pc->narg])) {\n                    // TODO: Check for btable bound\n                    objectclass *klass = MORPHO_GETINSTANCE(args[pc->narg])->klass;\n                    if (klass->uid<pc->data.btable.count) pc+=pc->data.btable.data[klass->uid];\n                } else pc+=pc->branch;\n            }\n                break;\n            case MF_RESOLVE:\n                *out = pc->data.resolvefn;\n                return true;\n            case MF_FAIL:\n                return false;\n        }\n        pc++;\n    } while(true);\n}\n\n/* **********************************************************************\n * Metafunction veneer class\n * ********************************************************************** */\n\n/** Constructor function for Metafunctions */\nvalue metafunction_constructor(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    \n    if (nargs==0) return MORPHO_NIL;\n    \n    value name = _getname(MORPHO_GETARG(args, 0));\n    if (!MORPHO_ISSTRING(name)) return MORPHO_NIL;\n    \n    objectmetafunction *new = object_newmetafunction(name);\n    \n    if (new) {\n        for (int i=0; i<nargs; i++) {\n            metafunction_add(new, MORPHO_GETARG(args, i));\n        }\n        \n        error err;\n        error_init(&err);\n        if (!metafunction_compile(new, &err)) morpho_runtimeerror(v, err.id);\n        error_clear(&err);\n        \n        out=morpho_wrapandbind(v, (object *) new);\n    }\n    \n    return out;\n}\n\n/** Count the number of implementations in a metafunction */\nvalue Metafunction_count(vm *v, int nargs, value *args) {\n    objectmetafunction *fn = MORPHO_GETMETAFUNCTION(MORPHO_SELF(args));\n    \n    return MORPHO_INTEGER(fn->fns.count);\n}\n\nMORPHO_BEGINCLASS(Metafunction)\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Metafunction_count, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectmetafunctiontype;\n\nvoid metafunction_initialize(void) {\n    // Create function object type\n    objectmetafunctiontype=object_addtype(&objectmetafunctiondefn);\n    \n    // Locate the Object class to use as the parent class of Metafunction\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Metafunction constructor function\n    morpho_addfunction(METAFUNCTION_CLASSNAME, METAFUNCTION_CLASSNAME \" (...)\", metafunction_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Create function veneer class\n    value metafunctionclass=builtin_addclass(METAFUNCTION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Metafunction), objclass);\n    object_setveneerclass(OBJECT_METAFUNCTION, metafunctionclass);\n    \n    // Metafunction error messages\n    morpho_defineerror(METAFUNCTION_CMPLAMBGS, ERROR_PARSE, METAFUNCTION_CMPLAMBGS_MSG);\n}\n"
  },
  {
    "path": "src/classes/metafunction.h",
    "content": "/** @file metafunction.h\n *  @author T J Atherton\n *\n *  @brief Defines metafunction object type and Metafunction veneer class\n */\n\n#ifndef metafunction_h\n#define metafunction_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Metafunction objects\n * ------------------------------------------------------- */\n\nextern objecttype objectmetafunctiontype;\n#define OBJECT_METAFUNCTION objectmetafunctiontype\n\n/** Index type for metafunction resolver */\ntypedef int mfindx;\n\n/** Compiled metafunction instruction set */\ntypedef struct {\n    int opcode;\n    int narg;\n    union {\n        int tindx;\n        value resolvefn;\n        varray_int btable;\n    } data;\n    mfindx branch; /* Branch the pc by this amount on fail */\n} mfinstruction;\n\nDECLARE_VARRAY(mfinstruction, mfinstruction);\n\n/** A metafunction object */\ntypedef struct sobjectmetafunction {\n    object obj;\n    value name;\n    objectclass *klass; // Parent class for metafunction methods\n    varray_value fns;\n    varray_mfinstruction resolver;\n} objectmetafunction;\n\n/** Gets an objectmetafunction from a value */\n#define MORPHO_GETMETAFUNCTION(val)   ((objectmetafunction *) MORPHO_GETOBJECT(val))\n\n/** Tests whether an object is a metafunction */\n#define MORPHO_ISMETAFUNCTION(val) object_istype(val, OBJECT_METAFUNCTION)\n\n/* -------------------------------------------------------\n * Metafunction veneer class\n * ------------------------------------------------------- */\n\n#define METAFUNCTION_CLASSNAME \"Metafunction\"\n\n/* -------------------------------------------------------\n * Metafunction error messages\n * ------------------------------------------------------- */\n\n#define METAFUNCTION_CMPLAMBGS          \"MltplDisptchAmbg\"\n#define METAFUNCTION_CMPLAMBGS_MSG      \"Ambiguous or duplicate implementations in multiple dispatch.\"\n\n/* -------------------------------------------------------\n * Metafunction interface\n * ------------------------------------------------------- */\n\nobjectmetafunction *object_newmetafunction(value name);\nobjectmetafunction *metafunction_clone(objectmetafunction *f);\n\nbool metafunction_wrap(value name, value fn, value *out);\nbool metafunction_add(objectmetafunction *f, value fn);\nbool metafunction_typefromvalue(value v, value *out);\n\nvoid metafunction_setclass(objectmetafunction *f, objectclass *klass);\nobjectclass *metafunction_class(objectmetafunction *f);\n\nbool metafunction_matchfn(objectmetafunction *fn, value f);\nbool metafunction_matchset(objectmetafunction *fn, int n, value *fns);\nsignature *metafunction_getsignature(value fn);\n\nbool metafunction_compile(objectmetafunction *fn, error *err);\nvoid metafunction_clearinstructions(objectmetafunction *fn);\n\nbool metafunction_resolve(objectmetafunction *f, int nargs, value *args, error *err, value *fn);\n\nvoid metafunction_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/range.c",
    "content": "/** @file range.c\n *  @author T J Atherton\n *\n *  @brief Implements the Range class\n */\n\n#include <float.h>\n#include <limits.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * objectrange definitions\n * ********************************************************************** */\n\nvoid objectrange_printfn(object *obj, void *v) {\n    objectrange *r = (objectrange *) obj;\n    morpho_printvalue(v, r->start);\n    morpho_printf(v, (r->inclusive ? \"..\" : \"...\"));\n    morpho_printvalue(v, r->end);\n    if (!MORPHO_ISNIL(r->step)) {\n        morpho_printf(v, \":\");\n        morpho_printvalue(v, r->step);\n    }\n}\n\nsize_t objectrange_sizefn(object *obj) {\n    return sizeof(objectrange);\n}\n\nobjecttypedefn objectrangedefn = {\n    .printfn=objectrange_printfn,\n    .markfn=NULL,\n    .freefn=NULL,\n    .sizefn=objectrange_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Determine the number of steps in a range */\nbool _range_count(objectrange *range) {\n    range->nsteps=0;\n    if (MORPHO_ISFLOAT(range->start)) {\n        double diff=MORPHO_GETFLOATVALUE(range->end)-MORPHO_GETFLOATVALUE(range->start);\n        \n        double stp=(MORPHO_ISNIL(range->step) ? 1 : MORPHO_GETFLOATVALUE(range->step));\n        double cnt = floor(diff / stp);\n        \n        if (cnt>(double) INT_MAX) return false;\n        \n        if (isfinite(cnt)) range->nsteps = (int) cnt;\n        \n        if (range->inclusive) {\n            if (MORPHO_ISEQUAL(range->start, range->end)) range->nsteps=1;\n            else while (morpho_comparevalue(MORPHO_FLOAT(fabs(diff)), MORPHO_FLOAT(fabs(range->nsteps*stp)))<=0) range->nsteps++;\n            \n        } else {\n            while (morpho_comparevalue(MORPHO_FLOAT(fabs(diff)), MORPHO_FLOAT(fabs(range->nsteps*stp)))<0) range->nsteps++;\n        }\n    } else {\n        int diff=MORPHO_GETINTEGERVALUE(range->end)-MORPHO_GETINTEGERVALUE(range->start);\n        int stp=(MORPHO_ISNIL(range->step) ? 1 : MORPHO_GETINTEGERVALUE(range->step));\n        if (stp) range->nsteps = diff / stp ;\n        if (range->inclusive) {\n            if (diff==0) range->nsteps=1;\n            else if (range->nsteps*stp<=diff) range->nsteps++;\n        }\n    }\n    if (range->nsteps < 0) range->nsteps=0;\n\n    return true;\n}\n\n/** Create a new range. Step may be set to MORPHO_NIL to use the default value of 1.\n    @param[out] errid - errid is filled in if the range can't be initialized */\nobjectrange *object_newrange(value start, value end, value step, bool inclusive, errorid *errid) {\n    value v[3]={start, end, step};\n\n    /* Ensure all three values are either integer or floating point */\n    if (!value_promotenumberlist((MORPHO_ISNIL(step) ? 2 : 3), v)) {\n        *errid = RANGE_ARGS;\n        return NULL;\n    }\n\n    objectrange *new = (objectrange *) object_new(sizeof(objectrange), OBJECT_RANGE);\n\n    if (new) {\n        new->start=v[0];\n        new->end=v[1];\n        new->step=v[2];\n        new->inclusive=inclusive;\n        if (!_range_count(new)) {\n            *errid = RANGE_STPSZ;\n            object_free((object *) new);\n            new=NULL;\n        }\n    } else *errid = ERROR_ALLOCATIONFAILED;\n\n    return new;\n}\n\n/* **********************************************************************\n * objectrange utility functions\n * ********************************************************************** */\n\n/** Return the number of steps in a range */\nint range_count(objectrange *range) {\n    return range->nsteps;\n}\n\n/** Find the ith value of a range object */\nvalue range_iterate(objectrange *range, unsigned int i) {\n    if (MORPHO_ISFLOAT(range->start)) {\n        return MORPHO_FLOAT( MORPHO_GETFLOATVALUE(range->start) +\n                            i*(MORPHO_ISNIL(range->step) ? 1.0 : MORPHO_GETFLOATVALUE(range->step)));\n    } else {\n        return MORPHO_INTEGER( MORPHO_GETINTEGERVALUE(range->start) +\n                            i*(MORPHO_ISNIL(range->step) ? 1 : MORPHO_GETINTEGERVALUE(range->step)));\n    }\n}\n\n/* **********************************************************************\n * Range veneer class\n * ********************************************************************** */\n\nstatic value _rangeconstructor(vm *v, int nargs, value *args, bool inclusive) {\n    value out=MORPHO_NIL;\n    objectrange *new=NULL;\n\n    value in[3] = { MORPHO_NIL, MORPHO_NIL, MORPHO_NIL};\n    for (int i=0; i<nargs; i++) in[i]=MORPHO_GETARG(args, i);\n    \n    errorid errid=RANGE_ARGS;\n    new=object_newrange(in[0], in[1], in[2], inclusive, &errid);\n    \n    if (new) {\n        out = morpho_wrapandbind(v, (object *) new);\n    } else morpho_runtimeerror(v, errid);\n\n    return out;\n}\n\n/** Constructor functions for ranges */\nvalue range_constructor(vm *v, int nargs, value *args) {\n    return _rangeconstructor(v, nargs, args, false);\n}\n\nvalue range_inclusiveconstructor(vm *v, int nargs, value *args) {\n    return _rangeconstructor(v, nargs, args, true);\n}\n\n/** Default if incorrect args passed */\nvalue range_invldconstructor(vm *v, int nargs, value *args) {\n    morpho_runtimeerror(v, RANGE_ARGS);\n    return MORPHO_NIL;\n}\n\n/** Gets a specified element from a range */\nvalue Range_getindex(vm *v, int nargs, value *args) {\n    objectrange *slf = MORPHO_GETRANGE(MORPHO_SELF(args));\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<slf->nsteps) return range_iterate(slf, n);\n        else morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n    }\n\n    return MORPHO_SELF(args);\n}\n\n/** Enumerate members of a range */\nvalue Range_enumerate(vm *v, int nargs, value *args) {\n    objectrange *slf = MORPHO_GETRANGE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<0) return MORPHO_INTEGER(slf->nsteps);\n        else return range_iterate(slf, n);\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n    return out;\n}\n\n/** Count number of items in a range */\nvalue Range_count(vm *v, int nargs, value *args) {\n    objectrange *slf = MORPHO_GETRANGE(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(slf->nsteps);\n}\n\n/** Clones a range */\nvalue Range_clone(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    objectrange *slf = MORPHO_GETRANGE(MORPHO_SELF(args));\n    errorid errid=RANGE_ARGS;\n    objectrange *new = object_newrange(slf->start, slf->end, slf->step, slf->inclusive, &errid);\n\n    if (new) {\n        out = MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, errid);\n    \n    return out;\n}\n\nMORPHO_BEGINCLASS(Range)\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Range_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Range_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Range_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Range_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nobjecttype objectrangetype;\n\nvoid range_initialize(void) {\n    // Create range object type\n    objectrangetype=object_addtype(&objectrangedefn);\n    \n    // Locate the Object class to use as the parent class of Range\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Create range veneer class\n    value rangeclass=builtin_addclass(RANGE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Range), objclass);\n    object_setveneerclass(OBJECT_RANGE, rangeclass);\n    \n    // Range constructor function\n    morpho_addfunction(RANGE_CLASSNAME, RANGE_CLASSNAME \" (_,_)\", range_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    morpho_addfunction(RANGE_CLASSNAME, RANGE_CLASSNAME \" (_,_,_)\", range_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    morpho_addfunction(RANGE_CLASSNAME, RANGE_CLASSNAME \" (...)\", range_invldconstructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Inclusive range constructor\n    morpho_addfunction(RANGE_INCLUSIVE_CONSTRUCTOR, RANGE_CLASSNAME \" (_,_)\", range_inclusiveconstructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    morpho_addfunction(RANGE_INCLUSIVE_CONSTRUCTOR, RANGE_CLASSNAME \" (_,_,_)\", range_inclusiveconstructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    morpho_addfunction(RANGE_INCLUSIVE_CONSTRUCTOR, RANGE_CLASSNAME \" (...)\", range_invldconstructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Range error messages\n    morpho_defineerror(RANGE_ARGS, ERROR_HALT, RANGE_ARGS_MSG);\n    morpho_defineerror(RANGE_STPSZ, ERROR_HALT, RANGE_STPSZ_MSG);\n}\n"
  },
  {
    "path": "src/classes/range.h",
    "content": "/** @file range.h\n *  @author T J Atherton\n *\n *  @brief Defines range object type and Range class\n */\n\n#ifndef range_h\n#define range_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Range objects\n * ------------------------------------------------------- */\n\nextern objecttype objectrangetype;\n#define OBJECT_RANGE objectrangetype\n\ntypedef struct {\n    object obj;\n    unsigned int nsteps;\n    value start;\n    value end;\n    value step;\n    bool inclusive; \n} objectrange;\n\n/** Tests whether an object is a range */\n#define MORPHO_ISRANGE(val) object_istype(val, OBJECT_RANGE)\n\n/** Gets the object as a range */\n#define MORPHO_GETRANGE(val)   ((objectrange *) MORPHO_GETOBJECT(val))\n\n/** Creates a new range object */\nobjectrange *object_newrange(value start, value end, value step, bool inclusive, errorid *errid);\n\n/* -------------------------------------------------------\n * Range veneer class\n * ------------------------------------------------------- */\n\n#define RANGE_CLASSNAME                   \"Range\"\n\n#define RANGE_INCLUSIVE_CONSTRUCTOR       \"InclusiveRange\"\n\n/* -------------------------------------------------------\n * Range error messages\n * ------------------------------------------------------- */\n\n#define RANGE_ARGS                        \"RngArgs\"\n#define RANGE_ARGS_MSG                    \"Range expects numerical arguments: a start, an end and an optional stepsize.\"\n\n#define RANGE_STPSZ                       \"RngStpSz\"\n#define RANGE_STPSZ_MSG                   \"Range stepsize too small.\"\n\n/* -------------------------------------------------------\n * Range interface\n * ------------------------------------------------------- */\n\nint range_count(objectrange *range);\nvalue range_iterate(objectrange *range, unsigned int i);\n\nvoid range_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/strng.c",
    "content": "/** @file strng.c\n *  @author T J Atherton\n *\n *  @brief Defines string object type and String class\n */\n\n#include <stdio.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"lex.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n * String objects\n * ********************************************************************** */\n\n/** String object definitions */\nvoid objectstring_printfn(object *obj, void *v) {\n    morpho_printf(v, \"%s\", ((objectstring *) obj)->string);\n}\n\nsize_t objectstring_sizefn(object *obj) {\n    return sizeof(objectstring)+((objectstring *) obj)->length+1;\n}\n\nhash objectstring_hashfn(object *obj) {\n    objectstring *str = (objectstring *) obj;\n    return dictionary_hashcstring(str->string, str->length);\n}\n\nint objectstring_cmpfn(object *a, object *b) {\n    objectstring *astring = (objectstring *) a;\n    objectstring *bstring = (objectstring *) b;\n    size_t len = (astring->length > bstring->length ? astring->length : bstring->length);\n\n    return -strncmp(astring->string, bstring->string, len);\n}\n\nobjecttypedefn objectstringdefn = {\n    .printfn = objectstring_printfn,\n    .markfn = NULL,\n    .freefn = NULL,\n    .sizefn = objectstring_sizefn,\n    .hashfn = objectstring_hashfn,\n    .cmpfn = objectstring_cmpfn\n};\n\n/** @brief Creates a string from an existing character array with given length\n *  @param in     the string to copy\n *  @param length length of string to copy\n *  @returns the object (as a value) which will be MORPHO_NIL on failure */\nvalue object_stringfromcstring(const char *in, size_t length) {\n    value out = MORPHO_NIL;\n    objectstring *new = (objectstring *) object_new(sizeof(objectstring) + sizeof(char) * (length + 1), OBJECT_STRING);\n\n    if (new) {\n        new->string=new->stringdata;\n        if (in) {\n            memcpy(new->string, in, length);\n        } else {\n            memset(new->string, 0, length);\n        }\n        new->string[length] = '\\0'; /* Zero terminate the string to be compatible with C */\n        new->length=strlen(new->string);\n        out = MORPHO_OBJECT(new);\n    }\n    return out;\n}\n\n/** @brief Creates a string with given length\n *  @param length length of string to allocate\n *  @returns the object (as a value) which will be MORPHO_NIL on failure */\nobjectstring *object_stringwithsize(size_t length) {\n    objectstring *new = (objectstring *) object_new(sizeof(objectstring) + sizeof(char) * (length + 1), OBJECT_STRING);\n\n    if (new) {\n        new->string=new->stringdata;\n        new->string[length] = '\\0'; // Ensure pre-null terminated\n        memset(new->string, 0, length);\n        new->length=length;\n        return new;\n    }\n    return NULL;\n}\n\n/** @brief Converts a varray_char into a string.\n *  @param in  the varray to convert\n *  @returns the object (as a value) which will be MORPHO_NIL on failure */\nvalue object_stringfromvarraychar(varray_char *in) {\n    return object_stringfromcstring(in->data, in->count);\n}\n\n\n/* Clones a string object */\nvalue object_clonestring(value val) {\n    value out = MORPHO_NIL;\n    if (MORPHO_ISSTRING(val)) {\n        objectstring *s = MORPHO_GETSTRING(val);\n        out=object_stringfromcstring(s->string, s->length);\n    }\n    return out;\n}\n\n/** @brief Concatenates strings together\n *  @param a      first string\n *  @param b      second string\n *  @returns the object (as a value) which will be MORPHO_NIL on failure  */\nvalue object_concatenatestring(value a, value b) {\n    objectstring *astring = MORPHO_GETSTRING(a);\n    objectstring *bstring = MORPHO_GETSTRING(b);\n    size_t length = (astring ? astring->length : 0) + (bstring ? bstring->length : 0);\n    value out = MORPHO_NIL;\n\n    objectstring *new = (objectstring *) object_new(sizeof(objectstring) + sizeof(char) * (length + 1), OBJECT_STRING);\n\n    if (new) {\n        new->string=new->stringdata;\n        new->length=length;\n        /* Copy across old strings */\n        if (astring) memcpy(new->string, astring->string, astring->length);\n        if (bstring) memcpy(new->string+(astring ? astring->length : 0), bstring->string, bstring->length);\n        new->string[length]='\\0';\n        out = MORPHO_OBJECT(new);\n    }\n    return out;\n}\n\n/* **********************************************************************\n * String utility functions\n * ********************************************************************** */\n\n/** Convert a string to a number */\nbool string_tonumber(objectstring *string, value *out) {\n    bool minus=false;\n    lexer l;\n    token tok;\n    error err;\n    error_init(&err);\n    lex_init(&l, string->string, 0);\n\n    if (lex(&l, &tok, &err)) {\n        if (tok.type==TOKEN_MINUS) { // Check for leading minus\n            minus=true;\n            if (!lex(&l, &tok, &err)) return false;\n        } else if (tok.type==TOKEN_PLUS) { // or plus\n            if (!lex(&l, &tok, &err)) return false;\n        }\n\n        if (tok.type==TOKEN_INTEGER) {\n            long i = strtol(tok.start, NULL, 10);\n            if (minus) i=-i;\n            *out = MORPHO_INTEGER((int) i);\n            return true;\n        } else if (tok.type==TOKEN_NUMBER) {\n            double f = strtod(tok.start, NULL);\n            if (minus) f=-f;\n            *out = MORPHO_FLOAT(f);\n            return true;\n        }\n    }\n    lex_clear(&l);\n\n    return false;\n}\n\n/** Count number of characters in a string */\nint string_countchars(objectstring *s) {\n    int n=0;\n    for (char *c = s->string; *c!='\\0'; ) {\n        c+=morpho_utf8numberofbytes(c);\n        n++;\n    }\n    return n;\n}\n\n/** Get a pointer to the i'th character of a string */\nchar *string_index(objectstring *s, int i) {\n    int n=0;\n    for (char *c = s->string; *c!='\\0'; ) {\n        if (i==n) return (char *) c;\n        c+=morpho_utf8numberofbytes(c);\n        n++;\n    }\n    return NULL;\n}\n\n/* **********************************************************************\n * String class\n * ********************************************************************** */\n\n/** Constructor function for strings */\nvalue string_constructor(vm *v, int nargs, value *args) {\n    value out=morpho_concatenate(v, nargs, args+1);\n    if (MORPHO_ISOBJECT(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\n/** Find a string's length */\nvalue String_count(vm *v, int nargs, value *args) {\n    objectstring *slf = MORPHO_GETSTRING(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(string_countchars(slf));\n}\n\n/** Prints a string */\nvalue String_print(vm *v, int nargs, value *args) {\n    morpho_printvalue(v, MORPHO_SELF(args));\n\n    return MORPHO_SELF(args);\n}\n\n/** Clones a string */\nvalue String_clone(vm *v, int nargs, value *args) {\n    objectstring *slf = MORPHO_GETSTRING(MORPHO_SELF(args));\n    value out = object_stringfromcstring(slf->string, slf->length);\n    if (MORPHO_ISNIL(out)) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\n/** Enumerate members of a string */\nvalue String_enumerate(vm *v, int nargs, value *args) {\n    objectstring *slf = MORPHO_GETSTRING(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<0) {\n            out=MORPHO_INTEGER(string_countchars(slf));\n        } else {\n            char *c=string_index(slf, n);\n            if (c) {\n                out=object_stringfromcstring(c, morpho_utf8numberofbytes(c));\n                morpho_bindobjects(v, 1, &out);\n            } else morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n        }\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n    return out;\n}\n\n/** Tests if a string encodes a number */\nvalue String_isnumber(vm *v, int nargs, value *args) {\n    objectstring *slf = MORPHO_GETSTRING(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (string_tonumber(slf, &out)) return MORPHO_TRUE;\n\n    return MORPHO_FALSE;\n}\n\n/** Splits a string */\nvalue String_split(vm *v, int nargs, value *args) {\n    objectstring *slf = MORPHO_GETSTRING(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        objectstring *split = MORPHO_GETSTRING(MORPHO_GETARG(args, 0));\n        objectlist *new = object_newlist(0, NULL);\n\n        if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return MORPHO_NIL; }\n\n        char *last = slf->string;\n        for (char *c = slf->string; *c!='\\0'; c+=morpho_utf8numberofbytes(c)) { // Loop over string\n            for (char *s = split->string; *s!='\\0';) { // Loop over split chars\n                int nbytes = morpho_utf8numberofbytes(s);\n                if (strncmp(c, s, nbytes)==0) {\n                    value newstring = object_stringfromcstring(last, c-last);\n                    if (MORPHO_ISNIL(newstring)) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n                    list_append(new, newstring);\n                    last=c+nbytes;\n                }\n                s+=nbytes;\n            }\n        }\n\n        value newstring = object_stringfromcstring(last, slf->string+slf->length-last);\n        if (MORPHO_ISNIL(newstring)) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        list_append(new, newstring);\n\n        out=MORPHO_OBJECT(new);\n        list_append(new, out);\n        morpho_bindobjects(v, new->val.count, new->val.data);\n        new->val.count-=1;\n    }\n\n    return out;\n}\n\nMORPHO_BEGINCLASS(String)\nMORPHO_METHOD(MORPHO_COUNT_METHOD, String_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, String_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, String_clone, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, String_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, String_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(STRING_ISNUMBER_METHOD, String_isnumber, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(STRING_SPLIT_METHOD, String_split, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nobjecttype objectstringtype;\n\nvoid string_initialize(void) {\n    // Create string object type\n    //objectstringtype=object_addtype(&objectstringdefn);\n    \n    // Locate the Object class to use as the parent class of Range\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Create String veneer class\n    value stringclass=builtin_addclass(STRING_CLASSNAME, MORPHO_GETCLASSDEFINITION(String), objclass);\n    object_setveneerclass(OBJECT_STRING, stringclass);\n    \n    // String constructor function\n    morpho_addfunction(STRING_CLASSNAME, STRING_CLASSNAME \" (...)\", string_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n}\n"
  },
  {
    "path": "src/classes/strng.h",
    "content": "/** @file strng.h\n *  @author T J Atherton\n *\n *  @brief Defines string object type and String class\n */\n\n#ifndef strng_h\n#define strng_h\n\n#include <string.h>\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * String object type\n * ------------------------------------------------------- */\n\nextern objecttype objectstringtype;\n#define OBJECT_STRING objectstringtype\n\n/** A string object */\ntypedef struct {\n    object obj;\n    size_t length;\n    char *string;\n    char stringdata[];\n} objectstring;\n\n/** Tests whether an object is a string */\n#define MORPHO_ISSTRING(val) object_istype(val, OBJECT_STRING)\n\n/** Extracts the objectstring from a value */\n#define MORPHO_GETSTRING(val)             ((objectstring *) MORPHO_GETOBJECT(val))\n\n/** Extracts a C string from a value */\n#define MORPHO_GETCSTRING(val)            (((objectstring *) MORPHO_GETOBJECT(val))->string)\n\n/** Extracts the string length from a value */\n#define MORPHO_GETSTRINGLENGTH(val)       (((objectstring *) MORPHO_GETOBJECT(val))->length)\n\n/** Use to create static strings on the C stack */\n#define MORPHO_STATICSTRING(cstring)      { .obj.type=OBJECT_STRING, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .string=cstring, .length=strlen(cstring) }\n\n/** Use to create static strings on the C stack */\n#define MORPHO_STATICSTRINGWITHLENGTH(cstring, len)      { .obj.type=OBJECT_STRING, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .string=cstring, .length=len }\n\n\n#define OBJECT_STRINGLABEL \"string\" // These are only used by the parser... Should be moved?\n#define OBJECT_SYMBOLLABEL \"symbol\"\n\n/** Create a string object from a C string */\nvalue object_stringfromcstring(const char *in, size_t length);\n\n/** Create an empty string of specified size */\nobjectstring *object_stringwithsize(size_t length);\n\n/** Create a string object from a character varray */\nvalue object_stringfromvarraychar(varray_char *in);\n\n/** Clone a string */\nvalue object_clonestring(value val);\n\n/** Concatenate two strings */\nvalue object_concatenatestring(value a, value b);\n\n/* -------------------------------------------------------\n * String veneer class\n * ------------------------------------------------------- */\n\n#define STRING_CLASSNAME                  \"String\"\n\n#define STRING_SPLIT_METHOD               \"split\"\n#define STRING_ISNUMBER_METHOD            \"isnumber\"\n\n/* -------------------------------------------------------\n * String error messages\n * ------------------------------------------------------- */\n\n/* -------------------------------------------------------\n * String interface\n * ------------------------------------------------------- */\n\nbool string_tonumber(objectstring *string, value *out);\nint string_countchars(objectstring *s);\nchar *string_index(objectstring *s, int i);\n\nvoid string_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/system.c",
    "content": "/** @file system.c\n *  @author T J Atherton\n *\n *  @brief Defines System class to provide access to the runtime and system\n */\n\n#include <stdio.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"system.h\"\n#include \"platform.h\"\n\n/* **********************************************************************\n * System utility functions\n * ********************************************************************** */\n\n/** Set arguments passed to morpho program */\nstatic value arglist;\n\n/** Set arguments with which the host program was called with */\nvoid morpho_setargs(int argc, const char * argv[]) {\n    if (!MORPHO_ISLIST(arglist)) return;\n    objectlist *alist = MORPHO_GETLIST(arglist);\n    for (int i=0; i<argc; i++) {\n        value arg = object_stringfromcstring(argv[i], strlen(argv[i]));\n        if (MORPHO_ISSTRING(arg)) list_append(alist, arg);\n    }\n}\n\n/** Free arguments */\nvoid system_freeargs(void) {\n    if (!MORPHO_ISLIST(arglist)) return;\n    objectlist *alist = MORPHO_GETLIST(arglist);\n    \n    for (int i=0; i<list_length(alist); i++) {\n        value el;\n        if (!list_getelement(alist, i, &el)) continue;\n        morpho_freeobject(el);\n    }\n    morpho_freeobject(arglist);\n}\n\n/* **********************************************************************\n * System class\n * ********************************************************************* */\n\n/** Returns a platform description */\nvalue System_platform(vm *v, int nargs, value *args) {\n    const char *platform = platform_name();\n    value ret = MORPHO_NIL;\n    \n    if (platform) {\n        ret = object_stringfromcstring(platform, strlen(platform));\n        morpho_bindobjects(v, 1, &ret);\n    }\n    \n    return ret;\n}\n\n/** Returns the version descriptor */\nvalue System_version(vm *v, int nargs, value *args) {\n    value ret = object_stringfromcstring(MORPHO_VERSIONSTRING, strlen(MORPHO_VERSIONSTRING));\n    morpho_bindobjects(v, 1, &ret);\n    \n    return ret;\n}\n\n/** Clock */\nvalue System_clock(vm *v, int nargs, value *args) {\n    return MORPHO_FLOAT(platform_clock());\n}\n\n/** Print */\nvalue System_print(vm *v, int nargs, value *args) {\n    for (int i=0; i<nargs; i++) morpho_printvalue(v, MORPHO_GETARG(args, i));\n    return MORPHO_NIL;\n}\n\n/** Sleep for a specified number of seconds */\nvalue System_sleep(vm *v, int nargs, value *args) {\n    if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double t;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &t)) {\n            platform_sleep((int) (1000*t));\n        }\n    } else morpho_runtimeerror(v, SLEEP_ARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Readline */\nvalue System_readline(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    varray_char buffer;\n    varray_charinit(&buffer);\n    \n    if (morpho_readline(v, &buffer)) {\n        out = object_stringfromvarraychar(&buffer);\n        if (MORPHO_ISSTRING(out)) morpho_bindobjects(v, 1, &out);\n    }\n\n    varray_charclear(&buffer);\n    \n    return out;\n}\n\n/** Arguments passed to the process */\nvalue System_arguments(vm *v, int nargs, value *args) {\n    return arglist;\n}\n\n/** Exit */\nvalue System_exit(vm *v, int nargs, value *args) {\n    morpho_runtimeerror(v, VM_EXIT);\n    return MORPHO_NIL;\n}\n\n/** Set working folder */\nvalue System_setworkingfolder(vm *v, int nargs, value *args) {\n    if (nargs==1 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        char *path = MORPHO_GETCSTRING(MORPHO_GETARG(args, 0));\n        \n        if (platform_setcurrentdirectory(path)) morpho_runtimeerror(v, SYS_STWRKDR);\n    } else morpho_runtimeerror(v, STWRKDR_ARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Get working folder */\nvalue System_workingfolder(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    \n    size_t size = platform_maxpathsize();\n    char str[size];\n\n    if (platform_getcurrentdirectory(str, size)) {\n        out = object_stringfromcstring(str, strlen(str));\n        if (MORPHO_ISOBJECT(out)) {\n            morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    \n    return out;\n}\n\n/** Get current user's home folder */\nvalue System_homefolder(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n\n    size_t size = platform_maxpathsize();\n    char str[size];\n\n    if (platform_gethomedirectory(str, size)) {\n        out = object_stringfromcstring(str, strlen(str));\n        if (MORPHO_ISOBJECT(out)) {\n            morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    \n    return out;\n}\n\nMORPHO_BEGINCLASS(System)\nMORPHO_METHOD(SYSTEM_PLATFORM_METHOD, System_platform, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_VERSION_METHOD, System_version, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_CLOCK_METHOD, System_clock, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, System_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_SLEEP_METHOD, System_sleep, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_READLINE_METHOD, System_readline, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_ARGUMENTS_METHOD, System_arguments, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_EXIT_METHOD, System_exit, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_SETWORKINGFOLDER_METHOD, System_setworkingfolder, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_WORKINGFOLDER_METHOD, System_workingfolder, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SYSTEM_HOMEFOLDER_METHOD, System_homefolder, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nvoid system_initialize(void) {\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    builtin_addclass(SYSTEM_CLASSNAME, MORPHO_GETCLASSDEFINITION(System), objclass);\n    \n    morpho_defineerror(SLEEP_ARGS, ERROR_HALT, SLEEP_ARGS_MSG);\n    morpho_defineerror(VM_EXIT, ERROR_EXIT, VM_EXIT_MSG);\n    morpho_defineerror(SYS_STWRKDR, ERROR_EXIT, SYS_STWRKDR_MSG);\n    morpho_defineerror(STWRKDR_ARGS, ERROR_EXIT, STWRKDR_ARGS_MSG);\n    \n    objectlist *alist = object_newlist(0, NULL);\n    if (alist) arglist = MORPHO_OBJECT(alist);\n    \n    morpho_addfinalizefn(system_finalize);\n}\n\nvoid system_finalize(void) {\n    system_freeargs();\n}\n"
  },
  {
    "path": "src/classes/system.h",
    "content": "/** @file system.h\n *  @author T J Atherton\n *\n *  @brief Defines System class to provide access to the runtime and system\n */\n\n#ifndef system_h\n#define system_h\n\n#include <stdio.h>\n#include \"morpho.h\"\n\n/* -------------------------------------------------------\n * System class\n * ------------------------------------------------------- */\n\n#define SYSTEM_CLASSNAME              \"System\"\n\n#define SYSTEM_PLATFORM_METHOD        \"platform\"\n#define SYSTEM_VERSION_METHOD         \"version\"\n#define SYSTEM_CLOCK_METHOD           \"clock\"\n#define SYSTEM_READLINE_METHOD        \"readline\"\n#define SYSTEM_SLEEP_METHOD           \"sleep\"\n#define SYSTEM_ARGUMENTS_METHOD       \"arguments\"\n#define SYSTEM_EXIT_METHOD            \"exit\"\n\n#define SYSTEM_HOMEFOLDER_METHOD      \"homefolder\"\n#define SYSTEM_WORKINGFOLDER_METHOD   \"workingfolder\"\n#define SYSTEM_SETWORKINGFOLDER_METHOD \"setworkingfolder\"\n\n/* -------------------------------------------------------\n * System error messages\n * ------------------------------------------------------- */\n\n#define SLEEP_ARGS                    \"SystmSlpArgs\"\n#define SLEEP_ARGS_MSG                \"Sleep method expects a time in seconds.\"\n\n#define STWRKDR_ARGS                  \"SystmStWrkDrArgs\"\n#define STWRKDR_ARGS_MSG              \"Setworkingdirectory method expects a path name.\"\n\n#define SYS_STWRKDR                   \"SystmStWrkDr\"\n#define SYS_STWRKDR_MSG               \"Couldn't set working directory.\"\n\nvoid system_initialize(void);\nvoid system_finalize(void);\n\n#endif /* system_h */\n"
  },
  {
    "path": "src/classes/tuple.c",
    "content": "/** @file tuple.c\n *  @author T J Atherton\n *\n *  @brief Defines tuple object type and Tuple class\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * Tuple objects\n * ********************************************************************** */\n\n/** Tuple object definitions */\nvoid objecttuple_printfn(object *obj, void *v) {\n    objecttuple *t = (objecttuple *) obj;\n    morpho_printf(v, \"(\");\n    for (unsigned int i=0; i<t->length; i++) {\n        morpho_printvalue(v, t->tuple[i]);\n        if (i<t->length-1) morpho_printf(v, \", \");\n    }\n    morpho_printf(v, \")\");\n}\n\nvoid objecttuple_markfn(object *obj, void *v) {\n    objecttuple *t = (objecttuple *) obj;\n    for (unsigned int i=0; i<t->length; i++) morpho_markvalue(v, t->tuple[i]);\n}\n\nsize_t objecttuple_sizefn(object *obj) {\n    return sizeof(objecttuple)+(((objecttuple *) obj)->length)*sizeof(value);\n}\n\nhash objecttuple_hashfn(object *obj) {\n    objecttuple *tuple = (objecttuple *) obj;\n    return dictionary_hashvaluelist(tuple->length, tuple->tuple);\n}\n\nint objecttuple_cmpfn(object *a, object *b) {\n    objecttuple *atuple = (objecttuple *) a;\n    objecttuple *btuple = (objecttuple *) b;\n    \n    if (atuple->length!=btuple->length) return MORPHO_NOTEQUAL;\n\n    int cmp=0;\n    for (unsigned int i=0; i<atuple->length && cmp==0; i++) {\n        cmp=morpho_comparevalue(atuple->tuple[i], btuple->tuple[i]);\n    }\n    \n    return cmp;\n}\n\nobjecttypedefn objecttupledefn = {\n    .printfn = objecttuple_printfn,\n    .markfn = objecttuple_markfn,\n    .freefn = NULL,\n    .sizefn = objecttuple_sizefn,\n    .hashfn = objecttuple_hashfn,\n    .cmpfn = objecttuple_cmpfn\n};\n\n/** @brief Creates a tuple from an existing C array of values\n *  @param length length of list\n *  @param in list of values\n *  @returns the object or NULL on failure */\nobjecttuple *object_newtuple(unsigned int length, value *in) {\n    objecttuple *new = (objecttuple *) object_new(sizeof(objecttuple) + sizeof(value)*length, OBJECT_TUPLE);\n\n    if (new) {\n        new->tuple=new->tupledata;\n        new->length=length;\n        if (in) memcpy(new->tuple, in, sizeof(value)*length);\n        else for (unsigned int i=0; i<length; i++) new->tuple[i]=MORPHO_NIL;\n    }\n    return new;\n}\n\n/* **********************************************************************\n * Tuple interface\n * ********************************************************************** */\n\n/** Returns the length of a tuple */\nunsigned int tuple_length(objecttuple *tuple) {\n    return tuple->length;\n}\n\n/** Gets an element from the tuple */\nbool tuple_getelement(objecttuple *tuple, int i, value *out) {\n    if (!(i>=-(int) tuple->length && i<(int) tuple->length)) return false;\n    if (i>=0) *out=tuple->tuple[i];\n    else *out=tuple->tuple[tuple->length+i];\n    return true;\n}\n\n/** Tests if a value is a member of a list */\nbool tuple_ismember(objecttuple *tuple, value v) {\n    for (unsigned int i=0; i<tuple->length; i++) {\n        if (MORPHO_ISEQUAL(tuple->tuple[i], v)) return true;\n    }\n    return false;\n}\n\n/** Concatenates two tuples */\nobjecttuple *tuple_concatenate(objecttuple *a, objecttuple *b) {\n    unsigned int newlength = a->length+b->length;\n    objecttuple *new=object_newtuple(newlength, NULL);\n\n    if (new) {\n        memcpy(new->tuple, a->tuple, sizeof(value)*a->length);\n        memcpy(new->tuple + a->length, b->tuple, sizeof(value)*b->length);\n        new->length=newlength;\n    }\n\n    return new;\n}\n\n/* -------------------------------------------------------\n * Slicing\n * ------------------------------------------------------- */\n\n/* Constructs a new list of a given size with a generic interface */\nvoid tuple_sliceconstructor(unsigned int *slicesize, unsigned int ndim, value *out){\n    objecttuple *tuple = object_newtuple(slicesize[0], NULL);\n    *out = MORPHO_OBJECT(tuple);\n}\n\n/* Return number of dimensions to slice-there's only one */\nbool tuple_slicedim(value *a, unsigned int ndim){\n    if (ndim>1||ndim<0) return false;\n    return true;\n}\n\n/* Copies data from tuple a at position indx to tuple out at position newindx with a generic interface */\nobjectarrayerror tuple_slicecopy(value *a,value *out, unsigned int ndim, unsigned int *indx, unsigned int *newindx){\n    value data;\n    objecttuple *outtuple = MORPHO_GETTUPLE(*out);\n\n    if (tuple_getelement(MORPHO_GETTUPLE(*a),indx[0],&data)){\n        outtuple->tuple[newindx[0]] = data;\n    } else return ARRAY_OUTOFBOUNDS;\n    return ARRAY_OK;\n}\n\n/** Converts an array error into an list error code for use in slices*/\nerrorid array_to_tuple_error(objectarrayerror err) {\n    switch (err) {\n        case ARRAY_OUTOFBOUNDS: return VM_OUTOFBOUNDS;\n        case ARRAY_WRONGDIM: return TUPLE_NUMARGS;\n        case ARRAY_NONINTINDX: return TUPLE_ARGS;\n        case ARRAY_ALLOC_FAILED: return ERROR_ALLOCATIONFAILED;\n        case ARRAY_OK: UNREACHABLE(\"array_to_tuple_error called incorrectly.\");\n    }\n    UNREACHABLE(\"Unhandled array error.\");\n    return VM_OUTOFBOUNDS;\n}\n\n/* **********************************************************************\n * Tuple class\n * ********************************************************************** */\n\n/** Constructor function for tuples */\nvalue tuple_constructor(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    objecttuple *new=object_newtuple(nargs, & MORPHO_GETARG(args, 0));\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    return out;\n}\n\n/** Find a tuple's length */\nvalue Tuple_count(vm *v, int nargs, value *args) {\n    objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(slf->length);\n}\n\n/** Clones a tuple */\nvalue Tuple_clone(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args));\n    objecttuple *new = object_newtuple(slf->length, slf->tuple);\n    \n    if (new) {\n        out = MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    \n    return out;\n}\n\n/** Get an element */\nvalue Tuple_getindex(vm *v, int nargs, value *args) {\n    objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n            if (!tuple_getelement(slf, i, &out)) morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n        } else {\n            objectarrayerror err = getslice(&MORPHO_SELF(args), tuple_slicedim, tuple_sliceconstructor, tuple_slicecopy, nargs, &MORPHO_GETARG(args, 0), &out);\n            if (err!=ARRAY_OK) MORPHO_RAISE(v, array_to_tuple_error(err) );\n            if (MORPHO_ISOBJECT(out)){\n                morpho_bindobjects(v,1,&out);\n            } else MORPHO_RAISE(v, VM_NONNUMINDX);\n        }\n    } else MORPHO_RAISE(v, LIST_NUMARGS)\n\n    return out;\n}\n\n/** Setindex just raises an error */\nvalue Tuple_setindex(vm *v, int nargs, value *args) {\n    morpho_runtimeerror(v, OBJECT_IMMUTABLE);\n    return MORPHO_NIL;\n}\n\n/** Enumerate members of a tuple */\nvalue Tuple_enumerate(vm *v, int nargs, value *args) {\n    objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        int n=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n        if (n<0) {\n            out=MORPHO_INTEGER(slf->length);\n        } else {\n            if (n<slf->length) {\n                out=slf->tuple[n];\n            } else morpho_runtimeerror(v, VM_OUTOFBOUNDS);\n        }\n    } else MORPHO_RAISE(v, ENUMERATE_ARGS);\n\n    return out;\n}\n\n/** Joins two tuples together  */\nvalue Tuple_join(vm *v, int nargs, value *args) {\n    objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISTUPLE(MORPHO_GETARG(args, 0))) {\n        objecttuple *operand = MORPHO_GETTUPLE(MORPHO_GETARG(args, 0));\n        objecttuple *new = tuple_concatenate(slf, operand);\n\n        if (new) {\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n\n    } else morpho_runtimeerror(v, LIST_ADDARGS);\n\n    return out;\n}\n\n/** Tests if a tuple has a value as a member */\nvalue Tuple_ismember(vm *v, int nargs, value *args) {\n    objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args));\n\n    if (nargs==1) {\n        return MORPHO_BOOL(tuple_ismember(slf, MORPHO_GETARG(args, 0)));\n    } else morpho_runtimeerror(v, ISMEMBER_ARG, 1, nargs);\n\n    return MORPHO_NIL;\n}\n\nMORPHO_BEGINCLASS(Tuple)\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Tuple_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Tuple_clone, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Tuple_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Tuple_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Tuple_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_JOIN_METHOD, Tuple_join, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(LIST_ISMEMBER_METHOD, Tuple_ismember, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CONTAINS_METHOD, Tuple_ismember, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nobjecttype objecttupletype;\n\nvoid tuple_initialize(void) {\n    // Create tuple object type\n    objecttupletype=object_addtype(&objecttupledefn);\n    \n    // Locate the Object class to use as the parent class of Range\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    // Create tuple veneer class\n    value tupleclass=builtin_addclass(TUPLE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Tuple), objclass);\n    object_setveneerclass(OBJECT_TUPLE, tupleclass);\n    \n    // Tuple constructor function\n    morpho_addfunction(TUPLE_CLASSNAME, TUPLE_CLASSNAME \" (...)\", tuple_constructor, MORPHO_FN_CONSTRUCTOR, NULL);\n    \n    // Tuple error messages\n    morpho_defineerror(TUPLE_ARGS, ERROR_HALT, TUPLE_ARGS_MSG);\n    morpho_defineerror(TUPLE_NUMARGS, ERROR_HALT, TUPLE_NUMARGS_MSG);\n}\n"
  },
  {
    "path": "src/classes/tuple.h",
    "content": "/** @file tuple.h\n *  @author T J Atherton\n *\n *  @brief Defines tuple object type and Tuple class\n */\n\n#ifndef tuple_h\n#define tuple_h\n\n#include \"object.h\"\n\n/* -------------------------------------------------------\n * Tuple object type\n * ------------------------------------------------------- */\n\nextern objecttype objecttupletype;\n#define OBJECT_TUPLE objecttupletype\n\n/** A string object */\ntypedef struct {\n    object obj;\n    unsigned int length;\n    value *tuple;\n    value tupledata[];\n} objecttuple;\n\n/** Tests whether an object is a tuple */\n#define MORPHO_ISTUPLE(val) object_istype(val, OBJECT_TUPLE)\n\n/** Extracts the objecttuple from a value */\n#define MORPHO_GETTUPLE(val)             ((objecttuple *) MORPHO_GETOBJECT(val))\n\n/** Extracts the length from a value */\n#define MORPHO_GETTUPLELENGTH(val)             ((MORPHO_GETTUPLE(val))->length)\n\n/** Extracts the value list from a value */\n#define MORPHO_GETTUPLEVALUES(val)             ((MORPHO_GETTUPLE(val))->tuple)\n\n/** Use to create static tuples on the C stack */\n#define MORPHO_STATICTUPLE(list, len)      { .obj.type=OBJECT_TUPLE, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .tuple=list, .length=len }\n\n/* -------------------------------------------------------\n * Tuple veneer class\n * ------------------------------------------------------- */\n\n#define TUPLE_CLASSNAME                   \"Tuple\"\n\n/* -------------------------------------------------------\n * Tuple error messages\n * ------------------------------------------------------- */\n\n#define TUPLE_ARGS                         \"TplArgs\"\n#define TUPLE_ARGS_MSG                     \"Tuples must be called with integer dimensions as arguments.\"\n\n#define TUPLE_NUMARGS                      \"TpmNumArgs\"\n#define TUPLE_NUMARGS_MSG                  \"Tuples can only be indexed with one argument.\"\n\n/* -------------------------------------------------------\n * Tuple interface\n * ------------------------------------------------------- */\n\n/** Create a tuple with an (optional) list of values */\nobjecttuple *object_newtuple(unsigned int length, value *v);\n\nunsigned int tuple_length(objecttuple *tuple);\nbool tuple_getelement(objecttuple *tuple, int i, value *out);\n\nvoid tuple_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/classes/upvalue.c",
    "content": "/** @file upvalue.c\n *  @author T J Atherton\n *\n *  @brief Implements upvalue object type\n */\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * objectupvalue definitions\n * ********************************************************************** */\n\n/** Upvalue object definitions */\nvoid objectupvalue_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Upvalue>\");\n}\n\nvoid objectupvalue_markfn(object *obj, void *v) {\n    morpho_markvalue(v, ((objectupvalue *) obj)->closed);\n}\n\nsize_t objectupvalue_sizefn(object *obj) {\n    return sizeof(objectupvalue);\n}\n\nobjecttypedefn objectupvaluedefn = {\n    .printfn=objectupvalue_printfn,\n    .markfn=objectupvalue_markfn,\n    .freefn=NULL,\n    .sizefn=objectupvalue_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n\n/** Initializes a new upvalue object. */\nvoid object_upvalueinit(objectupvalue *c) {\n    object_init(&c->obj, OBJECT_UPVALUE);\n    c->location=NULL;\n    c->closed=MORPHO_NIL;\n    c->next=NULL;\n}\n\n/* **********************************************************************\n * objectupvalue utility functions\n * ********************************************************************** */\n\n/** Creates a new upvalue for the register pointed to by reg. */\nobjectupvalue *object_newupvalue(value *reg) {\n    objectupvalue *new = (objectupvalue *) object_new(sizeof(objectupvalue), OBJECT_UPVALUE);\n\n    if (new) {\n        object_upvalueinit(new);\n        new->location=reg;\n    }\n\n    return new;\n}\n\n/* **********************************************************************\n * Initialization and finalization\n * ********************************************************************** */\n\nDEFINE_VARRAY(upvalue, upvalue);\nDEFINE_VARRAY(varray_upvalue, varray_upvalue);\n\nobjecttype objectupvaluetype;\n\nvoid upvalue_initialize(void) {\n    // Define upvalue object type\n    objectupvaluetype=object_addtype(&objectupvaluedefn);\n}\n"
  },
  {
    "path": "src/classes/upvalue.h",
    "content": "/** @file upvalue.h\n *  @author T J Atherton\n *\n *  @brief Defines upvalue object type\n */\n\n#ifndef upvalue_h\n#define upvalue_h\n\n#include \"object.h\"\n\n/** Upvalues are used by the virtual machine to implement closures; they are not visible to the user */\n\n/* -------------------------------------------------------\n * Upvalue structure\n * ------------------------------------------------------- */\n\n/** An upvalue descriptor */\ntypedef struct {\n    bool islocal; /** Set if the upvalue is local to this function */\n    indx reg; /** An index that either:\n                  if islocal - refers to the register\n               OR otherwise  - refers to the upvalue array in the current closure */\n} upvalue;\n\nDECLARE_VARRAY(upvalue, upvalue)\nDECLARE_VARRAY(varray_upvalue, varray_upvalue)\n\n/* ---------------------------\n * Upvalue objects\n * --------------------------- */\n\nextern objecttype objectupvaluetype;\n#define OBJECT_UPVALUE objectupvaluetype\n\ntypedef struct sobjectupvalue {\n    object obj;\n    value* location; /** Pointer to the location of the upvalue */\n    value  closed; /** Closed value of the upvalue */\n    struct sobjectupvalue *next;\n} objectupvalue;\n\nvoid object_upvalueinit(objectupvalue *c);\nobjectupvalue *object_newupvalue(value *reg);\n\n/** Gets an upvalue from a value */\n#define MORPHO_GETUPVALUE(val)   ((objectupvalue *) MORPHO_GETOBJECT(val))\n\n/** Tests whether an object is an upvalue */\n#define MORPHO_ISUPVALUE(val) object_istype(val, object_upvaluetype)\n\n/* -------------------------------------------------------\n * Upvalue error messages\n * ------------------------------------------------------- */\n\n/* -------------------------------------------------------\n * Upvalue interface\n * ------------------------------------------------------- */\n\n/* Initialization/finalization */\nvoid upvalue_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/core/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        compile.c    compile.h\n        gc.c         gc.h\n        vm.c         vm.h\n        core.h\n        opcodes.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        compile.h\n        gc.h\n        optimize.h\n        vm.h\n        core.h\n        opcodes.h\n)\n"
  },
  {
    "path": "src/core/compile.c",
    "content": "/** @file compile.c\n *  @author T J Atherton\n *\n *  @brief Compiles raw input to Morpho instructions\n */\n\n#include <stdarg.h>\n#include <string.h>\n#include \"compile.h\"\n#include \"error.h\"\n#include \"vm.h\"\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"file.h\"\n#include \"resources.h\"\n#include \"extensions.h\"\n\n/** Base class for instances */\nstatic objectclass *baseclass;\n\nstatic optimizerfn *optimizer;\n\n/* **********************************************************************\n* Bytecode compiler\n* ********************************************************************** */\n\n/* ------------------------------------------\n * Utility functions\n * ------------------------------------------- */\n\n/** Checks if the compiler is in an error state */\nstatic bool compiler_haserror(compiler *c) {\n    return (c->err.cat!=ERROR_NONE);\n}\n\n/** @brief Fills out the error record\n * @param c        the compiler\n * @param node     the node the error occurred at\n * @param id       error id\n * @param ...      additional data for sprintf. */\nstatic void compiler_error(compiler *c, syntaxtreenode *node, errorid id, ... ) {\n    if (c->err.cat!=ERROR_NONE) return; // Ensure errors are not overwritten.\n    va_list args;\n    int line = (node ? node->line : ERROR_POSNUNIDENTIFIABLE);\n    int posn = (node ? node->posn : ERROR_POSNUNIDENTIFIABLE);\n\n    char *file = (MORPHO_ISSTRING(c->currentmodule) ? MORPHO_GETCSTRING(c->currentmodule) : NULL);\n    \n    va_start(args, id);\n    morpho_writeerrorwithidvalist(&c->err, id, file, line, posn, args);\n    va_end(args);\n}\n\n/** Returns true if the compiler has encountered an error */\nstatic bool compiler_checkerror(compiler *c) {\n    return (c->err.cat!=ERROR_NONE); // Ensure errors are not overwritten.\n}\n\n/** @brief Catches a compiler error, resetting the errror state to none.\n * @param c        the compiler\n * @param id       error id to match\n * @returns true if the error was matched\n */\nstatic bool compiler_catch(compiler *c, errorid id) {\n    if (morpho_matcherror(&c->err, id)) {\n        error_clear(&c->err);\n        return true;\n    }\n    return false;\n}\n\n/** Gets a node given an index */\nstatic inline syntaxtreenode *compiler_getnode(compiler *c, syntaxtreeindx indx) {\n    if (indx==SYNTAXTREE_UNCONNECTED) return NULL;\n    return c->tree.tree.data+indx;\n}\n\n/** Gets a node given an index */\nstatic inline syntaxtree *compiler_getsyntaxtree(compiler *c) {\n    return &c->tree;\n}\n\n/** Adds an instruction to the current program */\nstatic instructionindx compiler_addinstruction(compiler *c, instruction instr, syntaxtreenode *node) {\n    debugannotation_addnode(&c->out->annotations, node);\n    return varray_instructionwrite(&c->out->code, instr);\n}\n\n/** Gets the instruction index of the current instruction */\nstatic instructionindx compiler_currentinstructionindex(compiler *c) {\n    return (instructionindx) c->out->code.count;\n}\n\n/** Modifies the instruction at a given index */\nstatic void compiler_setinstruction(compiler *c, instructionindx indx,  instruction instr) {\n    c->out->code.data[indx]=instr;\n}\n\n/* ------------------------------------------\n * The functionstate stack\n * ------------------------------------------- */\n\nDEFINE_VARRAY(registeralloc, registeralloc);\nDEFINE_VARRAY(forwardreference, forwardreference);\n\n/** Initializes a functionstate structure */\nstatic void compiler_functionstateinit(functionstate *state) {\n    state->func=NULL;\n    state->scopedepth=0;\n    state->loopdepth=0;\n    state->inargs=false;\n    state->nreg=0;\n    state->type=FUNCTION;\n    state->varg=REGISTER_UNALLOCATED;\n    varray_registerallocinit(&state->registers);\n    varray_forwardreferenceinit(&state->forwardref);\n    varray_upvalueinit(&state->upvalues);\n    varray_functionrefinit(&state->functionref);\n}\n\n/** Clears a functionstate structure */\nstatic void compiler_functionstateclear(functionstate *state) {\n    state->func=NULL;\n    state->scopedepth=0;\n    state->loopdepth=0;\n    state->nreg=0;\n    varray_registerallocclear(&state->registers);\n    varray_forwardreferenceclear(&state->forwardref);\n    varray_upvalueclear(&state->upvalues);\n    varray_functionrefclear(&state->functionref);\n}\n\n/** Initializes the function stack */\nvoid compiler_fstackinit(compiler *c) {\n    for (unsigned int i=0; i<MORPHO_CALLFRAMESTACKSIZE; i++) {\n        compiler_functionstateinit(&c->fstack[i]);\n    }\n    c->fstackp=0;\n}\n\n/** Clears the function stack */\nvoid compiler_fstackclear(compiler *c) {\n    for (unsigned int i=0; i<MORPHO_CALLFRAMESTACKSIZE; i++) {\n        compiler_functionstateclear(&c->fstack[i]);\n    }\n    c->fstackp=0;\n}\n\n/** Gets the current functionstate structure */\nstatic inline functionstate *compiler_currentfunctionstate(compiler *c) {\n    return c->fstack+c->fstackp;\n}\n\n/** The parent functionstate */\nstatic inline functionstate *compiler_parentfunctionstate(compiler *c) {\n    if (c->fstackp==0) return NULL;\n    return c->fstack+c->fstackp-1;\n}\n\n/** Detect if we're currently compiling an initializer */\nstatic inline bool compiler_ininitializer(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    return FUNCTIONTYPE_ISINITIALIZER(f->type);\n}\n\n/* ------------------------------------------\n * Increment and decrement the fstack\n * ------------------------------------------- */\n\n/** Begins a new function, advancing the fstack pointer */\nvoid compiler_beginfunction(compiler *c, objectfunction *func, functiontype type) {\n    c->fstackp++;\n    compiler_functionstateinit(&c->fstack[c->fstackp]);\n    c->fstack[c->fstackp].func=func;\n    c->fstack[c->fstackp].type=type;\n    debugannotation_setfunction(&c->out->annotations, func);\n}\n\n/** Sets the function register count */\nvoid compiler_setfunctionregistercount(compiler *c) {\n    functionstate *f=&c->fstack[c->fstackp];\n    if (f->nreg>f->func->nregs) f->func->nregs=f->nreg;\n}\n\n/** Ends a function, decrementing the fstack pointer  */\nvoid compiler_endfunction(compiler *c) {\n    functionstate *f=&c->fstack[c->fstackp];\n    c->prevfunction=f->func; /* Retain the function in case it needs to be bound as a method */\n    compiler_setfunctionregistercount(c);\n    compiler_functionstateclear(f);\n    c->fstackp--;\n    debugannotation_setfunction(&c->out->annotations, c->fstack[c->fstackp].func);\n}\n\n/** Gets the current function */\nobjectfunction *compiler_getcurrentfunction(compiler *c) {\n    return c->fstack[c->fstackp].func;\n}\n\n/** Gets the current constant table */\nvarray_value *compiler_getcurrentconstanttable(compiler *c) {\n    objectfunction *f = compiler_getcurrentfunction(c);\n    if (!f) {\n        UNREACHABLE(\"find current constant table [No current function defined].\");\n    }\n\n    return &f->konst;\n}\n\n/** Gets constant i from the current constant table */\nvalue compiler_getconstant(compiler *c, unsigned int i) {\n    value ret = MORPHO_NIL;\n    objectfunction *f = compiler_getcurrentfunction(c);\n    if (f && i<f->konst.count) ret = f->konst.data[i];\n    return ret;\n}\n\n/** Gets the most recently compiled function */\nobjectfunction *compiler_getpreviousfunction(compiler *c) {\n    return c->prevfunction;\n}\n\n/* ------------------------------------------\n * Types\n * ------------------------------------------- */\n\nvalue _closuretype;\n\n/* ------------------------------------------\n * Argument declarations\n * ------------------------------------------- */\n\n/** Begins arguments */\nstatic inline void compiler_beginargs(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (f) {\n        f->inargs=true;\n    }\n}\n\n/** Ends arguments */\nstatic inline void compiler_endargs(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (f) f->inargs=false;\n}\n\n/** Are we in an args statement? */\nstatic inline bool compiler_inargs(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    return f->inargs;\n}\n\n/* ------------------------------------------\n * Class declarations\n * ------------------------------------------- */\n\nstatic inline void compiler_beginclass(compiler *c, objectclass *klass) {\n    /* If we're already compiling a class, retain it in a linked list */\n    if (c->currentclass) klass->obj.next = (object *) klass;\n\n    c->currentclass=klass;\n    debugannotation_setclass(&c->out->annotations, klass);\n}\n\nstatic inline void compiler_endclass(compiler *c) {\n    /* Delink current class from list */\n    objectclass *current = c->currentclass;\n    c->currentclass=(objectclass *) current->obj.next;\n    debugannotation_setclass(&c->out->annotations, c->currentclass);\n    current->obj.next=NULL; /* as the class is no longer part of the list */\n}\n\n/** Gets the current class */\nstatic objectclass *compiler_getcurrentclass(compiler *c) {\n    return c->currentclass;\n}\n\n/** Adds an objectclass to the compilers dictionary of classes */\nvoid compiler_addclass(compiler *c, objectclass *klass) {\n    klass->uid = program_addclass(c->out, MORPHO_OBJECT(klass));\n    \n    dictionary_insert(&c->classes, klass->name, MORPHO_OBJECT(klass));\n}\n\n/** Finds a class in the compiler's dictionary of classes */\nobjectclass *compiler_findclass(compiler *c, value name) {\n    value val;\n    if (dictionary_get(&c->classes, name, &val) &&\n        MORPHO_ISCLASS(val)) return MORPHO_GETCLASS(val);\n    \n    if (c->parent) return compiler_findclass(c->parent, name);\n    \n    return NULL;\n}\n\n/** Adds a class to a class's parent list, and also links the class into the parent's child list */\nvoid compiler_addparent(compiler *c, objectclass *klass, objectclass *parent) {\n    varray_valuewrite(&klass->parents, MORPHO_OBJECT(parent));\n    varray_valuewrite(&parent->children, MORPHO_OBJECT(klass));\n}\n\n/* ------------------------------------------\n * Types\n * ------------------------------------------- */\n\n/** Identifies a type from a label */\nbool compiler_findtype(compiler *c, value label, value *out) {\n    value type=MORPHO_NIL;\n    \n    objectclass *clss=compiler_findclass(c, label); // A class we defined\n    if (clss) {\n        type = MORPHO_OBJECT(clss);\n    } else type = builtin_findclass(label); // Or a built in one\n    \n    if (!MORPHO_ISNIL(type)) *out = type;\n    \n    return (!MORPHO_ISNIL(type));\n}\n\n/** Identify a type from a label */\nbool compiler_findtypefromcstring(compiler *c, char *label, value *out) {\n    objectstring str = MORPHO_STATICSTRING(label);\n    return compiler_findtype(c, MORPHO_OBJECT(&str), out);\n}\n\n/** Identifies a type from a value */\nbool compiler_typefromvalue(compiler *c, value v, value *out) {\n    return metafunction_typefromvalue(v, out);\n}\n\n/** Recursively searches the parents list of classes to see if the type 'match' is present */\nbool compiler_findtypeinparent(compiler *c, objectclass *type, value match) {\n    for (int i=0; i<type->parents.count; i++) {\n        if (MORPHO_ISEQUAL(type->parents.data[i], match) ||\n            compiler_findtypeinparent(c, MORPHO_GETCLASS(type->parents.data[i]), match)) return true;\n    }\n    return false;\n}\n\n/** Checks if type \"match\" matches a given type \"type\"  */\nbool compiler_checktype(compiler *c, value type, value match) {\n    if (MORPHO_ISNIL(type) || // If type is unset, we always match\n        MORPHO_ISEQUAL(type, match)) return true; // Or if the types are the same\n    \n    // Also match if 'match' inherits from 'type'\n    if (MORPHO_ISCLASS(match)) return compiler_findtypeinparent(c, MORPHO_GETCLASS(match), type);\n    \n    return false;\n}\n\n/** Determines the type associated with a constant */\nbool compiler_getconstanttype(compiler *c, unsigned int i, value *type) {\n    value val = compiler_getconstant(c, i);\n    return compiler_typefromvalue(c, val, type);\n}\n\n/* ------------------------------------------\n * Modules\n * ------------------------------------------- */\n\n/** Sets the current module */\nstatic void compiler_setmodule(compiler *c, value module) {\n    c->currentmodule=module;\n}\n\n/** Gets the current module */\nstatic value compiler_getmodule(compiler *c) {\n    return c->currentmodule;\n}\n\n/* ------------------------------------------\n * Loops\n * ------------------------------------------- */\n\n/*\n 9 : bif r3 f 6\n10 : mov r3, r1\n11 : mov r4, r0\n12 : invoke r3, c4, 1            ;  c4=enumerate\n13 : b  2\n14 : add r0, r0, c2            ;  c2=1\n15 : b  -8\n16 : end\n */\n\n/** Checks through a loop, updating any placeholders for break or continue\n * @param[in] c the current compiler\n * @param[in] start first instruction in the loop body\n * @param[in] inc position of the loop increment section (continue statements redirect here)\n * @param[in] end position of the first instruction AFTER the loop (break sections redirect here) */\nstatic void compiler_fixloop(compiler *c, instructionindx start, instructionindx inc, instructionindx end) {\n    instruction *code=c->out->code.data;\n    for (instructionindx i=start; i<end; i++) {\n        if (DECODE_OP(code[i])==OP_NOP) {\n            if (DECODE_A(code[i])=='b') {\n                code[i]=ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, end-i-1);\n            } else if (DECODE_A(code[i])=='c') {\n                code[i]=ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, inc-i-1);\n            }\n        }\n    }\n}\n\n/** Begin a loop */\nstatic void compiler_beginloop(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    f->loopdepth++;\n}\n\n/** End a loop */\nstatic void compiler_endloop(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    f->loopdepth--;\n}\n\n/** Check if we are in a loop */\nstatic bool compiler_inloop(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    return (f->loopdepth>0);\n}\n\n/* ------------------------------------------\n * Register allocation and deallocation\n * ------------------------------------------- */\n\n/** Finds a free register in the current function state and claims it */\nstatic registerindx compiler_regallocwithstate(compiler *c, functionstate *f, value symbol) {\n    registeralloc r = REGISTERALLOC_EMPTY(f->scopedepth, symbol);\n    registerindx i = REGISTER_UNALLOCATED;\n\n    if (compiler_inargs(c)) {\n        /* Search backwards from the end to find the register AFTER\n           the last allocated register */\n        for (i=f->registers.count; i>0; i--) {\n            if (f->registers.data[i-1].isallocated) break;\n        }\n        if (i<f->registers.count) {\n            f->registers.data[i]=r;\n            goto regalloc_end;\n        }\n    } else {\n        /* Search forwards to find any unallocated register */\n        for (i=0; i<f->registers.count; i++) {\n            if (!f->registers.data[i].isallocated) {\n                f->registers.data[i]=r;\n                goto regalloc_end;\n            }\n        }\n    }\n\n    /* No unallocated register was found, so allocate one at the end */\n    if (varray_registerallocadd(&f->registers, &r, 1)) {\n        i = f->registers.count-1;\n        if (f->registers.count>f->nreg) f->nreg=f->registers.count;\n    }\n\nregalloc_end:\n    if (!MORPHO_ISNIL(symbol)) debugannotation_setreg(&c->out->annotations, i, symbol);\n\n    return i;\n}\n\nstatic registerindx compiler_regalloc(compiler *c, value symbol) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    return compiler_regallocwithstate(c, f, symbol);\n}\n\n/** Sets the symbol associated with a register */\nstatic void compiler_regsetsymbol(compiler *c, registerindx reg, value symbol) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg<f->registers.count && f->registers.data[reg].isallocated) {\n        f->registers.data[reg].symbol=symbol;\n        if (!MORPHO_ISNIL(symbol)) debugannotation_setreg(&c->out->annotations, reg, symbol);\n    }\n}\n\n/** Allocates a temporary register that is guaranteed to be at the top of the stack */\nstatic registerindx compiler_regalloctop(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    registerindx i = REGISTER_UNALLOCATED;\n    registeralloc r = REGISTERALLOC_EMPTY(f->scopedepth, MORPHO_NIL);\n\n    /* Search backwards from the end to find the register AFTER\n       the last allocated register */\n    for (i=f->registers.count; i>0; i--) {\n        if (f->registers.data[i-1].isallocated) break;\n    }\n    if (i<f->registers.count) {\n        f->registers.data[i]=r;\n        return i;\n    }\n\n    if (varray_registerallocadd(&f->registers, &r, 1)) {\n        i = f->registers.count-1;\n        if (f->registers.count>f->nreg) f->nreg=f->registers.count;\n    }\n\n    return i;\n}\n\n/** Claims a free register if reqreg is REGISTER_UNALLOCATED */\nstatic registerindx compiler_regtemp(compiler *c, registerindx reqreg) {\n    return (reqreg==REGISTER_UNALLOCATED ? compiler_regalloc(c, MORPHO_NIL) : reqreg);\n}\n\n/** Treis to allocate a specific register as a temporary register */\nstatic registerindx compiler_regtempwithindx(compiler *c, registerindx reg) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg>=f->registers.count) {\n        registeralloc empty = REGISTERALLOC_EMPTY(f->scopedepth, MORPHO_NIL);\n        while (reg>=f->registers.count) {\n            if (!varray_registerallocadd(&f->registers, &empty, 1)) break;\n            if (f->registers.count>f->nreg) f->nreg=f->registers.count;\n        }\n    }\n\n    if (reg<f->registers.count) {\n        f->registers.data[reg].isallocated=true;\n    }\n\n    return reg;\n}\n\n/** Releases a register that has been previously claimed */\nstatic void compiler_regfree(compiler *c, functionstate *f, registerindx reg) {\n    if (reg<f->registers.count) {\n        f->registers.data[reg].isallocated=false;\n        f->registers.data[reg].isoptionalarg=false;\n        f->registers.data[reg].scopedepth=0;\n        if (!MORPHO_ISNIL(f->registers.data[reg].symbol)) {\n            debugannotation_setreg(&c->out->annotations, reg, MORPHO_NIL);\n        }\n        f->registers.data[reg].symbol=MORPHO_NIL;\n        f->registers.data[reg].type=MORPHO_NIL;\n        f->registers.data[reg].currenttype=MORPHO_NIL;\n    }\n}\n\n/** Frees a register if it is not a local */\nstatic void compiler_regfreetemp(compiler *c, registerindx reg) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg!=REGISTER_UNALLOCATED &&\n        reg<f->registers.count &&\n        f->registers.data[reg].isallocated &&\n        MORPHO_ISNIL(f->registers.data[reg].symbol)) {\n        compiler_regfree(c, f, reg);\n    }\n}\n\n/** Frees all registers beyond and including reg  */\nstatic void compiler_regfreetoend(compiler *c, registerindx reg) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (f) for (registerindx i=reg; i<f->registers.count; i++) {\n        compiler_regfree(c, f, i);\n    }\n}\n\n/** @brief Releases an operand.\n *  @detail A node should call this for each operand it uses the result of.  */\nstatic void compiler_releaseoperand(compiler *c, codeinfo info) {\n    if (CODEINFO_ISREGISTER(info)) {\n        compiler_regfreetemp(c, info.dest);\n    }\n}\n\n/** Releases all registers at a given scope depth */\nstatic void compiler_regfreeatscope(compiler *c, unsigned int scopedepth) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    for (registerindx i=0; i<f->registers.count; i++) {\n        if (f->registers.data[i].isallocated &&\n            f->registers.data[i].scopedepth>=scopedepth) {\n            compiler_regfree(c, f, i);\n        }\n    }\n}\n\n/** Sets the type associated with a register */\nvoid compiler_regsettype(compiler *c, registerindx reg, value type) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg<0 || reg>=f->registers.count) return;\n    f->registers.data[reg].type=type;\n}\n\n/** Gets the current type of a register */\nbool compiler_regtype(compiler *c, registerindx reg, value *type) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg>=f->registers.count) return false;\n    *type = f->registers.data[reg].type;\n    return true;\n}\n\n/** Raises a type violation error */\nvoid compiler_typeviolation(compiler *c, syntaxtreenode *node, value type, value badtype, value symbol) {\n    char *tname=\"(unknown)\", *bname=\"(unknown)\";\n    char *sym=\"\";\n\n    if (MORPHO_ISCLASS(type)) tname=MORPHO_GETCSTRING(MORPHO_GETCLASS(type)->name);\n    if (MORPHO_ISCLASS(badtype)) bname=MORPHO_GETCSTRING(MORPHO_GETCLASS(badtype)->name);\n    if (MORPHO_ISSTRING(symbol)) sym = MORPHO_GETCSTRING(symbol);\n    \n    compiler_error(c, node, COMPILE_TYPEVIOLATION, bname, tname, sym);\n}\n\n/** Sets the current type of a register. Raises a type violation error if this is not compatible with the required type  */\nbool compiler_regsetcurrenttype(compiler *c, syntaxtreenode *node, registerindx reg, value type) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg>=f->registers.count) return false;\n    \n    if (compiler_checktype(c, f->registers.data[reg].type, type)) {\n        f->registers.data[reg].currenttype=type;\n        return true;\n    }\n    \n    compiler_typeviolation(c, node, f->registers.data[reg].type, type, f->registers.data[reg].symbol);\n    \n    return false;\n}\n\n/** Gets the current type of a register */\nbool compiler_regcurrenttype(compiler *c, registerindx reg, value *type) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg>=f->registers.count) return false;\n    *type = f->registers.data[reg].currenttype;\n    return true;\n}\n\n/** @brief Finds the register that contains symbol in a given functionstate\n *  @details Searches backwards so that the innermost scope has priority */\nstatic registerindx compiler_findsymbol(functionstate *f, value symbol) {\n    if (f) for (registerindx i=f->registers.count-1; i>=0; i--) {\n        if (f->registers.data[i].isallocated) {\n            if (MORPHO_ISEQUAL(f->registers.data[i].symbol, symbol)) {\n                return i;\n            }\n        }\n    }\n\n    return REGISTER_UNALLOCATED;\n}\n\n/** @brief Finds the register that contains symbol in a given functionstate with a scopedepth of scopedepth or higher\n *  @details Searches backwards so that the innermost scope has priority */\nstatic registerindx compiler_findsymbolwithscope(functionstate *f, value symbol, unsigned int scopedepth) {\n    if (f) for (registerindx i=f->registers.count-1; i>=0; i--) {\n        if (f->registers.data[i].scopedepth<scopedepth) break;\n        if (f->registers.data[i].isallocated) {\n            if (MORPHO_ISEQUAL(f->registers.data[i].symbol, symbol)) {\n                return i;\n            }\n        }\n    }\n\n    return REGISTER_UNALLOCATED;\n}\n\n/** @brief Find the last allocated register\n *  @returns Index of the last allocated register, or REGISTER_UNALLOCATED if there are no registers allocated */\nstatic registerindx compiler_regtop(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    for (registerindx i=f->registers.count-1; i>=0; i--) {\n        if (f->registers.data[i].isallocated) return i;\n    }\n    return REGISTER_UNALLOCATED;\n}\n\n/** @brief Check if a register is temporary or associated with a symbol\n *  @returns True if the register is temporary, false otherwise */\nstatic bool compiler_isregtemp(compiler *c, registerindx indx) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    return (indx<f->registers.count && MORPHO_ISNIL(f->registers.data[indx].symbol));\n}\n\n/** @brief Check if a register is allocated\n *  @returns True if the register is allocated, false otherwise */\nstatic bool compiler_isregalloc(compiler *c, registerindx indx) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    return (indx<f->registers.count && f->registers.data[indx].isallocated);\n}\n\n/** @brief Sets that a register contains an optional argument */\nvoid compiler_regsetoptionalarg(compiler *c, registerindx reg) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    if (reg<0 || reg>=f->registers.count) return;\n    f->registers.data[reg].isoptionalarg=true;\n}\n\n/** @brief Checks if a register contains an optional argument */\nstatic bool compiler_isregoptionalarg(compiler *c, registerindx indx) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    return (indx<f->registers.count && f->registers.data[indx].isoptionalarg);\n}\n\n/** Gets the number of args from the most recent argument specifier*/\nvoid compiler_regcountargs(compiler *c, registerindx start, registerindx end, int *nposn, int *nopt) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    int np=0, nop=0;\n    if (f) {\n        for (registerindx r=start; r<=end; r++) {\n            if (compiler_isregoptionalarg(c, r)) nop++;\n            else np++;\n        }\n        \n        *nposn=np;\n        *nopt=nop/2;\n    }\n}\n\n/** @brief Get the scope level associated with a register\n *  @param[in] c        compiler\n *  @param[in] indx register to examine\n *  @param[out] scope the scope\n *  @returns True if the requested register has a meaningful scope */\nstatic bool compiler_getregscope(compiler *c, registerindx indx, unsigned int *scope) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    if (indx<f->registers.count && !MORPHO_ISNIL(f->registers.data[indx].symbol)) {\n        if (scope) *scope=f->registers.data[indx].scopedepth;\n        return true;\n    }\n\n    return false;\n}\n\n/** @brief Function to tell if a codeinfo block has returned something that is at the top of the stack\n *  @details Calls and invocations, inter alia, rely on things being put in the last available register.\n *           This function checks whether this has been achieved; if not a call to\n *           compiler_movetoregister may be made.\n *  @returns true if the codeinfo meets these requirements is satisfied, false otherwise\n */\nstatic bool compiler_iscodeinfotop(compiler *c, codeinfo func) {\n    return ( CODEINFO_ISREGISTER(func) && // We're in a register\n              compiler_isregtemp(c, func.dest) && // and it's a temporary register\n              func.dest==compiler_regtop(c)); // and it's the top of the stack\n}\n\n/** @brief Shows the current allocation of the registers */\nstatic void compiler_regshow(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    printf(\"--Registers (%u in use)\\n\",f->nreg);\n    for (unsigned int i=0; i<f->registers.count; i++) {\n        registeralloc *r=f->registers.data+i;\n        printf(\"r%u \",i);\n        if (r->isallocated) {\n            if (i==0 && FUNCTIONTYPE_ISMETHOD(f->type)) {\n                printf(\"self\");\n            } else if (!MORPHO_ISNIL(r->symbol)) {\n                morpho_printvalue(NULL, r->symbol);\n            } else {\n                printf(\"temporary\");\n            }\n            printf(\" [%u]\", r->scopedepth);\n            if (r->iscaptured) printf(\" (captured)\");\n            if (r->isoptionalarg) printf(\" (optarg)\");\n        } else {\n            printf(\"unallocated\");\n        }\n        if (!MORPHO_ISNIL(r->type)) {\n            printf(\" [\");\n            morpho_printvalue(NULL, r->type);\n            printf(\"]\");\n        }\n        if (!MORPHO_ISNIL(r->currenttype)) {\n            printf(\" contains: \");\n            morpho_printvalue(NULL, r->currenttype);\n        }\n        printf(\"\\n\");\n    }\n    printf(\"--End registers\\n\");\n}\n\n/* ------------------------------------------\n * Track scope\n * ------------------------------------------- */\n\n/** Increments the scope counter in the current functionstate */\nvoid compiler_beginscope(compiler *c) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    f->scopedepth++;\n}\n\nvoid compiler_functionreffreeatscope(compiler *c, unsigned int scope);\n\n/** Decrements the scope counter in the current functionstate */\nvoid compiler_endscope(compiler *c) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    compiler_regfreeatscope(c, f->scopedepth);\n    compiler_functionreffreeatscope(c, f->scopedepth);\n    f->scopedepth--;\n}\n\n/** Gets the scope counter in the current functionstate */\nunsigned int compiler_currentscope(compiler *c) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    return f->scopedepth;\n}\n\n/* ------------------------------------------\n * Constants\n * ------------------------------------------- */\n\n/** Writes a constant to the current constant table\n *  @param c        the compiler\n *  @param node     current syntaxtree node\n *  @param constant the constant to add\n *  @param usestrict whether to use a strict e\n *  @param clone    whether to clone the constant if it's not already present\n *                  (typically this is set to copy strings from the syntax tree) */\nstatic registerindx compiler_addconstant(compiler *c, syntaxtreenode *node, value constant, bool usestrict, bool clone) {\n    varray_value *konst = compiler_getcurrentconstanttable(c);\n    if (!konst) return REGISTER_UNALLOCATED;\n\n    registerindx out=REGISTER_UNALLOCATED;\n    unsigned int prev=0;\n\n    if (konst) {\n        /* Was a similar previous constant already added? */\n        if (usestrict) {\n            if (varray_valuefindsame(konst, constant, &prev)) out=(registerindx) prev;\n        } else {\n            if (varray_valuefind(konst, constant, &prev)) out=(registerindx) prev;\n        }\n\n        /* No, so create a new one */\n        if (out==REGISTER_UNALLOCATED) {\n            if (konst->count>=MORPHO_MAXCONSTANTS) {\n                compiler_error(c, node, COMPILE_TOOMANYCONSTANTS);\n                return REGISTER_UNALLOCATED;\n            } else {\n                value add = constant;\n                if (clone && MORPHO_ISOBJECT(add)) {\n                    /* If clone is set, we should try to clone the contents if the thing is an object. */\n                    if (MORPHO_ISSTRING(add)) {\n                        add=object_clonestring(add);\n                    } else if (MORPHO_ISCOMPLEX(add)) {\n                        add=object_clonecomplexvalue(add);\n                    } else {\n                        UNREACHABLE(\"Erroneously being asked to clone a non-string non-complex constant.\");\n                    }\n                }\n\n                bool success=varray_valueadd(konst, &add, 1);\n                out=konst->count-1;\n                if (!success) compiler_error(c, node, ERROR_ALLOCATIONFAILED);\n\n                /* If the constant is an object and we cloned it, make sure it's bound to the program */\n                if (clone && MORPHO_ISOBJECT(add)) {\n                    program_bindobject(c->out, MORPHO_GETOBJECT(add));\n                }\n            }\n        }\n    }\n\n    return out;\n}\n\n/** Write a symbol to the constant table, performing interning.\n * @param c        the compiler\n * @param node     current syntaxtree node\n *  @param symbol the constant to add */\nstatic registerindx compiler_addsymbol(compiler *c, syntaxtreenode *node, value symbol) {\n    /* Intern the symbol */\n    value add=program_internsymbol(c->out, symbol);\n\n    return compiler_addconstant(c, node, add, true, false);\n}\n\n/** Finds a builtin function and loads it into a register at the top of the stack\n *  @param c the compiler\n *  @param node current syntax tree node\n *  @param name the symbol to lookup\n *  @param req requested register\n *  @details This function is oriented to setting up a function call, so req is checked whether it's at the top of the stack. */\ncodeinfo compiler_findbuiltin(compiler *c, syntaxtreenode *node, char *name, registerindx req) {\n    objectstring symbol = MORPHO_STATICSTRING(name);\n    codeinfo ret=CODEINFO_EMPTY;\n    registerindx rfn=req;\n\n    /* Find the function */\n    value fn=builtin_findfunction(MORPHO_OBJECT(&symbol));\n    if (MORPHO_ISNIL(fn)) {\n        UNREACHABLE(\"Compiler couldn't locate builtin function.\");\n    }\n\n    registerindx cfn=compiler_addconstant(c, node, fn, false, false);\n    /* Ensure output register is at top of stack */\n    if (rfn==REGISTER_UNALLOCATED || rfn<compiler_regtop(c)) {\n        rfn=compiler_regalloctop(c);\n    }\n    compiler_addinstruction(c, ENCODE_LONG(OP_LCT, rfn, cfn), node);\n\n    ret.returntype=REGISTER;\n    ret.ninstructions=1;\n    ret.dest=rfn;\n\n    return ret;\n}\n\n/* ------------------------------------------\n * Local variables\n * ------------------------------------------- */\n\n/** @brief Creates a new local variable\n *  @param c      the current compiler\n *  @param symbol symbol for the variable\n *  @returns an allocated register for the variable */\nstatic registerindx compiler_addlocal(compiler *c, syntaxtreenode *node, value symbol) {\n    unsigned int current = compiler_currentscope(c);\n    registerindx out=compiler_findsymbolwithscope(compiler_currentfunctionstate(c), symbol, current);\n    if (out==REGISTER_UNALLOCATED) {\n        out = compiler_regalloc(c, symbol);\n    } else {\n        unsigned int scope;\n        if (compiler_getregscope(c, out, &scope)) {\n            compiler_error(c, node, COMPILE_VARALREADYDECLARED);\n        } else {\n            UNREACHABLE(\"Local variable allocated incorrectly.\");\n        }\n    }\n\n    return out;\n}\n\n/** @brief Gets the register associated with a local variable\n *  @param c      the current compiler\n *  @param symbol symbol for the variable\n *  @returns an allocated register for the variable, or REGISTER_UNALLOCATED if the variable already exists */\nstatic registerindx compiler_getlocal(compiler *c, value symbol) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    return compiler_findsymbol(f, symbol);\n}\n\n/** @brief Determines is a register is a local variable\n *  @param c      the current compiler\n *  @param reg    Register to examine */\n/*static bool compiler_islocal(compiler *c, registerindx reg) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    if (f && reg!=REGISTER_UNALLOCATED) {\n        if (f->registers.data[reg].isallocated &&\n            !MORPHO_ISNIL(f->registers.data[reg].symbol)) return true;\n    }\n\n    return false;\n}*/\n\n/** @brief Moves the results of a codeinfo block into a register\n *  @details includes constants, upvalues etc.\n *  @param   c      the current compiler\n *  @param   node   current syntaxtreenode\n *  @param   info   a codeinfo struct\n *  @param   reg    destination register, or REGISTER_UNALLOCATED to allocate a new one\n *  @returns Number of instructions generated */\nstatic codeinfo compiler_movetoregister(compiler *c, syntaxtreenode *node, codeinfo info, registerindx reg) {\n    value type = MORPHO_NIL;\n    codeinfo out = info;\n    out.ninstructions=0;\n\n    if (CODEINFO_ISCONSTANT(info)) {\n        out.returntype=REGISTER;\n        out.dest=compiler_regtemp(c, reg);\n        \n        if (compiler_getconstanttype(c, info.dest, &type)) {\n            compiler_regsetcurrenttype(c, node, out.dest, type);\n        }\n        \n        compiler_addinstruction(c, ENCODE_LONG(OP_LCT, out.dest, info.dest), node);\n        out.ninstructions++;\n    } else if (CODEINFO_ISUPVALUE(info)) {\n        /* Move upvalues */\n        out.dest=compiler_regtemp(c, reg);\n        out.returntype=REGISTER;\n        compiler_addinstruction(c, ENCODE_DOUBLE(OP_LUP, out.dest, info.dest), node);\n        out.ninstructions++;\n    } else if (CODEINFO_ISGLOBAL(info)) {\n        /* Move globals */\n        out.dest=compiler_regtemp(c, reg);\n        out.returntype=REGISTER;\n        compiler_addinstruction(c, ENCODE_LONG(OP_LGL, out.dest, info.dest), node);\n        out.ninstructions++;\n    } else {\n        /* Move between registers */\n        if (reg==REGISTER_UNALLOCATED) {\n            out.dest=compiler_regtemp(c, reg);\n        } else {\n            out.dest=reg;\n        }\n\n        if (out.dest!=info.dest) {\n            if (compiler_regcurrenttype(c, info.dest, &type)) compiler_regsetcurrenttype(c, node, out.dest, type);\n            \n            compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, out.dest, info.dest), node);\n            out.ninstructions++;\n        }\n    }\n\n    return out;\n}\n\n/** Write a symbol to the constant table, performing interning and checking the result fits into a register definition\n * @param c        the compiler\n * @param node     current syntaxtree node\n *  @param symbol the constant to add */\ncodeinfo compiler_addsymbolwithsizecheck(compiler *c, syntaxtreenode *node, value symbol) {\n    codeinfo out = CODEINFO(CONSTANT, 0, 0);\n    out.dest = compiler_addsymbol(c, node, symbol);\n    out = compiler_movetoregister(c, node, out, REGISTER_UNALLOCATED);\n    return out;\n}\n\n/* ------------------------------------------\n * Optional and variadic args\n * ------------------------------------------- */\n\nDEFINE_VARRAY(optionalparam, optionalparam);\n\n/** Adds  a variadic parameter */\nstatic inline registerindx compiler_addpositionalarg(compiler *c, syntaxtreenode *node, value symbol) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    if (f) {\n        if (!function_hasvargs(f->func)) {\n            f->func->nargs++;\n            value sym=program_internsymbol(c->out, symbol);\n            return compiler_addlocal(c, node, sym);\n        } else compiler_error(c, node, COMPILE_VARPRMLST);\n    }\n    \n    return REGISTER_UNALLOCATED;\n}\n\n/** Adds  an optional argument */\nstatic inline void compiler_addoptionalarg(compiler *c, syntaxtreenode *node, value symbol, value def) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    if (f) {\n        f->func->nopt++;\n        \n        value sym=program_internsymbol(c->out, symbol);\n        registerindx reg = compiler_addlocal(c, node, sym);\n        registerindx val = compiler_addconstant(c, node, def, false, true);\n\n        optionalparam param = {.symbol=sym, .def=val, .reg=reg};\n\n        varray_optionalparamwrite(&f->func->opt, param);\n    }\n}\n\n/** Adds  a variadic parameter */\nstatic inline void compiler_addvariadicarg(compiler *c, syntaxtreenode *node, value symbol) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    if (f) {\n        if (function_hasvargs(f->func)) {\n            compiler_error(c, node, COMPILE_MLTVARPRMTR);\n            return;\n        }\n\n        value sym=program_internsymbol(c->out, symbol);\n        registerindx reg = compiler_addlocal(c, node, sym);\n\n        function_setvarg(f->func, reg-1);\n    }\n}\n\n/* ------------------------------------------\n * Global variables\n * ------------------------------------------- */\n\n/** Should we use global variables or registers?  */\nbool compiler_checkglobal(compiler *c) {\n    return ((c->fstackp==0) && (c->fstack[0].scopedepth==0));\n}\n\n/** Finds a global symbol, optionally searching successively through parent compilers */\nglobalindx compiler_findglobal(compiler *c, value symbol, bool recurse) {\n    for (compiler *cc=c; cc!=NULL; cc=cc->parent) {\n        value indx;\n        if (dictionary_get(&cc->globals, symbol, &indx)) {\n            if (MORPHO_ISINTEGER(indx)) return (globalindx) MORPHO_GETINTEGERVALUE(indx);\n            else UNREACHABLE(\"Unknown type in global table.\");\n        }\n        if (!recurse) break;\n    }\n\n    return GLOBAL_UNALLOCATED;\n}\n\n/** Adds a global variable to */\nglobalindx compiler_addglobal(compiler *c, syntaxtreenode *node, value symbol) {\n    globalindx indx=compiler_findglobal(c, symbol, false);\n\n    if (indx==GLOBAL_UNALLOCATED) {\n        indx = program_addglobal(c->out, symbol);\n        value key = program_internsymbol(c->out, symbol);\n        \n        if (dictionary_insert(&c->globals, key, MORPHO_INTEGER(indx))) {\n            debugannotation_setglobal(&c->out->annotations, indx, symbol);\n        }\n    }\n\n    return indx;\n}\n\n/** Sets the type of a global variable */\nvoid compiler_setglobaltype(compiler *c, globalindx indx, value type) {\n    program_globalsettype(c->out, indx, type);\n}\n\n/** Checks if the type match satisfies the type of the global variable indx */\nbool compiler_checkglobaltype(compiler *c, syntaxtreenode *node, globalindx indx, value match) {\n    value type=MORPHO_NIL;\n    if (!program_globaltype(c->out, indx, &type)) return false;\n    \n    bool success=compiler_checktype(c, type, match);\n    \n    if (!success) {\n        value symbol=MORPHO_NIL;\n        program_globalsymbol(c->out, indx, &symbol);\n        compiler_typeviolation(c, node, type, match, symbol);\n    }\n    \n    return success;\n}\n\n/** Shows all currently allocated globals */\nvoid compiler_globalshow(compiler *c) {\n    int nglobals = program_countglobals(c->out);\n    printf(\"--Globals (%u in use)\\n\", nglobals);\n    for (unsigned int i=0; i<nglobals; i++) {\n        globalinfo *r=&c->out->globals.data[i];\n        printf(\"g%u \",i);\n        if (!MORPHO_ISNIL(r->symbol)) morpho_printvalue(NULL, r->symbol);\n        if (!MORPHO_ISNIL(r->type)) {\n            printf(\" [\");\n            morpho_printvalue(NULL, r->type);\n            printf(\"]\");\n        }\n        printf(\"\\n\");\n    }\n    printf(\"--End globals\\n\");\n}\n\n/* Moves the result of a calculation to an global variable */\ncodeinfo compiler_movetoglobal(compiler *c, syntaxtreenode *node, codeinfo in, globalindx slot) {\n    codeinfo use = in;\n    codeinfo out = CODEINFO_EMPTY;\n    bool tmp=false;\n\n    if (!(CODEINFO_ISREGISTER(in))) {\n        use=compiler_movetoregister(c, node, in, REGISTER_UNALLOCATED);\n        out.ninstructions+=use.ninstructions;\n        tmp=true;\n    }\n\n    value type=MORPHO_NIL;\n    if (compiler_regcurrenttype(c, in.dest, &type)) {\n        if (!compiler_checkglobaltype(c, node, slot, type)) goto compiler_movetoglobal_cleanup;\n    }\n    \n    compiler_addinstruction(c, ENCODE_LONG(OP_SGL, use.dest, slot) , node);\n    out.ninstructions++;\n\ncompiler_movetoglobal_cleanup:\n    \n    if (tmp) compiler_releaseoperand(c, use);\n    return out;\n}\n\ncodeinfo compiler_addvariable(compiler *c, syntaxtreenode *node, value symbol) {\n    codeinfo out=CODEINFO_EMPTY;\n\n    if (compiler_checkglobal(c)) {\n        out.dest=compiler_addglobal(c, node, symbol);\n        out.returntype=GLOBAL;\n    } else {\n        out.dest=compiler_addlocal(c, node, symbol);\n        out.returntype=REGISTER;\n    }\n\n    return out;\n}\n\n/* ------------------------------------------\n * Upvalues\n * ------------------------------------------- */\n\n/** Adds an upvalue to a functionstate */\nregisterindx compiler_addupvalue(functionstate *f, bool islocal, indx ix) {\n    upvalue v = (upvalue) { .islocal = islocal, .reg = ix};\n\n    /* Does this upvalue already exist? */\n    for (registerindx i=0; i<f->upvalues.count; i++) {\n        upvalue *up = &f->upvalues.data[i];\n        if (up->islocal==islocal &&\n            up->reg==ix) {\n            return i;\n        }\n    }\n\n    /* If not, add it */\n    varray_upvalueadd(&f->upvalues, &v, 1);\n    return (registerindx) f->upvalues.count-1;\n}\n\n/** Propagates upvalues up the functionstate stack.\n    @param c    the compiler\n    @param start    the initial functionstate\n    @param sindx    starting index\n    @returns register index at the top of the function state */\nregisterindx compiler_propagateupvalues(compiler *c, functionstate *start, registerindx sindx) {\n    registerindx indx=sindx;\n    for (functionstate *f = start; f<c->fstack+c->fstackp; f++) {\n        indx=compiler_addupvalue(f, f==start, indx);\n    }\n    return indx;\n}\n\n/** @brief Determines whether a symbol refers to something outside its scope\n    @param c      the compiler\n    @param symbol symbol to resolve\n    @returns the index of the upvalue, or REGISTER_UNALLOCATED if not found */\nstatic registerindx compiler_resolveupvalue(compiler *c, value symbol) {\n    registerindx indx=REGISTER_UNALLOCATED;\n    functionstate *found=NULL;\n\n    for (functionstate *f = c->fstack+c->fstackp-1; f>=c->fstack; f--) {\n        /* Try to find the symbol */\n        indx = compiler_findsymbol(f, symbol);\n        if (indx!=REGISTER_UNALLOCATED) {\n            /* Mark that this register must be captured as an upvalue */\n            f->registers.data[indx].iscaptured=true;\n            found=f;\n            break;\n        }\n    }\n\n    /* Now walk up the functionstate stack adding in the upvalues */\n    if (found) indx=compiler_propagateupvalues(c, found, indx);\n\n    return indx;\n}\n\n/* Moves the result of a calculation to an upvalue */\nstatic codeinfo compiler_movetoupvalue(compiler *c, syntaxtreenode *node, codeinfo in, registerindx slot) {\n    codeinfo use = in;\n    codeinfo out = CODEINFO_EMPTY;\n    bool tmp=false;\n\n    if (!CODEINFO_ISREGISTER(in)) {\n        use=compiler_movetoregister(c, node, in, REGISTER_UNALLOCATED);\n        out.ninstructions+=use.ninstructions;\n        tmp=true;\n    }\n\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_SUP, slot, use.dest), node);\n    out.ninstructions++;\n\n    if (tmp) {\n        compiler_releaseoperand(c, use);\n    }\n\n    return out;\n}\n\n/** Creates code to generate a closure for the current environment. */\nindx compiler_closure(compiler *c, syntaxtreenode *node, registerindx reg) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    objectfunction *func = f->func;\n    \n    indx ix=REGISTER_UNALLOCATED;\n    if (f->upvalues.count>0) {\n        object_functionaddprototype(func, &f->upvalues, &ix);\n    }\n    return ix;\n}\n\n/* ------------------------------------------\n * Manage the functionref stack\n * ------------------------------------------- */\n\nDEFINE_VARRAY(functionref, functionref)\n\n/** Adds a reference to a function in the current functionstate */\nint compiler_addfunctionref(compiler *c, objectfunction *func) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    functionref ref = { .function = func, .symbol = func->name, .scopedepth = f->scopedepth};\n    return varray_functionrefwrite(&f->functionref, ref);\n}\n\n/** Removes functions visible at a given scope level */\nvoid compiler_functionreffreeatscope(compiler *c, unsigned int scope) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    \n    while (f->functionref.count>0 && f->functionref.data[f->functionref.count-1].scopedepth>=scope) f->functionref.count--;\n}\n\nvoid _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) {\n    value in = *out;\n    if (MORPHO_ISNIL(in)) {\n        // If the function has a signature, will need to wrap in a metafunction\n        if (MORPHO_ISFUNCTION(fn) && function_hastypedparameters(MORPHO_GETFUNCTION(fn))) {\n            if (metafunction_wrap(symbol, fn, out)) {\n                program_bindobject(c->out, MORPHO_GETOBJECT(*out));\n            }\n        } else *out=fn;\n    } else if (MORPHO_ISFUNCTION(in)) {\n        if (metafunction_wrap(symbol, in, out)) { metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn);\n            program_bindobject(c->out, MORPHO_GETOBJECT(*out));\n        }\n    } else if (MORPHO_ISMETAFUNCTION(in)) {\n        metafunction_add(MORPHO_GETMETAFUNCTION(in), fn);\n    }\n}\n\n/** Finds an existing metafunction in the current context that matches a given set of implementations */\nbool compiler_findmetafunction(compiler *c, value symbol, int n, value *fns, codeinfo *out) {\n    functionstate *f=compiler_currentfunctionstate(c);\n    \n    for (int i=0; i<f->func->konst.count; i++) {\n        value v = f->func->konst.data[i];\n        if (MORPHO_ISMETAFUNCTION(v) &&\n            MORPHO_ISEQUAL(MORPHO_GETMETAFUNCTION(v)->name, symbol) &&\n            metafunction_matchset(MORPHO_GETMETAFUNCTION(v), n, fns)) {\n            *out = CODEINFO(CONSTANT, i, 0);\n            return true;\n        }\n    }\n    \n    return false;\n}\n\n/** Finds a closure, looking back up the functionstate stack and propagating upvalues as necessary */\nvoid _findclosure(compiler *c, objectfunction *closure, codeinfo *out) {\n    functionstate *fc = compiler_currentfunctionstate(c);\n    \n    if (fc->func==closure->parent) {\n        out->returntype=REGISTER;\n        out->dest = (registerindx) closure->creg;\n    } else {\n        out->returntype=UPVALUE;\n        \n        for (functionstate *f=fc; f>=c->fstack; f--) {\n            if (f->func==closure->parent) {\n                f->registers.data[closure->creg].iscaptured=true;\n                out->dest=compiler_propagateupvalues(c, f, closure->creg);\n                return;\n            }\n        }\n\n        UNREACHABLE(\"Couldn't locate parent of closure.\");\n    }\n}\n\n/** Compile a metafunction constructor by setting up a call to the Metafunction() constructor */\ncodeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value *fns) {\n    codeinfo out = compiler_findbuiltin(c, node, METAFUNCTION_CLASSNAME, REGISTER_UNALLOCATED);\n\n    for (int i=0; i<n; i++) { // Loop over the implementations\n        registerindx reg=compiler_regalloctop(c);\n        codeinfo val = CODEINFO(CONSTANT, REGISTER_UNALLOCATED, 0);\n        \n        if (MORPHO_ISFUNCTION(fns[i]) && function_isclosure(MORPHO_GETFUNCTION(fns[i]))) {\n            _findclosure(c, MORPHO_GETFUNCTION(fns[i]), &val);\n        } else {\n            val.dest = compiler_addconstant(c, node, fns[i], true, false);\n        }\n        \n        val=compiler_movetoregister(c, node, val, reg);\n        out.ninstructions+=val.ninstructions;\n    }\n\n    /* Make the function call */\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_CALL, out.dest, n), node);\n    out.ninstructions++;\n    compiler_regfreetoend(c, out.dest+1);\n    \n    return out;\n}\n\n/** Checks for duplicate function refs */\nstatic bool _checkduplicateref(varray_functionref *refs, functionref *match) {\n    for (int i=0; i<refs->count; i++) {\n        functionref *r = &refs->data[i];\n        // Match functionrefs with the same signature, but only if they're\n        // in a different scope or parent \n        if (signature_isequal(&r->function->sig, &match->function->sig) &&\n            (r->function->parent!=match->function->parent ||\n             r->scopedepth!=match->scopedepth)) return true;\n    }\n    return false;\n}\n\n/** Collects function implementations that match a given symbol */\nstatic void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray_value *out) {\n    bool closure=false;\n    \n    varray_functionref refs;\n    varray_functionrefinit(&refs);\n    \n    functionstate *fc = compiler_currentfunctionstate(c);\n    for (functionstate *f=fc; f>=c->fstack; f--) { // Go backwards to prioritize recent def'ns\n        for (int i=f->functionref.count-1; i>=0; i--) { // Go backwards\n            functionref *ref=&f->functionref.data[i];\n            if (MORPHO_ISEQUAL(ref->symbol, symbol) &&\n                !_checkduplicateref(&refs, ref)) {\n                closure |= function_isclosure(ref->function);\n                varray_functionrefadd(&refs, ref, 1);\n            }\n        }\n    }\n    \n    // Return the collected implementations\n    for (int i=0; i<refs.count; i++) {\n        varray_valuewrite(out, MORPHO_OBJECT(refs.data[i].function));\n    }\n    \n    varray_functionrefclear(&refs);\n    \n    *hasclosure=closure;\n}\n\n/** Determines whether a symbol refers to one (or more) functions. If so, returns either a single function or a metafunction as appropriate. */\nbool compiler_resolvefunctionref(compiler *c, syntaxtreenode *node, value symbol, codeinfo *out) {\n    value outfn=MORPHO_NIL;\n    bool closure=false; // Set if one of the references contains a closure.\n    \n    varray_value fns;\n    varray_valueinit(&fns);\n    \n    _findfunctionref(c, symbol, &closure, &fns);\n \n    if (!fns.count) return false; // No need to clear an empty varray\n    \n    if (closure) {\n        if (fns.count>1) { // If the list contains a closure, we must build the MF at runtime\n            *out = compiler_metafunction(c, node, fns.count, fns.data);\n        } else { // If just one closure, no need to build a metafunction\n            _findclosure(c, MORPHO_GETFUNCTION(fns.data[0]), out);\n            out->ninstructions=0;\n        }\n    } else if (!compiler_findmetafunction(c, symbol, fns.count, fns.data, out)) {\n        // If a suitable MF doesn't exist in the constant table, we should build one\n        for (int i=0; i<fns.count; i++) {\n            _addmatchingfunctionref(c, symbol, fns.data[i], &outfn);\n        }\n        \n        if (MORPHO_ISMETAFUNCTION(outfn)) {\n            metafunction_compile(MORPHO_GETMETAFUNCTION(outfn), &c->err);\n        }\n        \n        out->returntype=CONSTANT;\n        out->dest=compiler_addconstant(c, node, outfn, true, false);\n        out->ninstructions=0;\n    }\n    \n    varray_valueclear(&fns);\n    \n    return true;\n}\n\n/* ------------------------------------------\n * Helper function to move from a register\n * ------------------------------------------- */\n\n/* Moves the result of a calculation from a register to another destination */\ncodeinfo compiler_movefromregister(compiler *c, syntaxtreenode *node, codeinfo dest, registerindx reg) {\n    codeinfo in=CODEINFO(REGISTER, reg, 0), out=CODEINFO_EMPTY;\n\n    if (CODEINFO_ISUPVALUE(dest)) {\n        out=compiler_movetoupvalue(c, node, in, dest.dest);\n    } else if (CODEINFO_ISGLOBAL(dest)) {\n        out=compiler_movetoglobal(c, node, in, dest.dest);\n    } else if (CODEINFO_ISCONSTANT(dest)) {\n        UNREACHABLE(\"Cannot move to a constant.\");\n    } else {\n        out=compiler_movetoregister(c, node, in, dest.dest);\n    }\n\n    return out;\n}\n\n/* ------------------------------------------\n * Self\n * ------------------------------------------- */\n\n/** @brief Resolves self by walking back along the functionstate stack\n    @param c      the compiler\n    @returns the index of self */\nstatic registerindx compiler_resolveself(compiler *c) {\n    registerindx indx=REGISTER_UNALLOCATED;\n    functionstate *found=NULL;\n\n    for (functionstate *f = c->fstack+c->fstackp-1; f>=c->fstack; f--) {\n        /* If this is a method, we can capture self from it */\n        if (FUNCTIONTYPE_ISMETHOD(f->type)) {\n            indx=0;\n            f->registers.data[indx].iscaptured=true;\n            found=f;\n            break;\n        }\n    }\n\n    /* Now walk up the functionstate stack adding in the upvalues */\n    if (found) indx = compiler_propagateupvalues(c, found, indx);\n\n    return indx;\n}\n\n/* ------------------------------------------\n * Forward references\n * ------------------------------------------- */\n\n/** Adds a forward reference\n * @param[in] c            the compiler\n * @param[in] symbol symbol corresponding to forward reference */\nstatic codeinfo compiler_addforwardreference(compiler *c, syntaxtreenode *node, value symbol) {\n    codeinfo ret = CODEINFO_EMPTY;\n    functionstate *fparent = compiler_parentfunctionstate(c);\n\n    if (!fparent) { // If in global scope, generate symbol not defined\n        char *label = MORPHO_GETCSTRING(node->content);\n        compiler_error(c, node, COMPILE_SYMBOLNOTDEFINED, label);\n        return ret;\n    }\n\n    forwardreference ref = { .symbol = symbol,\n                             .node = node,\n                             .returntype=REGISTER,\n                             .dest=compiler_regallocwithstate(c, fparent, symbol),\n                             .scopedepth = fparent->scopedepth\n    };\n\n    ret.returntype = UPVALUE;\n    ret.dest=compiler_addupvalue(fparent, true, ref.dest);\n\n    varray_forwardreferencewrite(&fparent->forwardref, ref);\n\n    return ret;\n}\n\n/** Checks if a symbol resolves a forward reference */\nstatic bool compiler_resolveforwardreference(compiler *c, value symbol, codeinfo *out) {\n    bool success=false;\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    for (unsigned int i=0; i<f->forwardref.count; i++) {\n        forwardreference *ref = f->forwardref.data+i;\n        if (MORPHO_ISEQUAL(symbol, ref->symbol) &&\n            ref->scopedepth==compiler_currentscope(c)) {\n            out->returntype=ref->returntype;\n            out->dest=ref->dest;\n            ref->symbol=MORPHO_NIL; // Forward reference was successfully resolved\n            success=true;\n        }\n    }\n\n    return success;\n}\n\n/** Check for outstanding forward references  */\nbool compiler_checkoutstandingforwardreference(compiler *c) {\n    functionstate *f = compiler_currentfunctionstate(c);\n\n    for (unsigned int i=0; i<f->forwardref.count; i++) {\n        forwardreference *ref = f->forwardref.data+i;\n        if (!MORPHO_ISNIL(ref->symbol)) {\n            compiler_error(c, ref->node, COMPILE_FORWARDREF, MORPHO_GETCSTRING(ref->symbol));\n            return false;\n        }\n    }\n    return true;\n}\n\n/* ------------------------------------------\n * Namespaces\n * ------------------------------------------- */\n\n/** Adds a namespace to the compiler */\nnamespc *compiler_addnamespace(compiler *c, value symbol) {\n    namespc *new=MORPHO_MALLOC(sizeof(namespc));\n    \n    if (new) {\n        new->label=symbol;\n        dictionary_init(&new->symbols);\n        dictionary_init(&new->classes);\n        \n        new->next=c->namespaces; // Link namespace to compiler\n        c->namespaces=new;\n    }\n    \n    return new;\n}\n\n/** Checks if a given label corresponds to a namespace */\nnamespc *compiler_isnamespace(compiler *c, value label) {\n    for (namespc *spc=c->namespaces; spc!=NULL; spc=spc->next) {\n        if (MORPHO_ISEQUAL(spc->label, label)) return spc;\n    }\n    return NULL;\n}\n\n/** Attempts to locate a symbol given a namespace label */\nbool compiler_findsymbolwithnamespace(compiler *c, syntaxtreenode *node, value label, value symbol, value *out) {\n    bool success=false;\n    namespc *spc = compiler_isnamespace(c, label);\n    \n    // Now try to find the symbol\n    if (spc) {\n        success=dictionary_get(&spc->symbols, symbol, out);\n        \n        if (!success) compiler_error(c, node, COMPILE_SYMBOLNOTDEFINEDNMSPC, MORPHO_GETCSTRING(symbol), MORPHO_GETCSTRING(label));\n    }\n    \n    return success;\n}\n\n/** Attempts to locate a class given a namespace label */\nbool compiler_findclasswithnamespace(compiler *c, syntaxtreenode *node, value label, value symbol, value *out) {\n    bool success=false;\n    namespc *spc = compiler_isnamespace(c, label);\n    \n    // Now try to find the symbol\n    if (spc) {\n        success=dictionary_get(&spc->classes, symbol, out);\n        \n        if (!success) compiler_error(c, node, COMPILE_SYMBOLNOTDEFINEDNMSPC, MORPHO_GETCSTRING(symbol), MORPHO_GETCSTRING(label));\n    }\n    \n    return success;\n}\n\n/** Clears the namespace list, freeing attached data */\nvoid compiler_clearnamespacelist(compiler *c) {\n    namespc *next=NULL;\n    \n    for (namespc *spc=c->namespaces; spc!=NULL; spc=next) {\n        next=spc->next;\n        \n        dictionary_clear(&spc->symbols);\n        dictionary_clear(&spc->classes);\n        MORPHO_FREE(spc);\n    }\n    \n    c->namespaces=NULL;\n}\n\n/* ------------------------------------------\n * Compiler node implementation functions\n * ------------------------------------------- */\n\nstatic codeinfo compiler_constant(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_list(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_dictionary(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_index(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_negate(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_not(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_binary(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_ternary(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_property(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_dot(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_grouping(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_sequence(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_interpolation(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_range(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_scope(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_print(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_if(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_while(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_for(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_do(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_break(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_try(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_logical(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_arglist(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_invoke(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_return(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx out);\nstatic codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_self(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_super(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_assign(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_import(compiler *c, syntaxtreenode *node, registerindx reqout);\nstatic codeinfo compiler_breakpoint(compiler *c, syntaxtreenode *node, registerindx out);\n\nstatic codeinfo compiler_nodetobytecode(compiler *c, syntaxtreeindx indx, registerindx out);\n\n/* ------------------------------------------\n * Compiler definition table\n * ------------------------------------------- */\n\n#define NODE_NORULE NULL\n#define NODE_UNDEFINED { NODE_NORULE }\n\n/** Compiler definition table. This specifies a compiler function that handles each node type */\ncompilenoderule noderules[] = {\n    NODE_UNDEFINED,                  // NODE_BASE\n\n    { compiler_constant      },      // NODE_NIL,\n    { compiler_constant      },      // NODE_BOOL,\n    { compiler_constant      },      // NODE_FLOAT\n    { compiler_constant      },      // NODE_INTEGER\n    { compiler_constant      },      // NODE_STRING\n    { compiler_symbol        },      // NODE_SYMBOL\n    { compiler_self          },      // NODE_SELF\n    { compiler_super         },      // NODE_SUPER\n    { compiler_constant      },      // NODE_IMAG\n    NODE_UNDEFINED,                  // NODE_LEAF\n\n    { compiler_negate        },      // NODE_NEGATE\n    { compiler_not           },      // NODE_NOT\n    NODE_UNDEFINED,                  // NODE_UNARY\n\n    { compiler_binary        },      // NODE_ADD\n    { compiler_binary        },      // NODE_SUBTRACT\n    { compiler_binary        },      // NODE_MULTIPLY\n    { compiler_binary        },      // NODE_DIVIDE\n    { compiler_binary        },      // NODE_POW\n\n    { compiler_assign        },      // NODE_ASSIGN\n    \n    { compiler_binary        },      // NODE_EQ\n    { compiler_binary        },      // NODE_NEQ\n    { compiler_binary        },      // NODE_LT\n    { compiler_binary        },      // NODE_GT\n    { compiler_binary        },      // NODE_LTEQ\n    { compiler_binary        },      // NODE_GTEQ\n\n    { compiler_logical       },      // NODE_AND\n    { compiler_logical       },      // NODE_OR\n    \n    { compiler_ternary       },      // NODE_TERNARY\n\n    { compiler_dot           },      // NODE_DOT\n\n    { compiler_range         },      // NODE_RANGE\n    { compiler_range         },      // NODE_INCLUSIVERANGE\n\n    NODE_UNDEFINED,                  // NODE_OPERATOR\n\n    { compiler_print         },      // NODE_PRINT\n    { compiler_declaration   },      // NODE_DECLARATION\n    { compiler_declaration   },      // NODE_TYPE\n    { compiler_function      },      // NODE_FUNCTION\n    { NODE_NORULE            },      // NODE_METHOD\n    { compiler_class         },      // NODE_CLASS\n    { compiler_return        },      // NODE_RETURN\n    { compiler_if            },      // NODE_IF\n    { NODE_NORULE            },      // NODE_ELSE\n    { compiler_while         },      // NODE_WHILE\n    { compiler_for           },      // NODE_FOR\n    { compiler_do            },      // NODE_DO\n    { NODE_NORULE            },      // NODE_IN\n    { compiler_break         },      // NODE_BREAK\n    { compiler_break         },      // NODE_CONTINUE\n    { compiler_try           },      // NODE_TRY\n\n    NODE_UNDEFINED,                  // NODE_STATEMENT\n\n    { compiler_grouping      },      // NODE_GROUPING\n    { compiler_sequence      },      // NODE_SEQUENCE\n    { compiler_dictionary    },      // NODE_DICTIONARY\n    NODE_UNDEFINED,                  // NODE_DICTENTRY\n    { compiler_interpolation },      // NODE_INTERPOLATION\n    { compiler_arglist       },      // NODE_ARGLIST\n    { compiler_scope         },      // NODE_SCOPE\n    { compiler_call          },      // NODE_CALL\n    { compiler_index         },      // NODE_INDEX\n    { compiler_list          },      // NODE_LIST\n    { compiler_list          },      // NODE_TUPLE\n    { compiler_import        },      // NODE_IMPORT\n    NODE_UNDEFINED,                  // NODE_AS\n    { compiler_breakpoint    }       // NODE_BREAKPOINT\n};\n\n/** Get the associated node rule for a given node type */\nstatic compilenoderule *compiler_getrule(syntaxtreenodetype type) {\n    return &noderules[type];\n}\n\n/** Compile a constant */\nstatic codeinfo compiler_constant(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    registerindx indx = compiler_addconstant(c, node, node->content, (node->type==NODE_FLOAT ? true : false), true);\n    return CODEINFO(CONSTANT, indx, 0);\n}\n\n/** Compiles a list or tuple */\nstatic codeinfo compiler_list(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreenodetype dictentrytype[] = { NODE_ARGLIST };\n    varray_syntaxtreeindx entries;\n\n    /* Set up a call to the List() function */\n    char *classname = LIST_CLASSNAME;\n    if (node->type==NODE_TUPLE) classname = TUPLE_CLASSNAME;\n    codeinfo out = compiler_findbuiltin(c, node, classname, reqout);\n    \n    value listtype=MORPHO_NIL; /* Set the type associated with the register */\n    if (compiler_findtypefromcstring(c, classname, &listtype)) {\n        if (!compiler_regsetcurrenttype(c, node, out.dest, listtype)) return CODEINFO_EMPTY;\n    }\n\n    varray_syntaxtreeindxinit(&entries);\n    if (node->right!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(compiler_getsyntaxtree(c), node->right, 1, dictentrytype, &entries);\n\n    /* Now loop over the nodes */\n    unsigned int nargs=0;\n    for (int i=0; i<entries.count; i++) {\n        syntaxtreenode *entry=compiler_getnode(c, entries.data[i]);\n\n        registerindx reg=compiler_regalloctop(c);\n        codeinfo val = compiler_nodetobytecode(c, entries.data[i], reg);\n        out.ninstructions+=val.ninstructions;\n        if (!(CODEINFO_ISREGISTER(val) && (val.dest==reg))) {\n            compiler_releaseoperand(c, val);\n            compiler_regtempwithindx(c, reg);\n            val=compiler_movetoregister(c, entry, val, reg);\n            out.ninstructions+=val.ninstructions;\n        }\n        nargs++;\n    }\n\n    varray_syntaxtreeindxclear(&entries);\n\n    /* Make the function call */\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_CALL, out.dest, nargs), node);\n    out.ninstructions++;\n    compiler_regfreetoend(c, out.dest+1);\n\n    return out;\n}\n\n/** Compiles a dictionary */\nstatic codeinfo compiler_dictionary(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreenodetype dictentrytype[] = { NODE_DICTIONARY, NODE_DICTENTRY };\n    varray_syntaxtreeindx entries;\n\n    /* Set up a call to the Dictionary() function */\n    codeinfo out = compiler_findbuiltin(c, node, DICTIONARY_CLASSNAME, reqout);\n\n    value dicttype=MORPHO_NIL; /* Set the type associated with the register */\n    if (compiler_findtypefromcstring(c, DICTIONARY_CLASSNAME, &dicttype)) {\n        if (!compiler_regsetcurrenttype(c, node, out.dest, dicttype)) return CODEINFO_EMPTY;\n    }\n    \n    varray_syntaxtreeindxinit(&entries);\n    /* Flatten all the child nodes; these end up as a sequence: key, val, key, val, ... */\n    if (node->left!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(compiler_getsyntaxtree(c), node->left, 2, dictentrytype, &entries);\n    if (node->right!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(compiler_getsyntaxtree(c), node->right, 2, dictentrytype, &entries);\n\n    /* Now loop over the nodes */\n    unsigned int nargs=0;\n    for (int i=0; i<entries.count; i+=2) {\n        /* Loop over key/val pairs */\n        for (int j=0; j<2; j++) {\n            syntaxtreenode *entry=compiler_getnode(c, entries.data[i+j]);\n\n            registerindx reg=compiler_regalloctop(c);\n            codeinfo val = compiler_nodetobytecode(c, entries.data[i+j], reg);\n            out.ninstructions+=val.ninstructions;\n            if (!(CODEINFO_ISREGISTER(val) && (val.dest==reg))) {\n                compiler_releaseoperand(c, val);\n                val=compiler_movetoregister(c, entry, val, reg);\n                out.ninstructions+=val.ninstructions;\n            }\n            nargs++;\n        }\n    }\n    varray_syntaxtreeindxclear(&entries);\n\n    /* Make the function call */\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_CALL, out.dest, nargs), node);\n    out.ninstructions++;\n    compiler_regfreetoend(c, out.dest+1);\n\n    return out;\n}\n\n/** Compiles a range */\nstatic codeinfo compiler_range(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreeindx s[3]={ SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED};\n    bool inclusive = node->type==NODE_INCLUSIVERANGE;\n\n    /* Determine whether we have start..end or start..end:step */\n    syntaxtreenode *left=compiler_getnode(c, node->left);\n    if (left && (left->type==NODE_RANGE || left->type==NODE_INCLUSIVERANGE)) {\n        s[0]=left->left; s[1]=left->right; s[2]=node->right;\n        inclusive = left->type==NODE_INCLUSIVERANGE;\n    } else {\n        s[0]=node->left; s[1]=node->right;\n    }\n\n    /* Set up a call to the Range() function */\n    codeinfo rng = compiler_findbuiltin(c, node, (inclusive ? RANGE_INCLUSIVE_CONSTRUCTOR: RANGE_CLASSNAME), reqout);\n    \n    value rngtype=MORPHO_NIL; /* Set the type associated with the register */\n    if (compiler_findtypefromcstring(c, RANGE_CLASSNAME, &rngtype)) {\n        if (!compiler_regsetcurrenttype(c, node, rng.dest, rngtype)) return CODEINFO_EMPTY;\n    }\n\n    /* Construct the arguments */\n    unsigned int n;\n    for (n=0; n<3; n++) {\n        if (s[n]!=SYNTAXTREE_UNCONNECTED) {\n            registerindx rarg=compiler_regalloctop(c);\n            codeinfo data=compiler_nodetobytecode(c, s[n], rarg);\n            rng.ninstructions+=data.ninstructions;\n            if (!(CODEINFO_ISREGISTER(data) && (data.dest==rarg))) {\n                compiler_releaseoperand(c, data);\n                data=compiler_movetoregister(c, node, data, rarg);\n                rng.ninstructions+=data.ninstructions;\n            }\n        } else {\n            break;\n        }\n    }\n\n    /* Make the function call */\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_CALL, rng.dest, n), node);\n    rng.ninstructions++;\n    compiler_regfreetoend(c, rng.dest+1);\n\n    return rng;\n}\n\n/* @brief Compiles a index node\n * @param[in] c        - the compiler\n * @param[in] indxnode - the root syntaxtreenode (should be of type NODE_INDEX)\n * @param[out] start   - first register used\n * @param[out] end     - last register used\n * @param[out] out     - codeinfo struct with number of instructions used\n * @returns true on success */\ncodeinfo compiler_compileindexlist(compiler *c, syntaxtreenode *indxnode, registerindx *start, registerindx *end) {\n    registerindx istart=compiler_regtop(c), iend;\n\n    if (indxnode->right==SYNTAXTREE_UNCONNECTED) {\n        compiler_error(c, indxnode, COMPILE_MSSNGINDX);\n        return CODEINFO_EMPTY;\n    }\n    \n    compiler_beginargs(c);\n    codeinfo right = compiler_nodetobytecode(c, indxnode->right, REGISTER_UNALLOCATED);\n    compiler_endargs(c);\n    iend=compiler_regtop(c);\n\n    if (iend==istart) compiler_error(c, indxnode, PARSE_VARBLANKINDEX);\n\n    if (start) *start = istart+1;\n    if (end) *end = iend;\n\n    return right;\n}\n\n/** Compiles a lookup of an indexed variable */\nstatic codeinfo compiler_index(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    registerindx start, end;\n\n    /* Compile the index selector */\n    codeinfo left = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    unsigned int ninstructions=left.ninstructions;\n\n    if (!CODEINFO_ISREGISTER(left)) {\n        left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n        ninstructions+=left.ninstructions;\n    }\n\n    /* Compile indices */\n    codeinfo out = compiler_compileindexlist(c, node, &start, &end);\n    if (compiler_haserror(c)) return CODEINFO_EMPTY;\n    ninstructions+=out.ninstructions;\n\n    /* Compile instruction */\n    compiler_addinstruction(c, ENCODE(OP_LIX, left.dest, start, end), node);\n    ninstructions++;\n    \n    /* Free anything we're done with */\n    compiler_releaseoperand(c, left);\n    compiler_regfreetoend(c, start+1);\n    \n    codeinfo iout = CODEINFO(REGISTER, start, ninstructions);\n    \n    if (reqout>=0 &&\n        start!=reqout) {\n        compiler_regfreetemp(c, start);\n        iout = compiler_movetoregister(c, node, iout, reqout);\n        ninstructions+=iout.ninstructions;\n    }\n    \n    iout.ninstructions=ninstructions;\n\n    return iout;\n}\n\n/** Compile negation. Note that this is compiled as A=(0-B) */\nstatic codeinfo compiler_negate(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreenode *operand = compiler_getnode(c, node->left);\n    codeinfo out;\n\n    if (operand->type==NODE_FLOAT) {\n        registerindx neg = compiler_addconstant(c, node, MORPHO_FLOAT(-MORPHO_GETFLOATVALUE(operand->content)), true, false);\n        out = CODEINFO(CONSTANT, neg, 0);\n    } else {\n        out = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n        unsigned int ninstructions=out.ninstructions;\n\n        if (!CODEINFO_ISREGISTER(out)) {\n            /* Ensure we're working with a register */\n            out=compiler_movetoregister(c, node, out, REGISTER_UNALLOCATED);\n            ninstructions+=out.ninstructions;\n        }\n\n        registerindx zero = compiler_addconstant(c, node, MORPHO_INTEGER(0), false, false);\n        codeinfo zeroinfo = CODEINFO(CONSTANT, zero, 0);\n        zeroinfo=compiler_movetoregister(c, node, zeroinfo, REGISTER_UNALLOCATED);\n        ninstructions+=zeroinfo.ninstructions;\n\n        registerindx rout = compiler_regtemp(c, reqout);\n\n        compiler_addinstruction(c, ENCODE(OP_SUB, rout, zeroinfo.dest, out.dest), node);\n        ninstructions++;\n        compiler_releaseoperand(c, out);\n        compiler_releaseoperand(c, zeroinfo);\n        out = CODEINFO(REGISTER, rout, ninstructions);\n    }\n\n    return out;\n}\n\n/** Compile not operator */\nstatic codeinfo compiler_not(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo left = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    unsigned int ninstructions=left.ninstructions;\n    registerindx out = compiler_regtemp(c, reqout);\n\n    if (!CODEINFO_ISREGISTER(left)) {\n        /* Ensure we're working with a register or a constant */\n        left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n        ninstructions+=left.ninstructions;\n    }\n\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_NOT, out, left.dest), node);\n    ninstructions++;\n    compiler_releaseoperand(c, left);\n\n    return CODEINFO(REGISTER, out, ninstructions);\n}\n\n/** Compile arithmetic operators */\nstatic codeinfo compiler_binary(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo left = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    unsigned int ninstructions=left.ninstructions;\n    if (!(CODEINFO_ISREGISTER(left))) {\n        /* Ensure we're working with a register */\n        left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n        ninstructions+=left.ninstructions;\n    }\n\n    codeinfo right = compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n    ninstructions+=right.ninstructions;\n    if (!(CODEINFO_ISREGISTER(right))) {\n        /* Ensure we're working with a register  */\n        right=compiler_movetoregister(c, node, right, REGISTER_UNALLOCATED);\n        ninstructions+=right.ninstructions;\n    }\n\n    registerindx out = compiler_regtemp(c, reqout);\n\n    opcode op=OP_NOP;\n\n    switch (node->type) {\n        case NODE_ADD: op=OP_ADD; break;\n        case NODE_SUBTRACT: op=OP_SUB; break;\n        case NODE_MULTIPLY: op=OP_MUL; break;\n        case NODE_DIVIDE: op=OP_DIV; break;\n        case NODE_POW: op=OP_POW; break;\n        case NODE_EQ: op=OP_EQ; break;\n        case NODE_NEQ: op=OP_NEQ; break;\n        case NODE_LT: op=OP_LT; break;\n        case NODE_LTEQ: op=OP_LE; break;\n        case NODE_GT:\n            {   /* a>b is equivalent to b<a */\n                codeinfo swap = right; right=left; left = swap;\n                op=OP_LT;\n            }\n            break;\n        case NODE_GTEQ:\n            {   /* a>=b is equivalent to b<=a */\n                codeinfo swap = right; right=left; left = swap;\n                op=OP_LE;\n            }\n            break;\n        default:\n            UNREACHABLE(\"in compiling binary instruction [check bytecode compiler table]\");\n    }\n\n    if (compiler_haserror(c)) return CODEINFO_EMPTY;\n    \n    compiler_addinstruction(c, ENCODE(op, out, left.dest, right.dest), node);\n    ninstructions++;\n    compiler_releaseoperand(c, left);\n    compiler_releaseoperand(c, right);\n\n    return CODEINFO(REGISTER, out, ninstructions);\n}\n\n/** @brief Compiles the ternary operator\n *  @details Ternary operators are encoded in the syntax tree\n *\n *           <pre>\n *                 TERNARY\n *                  /  \\\n *              cond    SEQUENCE\n *                       /    \\\n *            true outcome    false outcome\n *           </pre> \n *   and are compiled to:\n *              <cond>\n *              bif    <cond>, fail:  ; branch if condition isn't met\n *              .. true outcome\n *              b      end            ; generated if an else statement is present\n *           fail:\n *              .. false outcome\n *           end:\n */\nstatic codeinfo compiler_ternary(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    unsigned int ninstructions=0;\n    \n    // Compile the condition\n    codeinfo cond = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    ninstructions+=cond.ninstructions;\n    \n    // And make sure it's in a register */\n    if (!CODEINFO_ISREGISTER(cond)) {\n       cond=compiler_movetoregister(c, node, cond, REGISTER_UNALLOCATED);\n       ninstructions+=cond.ninstructions; /* Keep track of instructions */\n    }\n\n    // Generate empty instruction to contain the conditional branch\n    instructionindx cbrnchindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n    ninstructions++;\n    \n    // We're now done with the result of the condition\n    compiler_releaseoperand(c, cond);\n    \n    // Claim an output register\n    registerindx rout=compiler_regtemp(c, reqout);\n    \n    // Get the possible outcomes\n    syntaxtreenode *outcomes = compiler_getnode(c, node->right);\n    \n    // Compile true outcome\n    codeinfo left = compiler_nodetobytecode(c, outcomes->left, rout);\n    ninstructions+=left.ninstructions;\n    \n    // Ensure the result is in the output register\n    left = compiler_movetoregister(c, outcomes, left, rout);\n    ninstructions+=left.ninstructions;\n    \n    // Generate empty instruction to branch to the end\n    instructionindx brnchendindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n    ninstructions++;\n    \n    // Compile false outcome\n    codeinfo right = compiler_nodetobytecode(c, outcomes->right, rout);\n    ninstructions+=right.ninstructions;\n    \n    // Ensure the result is in the output register\n    right = compiler_movetoregister(c, outcomes, right, rout);\n    ninstructions+=right.ninstructions;\n    \n    instructionindx end=compiler_currentinstructionindex(c);\n    \n    // Patch up branches\n    compiler_setinstruction(c, cbrnchindx, ENCODE_LONG(OP_BIFF, cond.dest, brnchendindx-cbrnchindx));\n    compiler_setinstruction(c, brnchendindx, ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, end-brnchendindx-1));\n    \n    return CODEINFO(REGISTER, rout, ninstructions);\n}\n\n/** Compiles a group (used to modify precedence) */\nstatic codeinfo compiler_grouping(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo out = CODEINFO_EMPTY;\n\n    if (node->left!=SYNTAXTREE_UNCONNECTED)\n        out=compiler_nodetobytecode(c, node->left, reqout);\n\n    return out;\n}\n\n/** Compiles a sequence of nodes */\nstatic codeinfo compiler_sequence(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo left = CODEINFO_EMPTY;\n    codeinfo right = CODEINFO_EMPTY;\n    bool inargs=compiler_inargs(c);\n\n    if (node->left!=SYNTAXTREE_UNCONNECTED)\n        left=compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    if (!inargs) compiler_releaseoperand(c, left);\n\n    if (node->right!=SYNTAXTREE_UNCONNECTED)\n        right=compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n    if (!inargs) compiler_releaseoperand(c, right);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, left.ninstructions+right.ninstructions);\n}\n\n/** Compiles a string interpolation */\nstatic codeinfo compiler_interpolation(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo exp;\n    registerindx start = REGISTER_UNALLOCATED;\n    unsigned int ninstructions=0;\n    registerindx r = REGISTER_UNALLOCATED;\n\n    for (syntaxtreenode *current = node;\n         current!=NULL;\n         current=compiler_getnode(c, current->right)) {\n\n        /* Add the contents of the node as a constant and move them into a register at the top of the stack */\n        registerindx cindx = compiler_addconstant(c, current, current->content, false, true);\n        r=compiler_regalloctop(c);\n        codeinfo string=compiler_movetoregister(c, current, CODEINFO(CONSTANT, cindx, 0), r);\n        ninstructions+=string.ninstructions;\n        if (start==REGISTER_UNALLOCATED) start = string.dest;\n\n        /* Now compile the interpolation expression */\n        if (current->left!=SYNTAXTREE_UNCONNECTED) {\n            r = compiler_regalloctop(c);\n            exp=compiler_nodetobytecode(c, current->left, r);\n            ninstructions+=exp.ninstructions;\n\n            /* Make sure this goes into the correct register */\n            if (!(exp.returntype==REGISTER && exp.dest==r)) {\n                compiler_regtempwithindx(c, r);\n                exp=compiler_movetoregister(c, current, exp, r);\n                ninstructions+=exp.ninstructions;\n            }\n            /* Free any registers above the result */\n            compiler_regfreetoend(c, r+1);\n        }\n    }\n\n    compiler_addinstruction(c, ENCODE(OP_CAT, (reqout!=REGISTER_UNALLOCATED ? reqout : start), start, r), node);\n    ninstructions++;\n\n    /* Free all the registers used, including start if it wasn't the destination for the output */\n    if (start!=REGISTER_UNALLOCATED) compiler_regfreetoend(c, start + (reqout!=REGISTER_UNALLOCATED ? 0: 1));\n\n    return CODEINFO(REGISTER, (reqout!=REGISTER_UNALLOCATED ? reqout : start), ninstructions);\n}\n\n/** Inserts instructions to close upvalues */\nstatic codeinfo compiler_closeupvaluesforscope(compiler *c, syntaxtreenode *node) {\n    functionstate *f = compiler_currentfunctionstate(c);\n    codeinfo out=CODEINFO_EMPTY;\n    indx closereg=VM_MAXIMUMREGISTERNUMBER; /* Keep track of the lowest register number to close */\n    bool closed=false; /* Do we need to close anything? */\n\n    for (unsigned int i=0; i<f->upvalues.count; i++) {\n        if (f->upvalues.data[i].islocal) {\n            indx reg = f->upvalues.data[i].reg;\n\n            if (f->registers.data[reg].scopedepth>=f->scopedepth) {\n                if (reg<closereg) closereg=reg; closed=true;\n            }\n        }\n    }\n\n    if (closed) {\n        compiler_addinstruction(c, ENCODE_SINGLE(OP_CLOSEUP, (registerindx) closereg), node);\n        out.ninstructions++;\n    }\n    return out;\n}\n\n/** Compiles a scope node. */\nstatic codeinfo compiler_scope(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo out=CODEINFO_EMPTY, up;\n    compiler_beginscope(c);\n    if (node->right!=REGISTER_UNALLOCATED) {\n        out=compiler_nodetobytecode(c, node->right, reqout);\n    }\n    up=compiler_closeupvaluesforscope(c, node);\n    out.ninstructions+=up.ninstructions;\n    compiler_endscope(c);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, out.ninstructions);\n}\n\n/** Compile print statements */\nstatic codeinfo compiler_print(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    if (node->left==REGISTER_UNALLOCATED) return CODEINFO_EMPTY;\n\n    codeinfo left=compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    unsigned int ninstructions=left.ninstructions;\n\n    if (!CODEINFO_ISREGISTER(left)) {\n        left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n        ninstructions+=left.ninstructions;\n    } else if (c->err.cat==ERROR_NONE &&  left.dest==REGISTER_UNALLOCATED) {\n        UNREACHABLE(\"print was passed an invalid operand\");\n    }\n\n    compiler_addinstruction(c, ENCODE_SINGLE(OP_PRINT, left.dest), node);\n    ninstructions++;\n    compiler_releaseoperand(c, left);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** @brief   Compile if statements\n *  @details If statements come in two flavors, those with else clauses and those\n *           without. They are encoded in the syntax tree as follows:\n *\n *           <pre>\n *                   IF                        IF\n *                  /  \\                      /  \\\n *              cond    then statement    cond    THEN\n *                                               /    \\\n *                                 then statement      else statement\n *           </pre>\n *\n *           and are compiled to bytecode that looks like\n *\n *           <pre>\n *              test   rtst, rB, rC\n *              bif    rtst, fail:    ; branch if condition isn't met\n *              .. then statement...\n *              b      end            ; generated if an else statement is present\n *           fail:\n *              .. else statement (if present)\n *           end:\n *           </pre>\n */\nstatic codeinfo compiler_if(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    unsigned int ninstructions=0;\n    bool unreachable=false;\n\n    /* The left node is the condition; compile it already */\n    codeinfo cond = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    ninstructions+=cond.ninstructions;\n\n    if (CODEINFO_ISCONSTANT(cond) &&\n        MORPHO_ISFALSE(compiler_getconstant(c, cond.dest)) ) unreachable=true;\n\n    /* And make sure it's in a register */\n    if (!CODEINFO_ISREGISTER(cond)) {\n       cond=compiler_movetoregister(c, node, cond, REGISTER_UNALLOCATED);\n       ninstructions+=cond.ninstructions; /* Keep track of instructions */\n    }\n\n    compiler_releaseoperand(c, cond);\n\n    /* The right node may be a then node or just a regular statement */\n    syntaxtreenode *right = compiler_getnode(c, node->right);\n\n    /* Hold the position of the then and else statements */\n    codeinfo then=CODEINFO_EMPTY, els=CODEINFO_EMPTY;\n\n    /* Remember where the if conditional branch is located */\n    instructionindx ifindx=REGISTER_UNALLOCATED, elsindx=REGISTER_UNALLOCATED;\n\n    /* Keep track of the number of instructions */\n    unsigned int nextra=0;\n\n    /* Generate empty instruction to contain the conditional branch */\n    ifindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n    ninstructions++;\n\n    if (right->type==NODE_THEN) {\n        /* If the right node is a THEN node, the then/else statements are located off it. */\n        if (!unreachable) {\n            then = compiler_nodetobytecode(c, right->left, REGISTER_UNALLOCATED);\n        }\n        ninstructions+=then.ninstructions;\n\n        /* Create a blank instruction */\n        elsindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), right);\n        ninstructions++;\n        nextra++; /* Keep track of this extra instruction for the original branch */\n\n        /* Now compile the els clause */\n        els = compiler_nodetobytecode(c, right->right, REGISTER_UNALLOCATED);\n        ninstructions+=els.ninstructions;\n    } else {\n        /* Otherwise, the then statement is just the right operand */\n        if (!unreachable) {\n            then = compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n        }\n        ninstructions+=then.ninstructions;\n    }\n\n    /* Now generate the conditional branch over the then clause */\n    compiler_setinstruction(c, ifindx, ENCODE_LONG(OP_BIFF, cond.dest, then.ninstructions+nextra));\n\n    /* If necessary generate the unconditional branch over the else clause */\n    if (right->type==NODE_THEN) {\n        compiler_setinstruction(c, elsindx, ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, els.ninstructions));\n    }\n    compiler_releaseoperand(c, then);\n    compiler_releaseoperand(c, els);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** @brief   Compiles a while statement\n *  @details While statements are encoded as follows:\n *\n *           <pre>\n *                   WHILE\n *                  /     \\\n *              cond       body\n *           </pre>\n *\n *           and are compiled to bytecode that looks like\n *\n *           <pre>\n *           start:\n *               test   rtst, rB, rC\n *               bif    rtst, end:    ; branch if condition isn't met\n *               .. body ..\n *               b      start\n *           end:\n *           </pre>\n*/\nstatic codeinfo compiler_while(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo cond = CODEINFO_EMPTY,\n             body = CODEINFO_EMPTY,\n             inc = CODEINFO_EMPTY;\n    unsigned int ninstructions=0;\n    instructionindx condindx=REGISTER_UNALLOCATED; /* Where is the condition located */\n    instructionindx startindx=compiler_currentinstructionindex(c);\n    instructionindx nextindx=startindx; /* Where should continue jump to? */\n\n    /* The left node is the condition; compile it already */\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        cond=compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n        ninstructions+=cond.ninstructions; /* Keep track of instructions */\n\n        /* And make sure it's in a register */\n        if (!CODEINFO_ISREGISTER(cond)) {\n            cond=compiler_movetoregister(c, node, cond, REGISTER_UNALLOCATED);\n            ninstructions+=cond.ninstructions; /* Keep track of instructions */\n        }\n\n        /* Generate empty instruction to contain the conditional branch */\n        condindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n        nextindx=condindx;\n        ninstructions++;\n\n        compiler_releaseoperand(c, cond);\n    }\n\n    compiler_beginloop(c);\n\n    if (node->right!=SYNTAXTREE_UNCONNECTED) {\n        syntaxtreenode *bodynode=compiler_getnode(c, node->right);\n\n        /* Check if we're in a for loop */\n        if (bodynode->type!=NODE_SEQUENCE) {\n            body = compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n            ninstructions+=body.ninstructions;\n            compiler_releaseoperand(c, body);\n        } else {\n            if (bodynode->left!=SYNTAXTREE_UNCONNECTED) {\n                body = compiler_nodetobytecode(c, bodynode->left, REGISTER_UNALLOCATED);\n                compiler_releaseoperand(c, body);\n            }\n            nextindx = compiler_currentinstructionindex(c);\n            if (bodynode->right!=SYNTAXTREE_UNCONNECTED) {\n                inc = compiler_nodetobytecode(c, bodynode->right, REGISTER_UNALLOCATED);\n                compiler_releaseoperand(c, inc);\n            }\n\n            body.ninstructions+=inc.ninstructions; // Used for the branch back\n            ninstructions+=body.ninstructions; // Track all the instructions\n        }\n    }\n\n    compiler_endloop(c);\n\n    /* Compile the unconditional branch back to the test instruction */\n    instructionindx end=compiler_addinstruction(c, ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, -ninstructions-1), node);\n    ninstructions++;\n\n    compiler_fixloop(c, startindx, nextindx, end+1);\n\n    /* If we did have a condition... */\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        /* And generate the conditional branch at the start of the loop.\n           The extra 1 is to skip the loop instruction */\n        compiler_setinstruction(c, condindx, ENCODE_LONG(OP_BIFF, cond.dest, body.ninstructions+1));\n    }\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** @brief Compiles a for .. in loop\n * @details For..in loops enable looping over the contents of a collection without knowing how many elements are present\n *                 forin\n *                /     \\\n *               in      body\n *              /   \\\n *            init     collection\n * This works by successively calling enumerate() on the collections, first with no arguments to get the bound, then\n * with the integer counter as in the below code\n *\n * Register allocation\n *  |  rObj  | rIndx  | | rMax  | rEnum |  rVal   | rTmp   |  ...\n *\n *  Find maximum value of counter\n *   lct   rEnum,  c             ; \"enumerate\"\n *   mov   rVal,   rObj          ;\n *   lct   rTmp, c               ; -1\n *   invoke rEnum, 1, 0\n *   mov   rMax, rVal\n\n *  loopstart:\n *   lt    rTmp,  rIndx,  rMax\n *   biff  rTmp,  <loopend>\n\n *   mov   rVal, rObj\n *   mov   rTmp, rIndx\n *   invoke rEnum, 1, 0\n\n *   ... Loop body ...\n\n *   lct   rTmp, c               ; 1\n *   add   rIndx, rIndx, rTmp    ;\n\n *   b     <loopstart>\n *  loopend:\n */\nstatic codeinfo compiler_for(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo body;\n    unsigned int ninstructions=0;\n    syntaxtreenode *innode=NULL, *initnode=NULL, *indxnode=NULL, *collnode=NULL;\n    instructionindx condindx=REGISTER_UNALLOCATED; /* Where is the condition located */\n\n    compiler_beginscope(c);\n\n    /* Collect the syntaxtree nodes */\n    innode=compiler_getnode(c, node->left);\n    if (innode) {\n        initnode=compiler_getnode(c, innode->left);\n\n        if (initnode->type==NODE_SEQUENCE) { // Unpack a variable, indx sequence\n            indxnode=compiler_getnode(c, initnode->right);\n            initnode=compiler_getnode(c, initnode->left);\n        }\n\n        if (initnode->type==NODE_DECLARATION) initnode=compiler_getnode(c, initnode->left);\n        if (indxnode && indxnode->type==NODE_DECLARATION) indxnode=compiler_getnode(c, indxnode->left);\n\n        collnode=compiler_getnode(c, innode->right);\n    }\n\n    // Register allocation for the loop\n    // |  rObj  | rIndx  || rMax  | rEnum |  rVal   | rTmp   |  ...\n\n    // Fetch the collection object\n    codeinfo coll=compiler_nodetobytecode(c, innode->right, REGISTER_UNALLOCATED);\n    ninstructions+=coll.ninstructions;\n    if (!CODEINFO_ISREGISTER(coll)) {\n        coll=compiler_movetoregister(c, collnode, coll, REGISTER_UNALLOCATED);\n        ninstructions+=coll.ninstructions;\n    }\n    registerindx rObj = coll.dest;\n    \n    // Initialize the index variable\n    registerindx rIndx=compiler_regalloc(c, MORPHO_NIL);\n    if (indxnode) compiler_regsetsymbol(c, rIndx, indxnode->content);\n    int cNil = compiler_addconstant(c, node, MORPHO_INTEGER(0), false, false);\n    compiler_addinstruction(c, ENCODE_LONG(OP_LCT, rIndx, cNil), node);\n    ninstructions++;\n    \n    /* Obtain the maximum value of rIndx by invoking enumerate */\n    \n    // Allocate register to contain maximum value of the counter\n    registerindx rMax=compiler_regalloctop(c);\n    \n    // Initialize enumerate selector\n    registerindx rEnum=compiler_regalloctop(c);\n    int cEnum = compiler_addconstant(c, node, enumerateselector, false, false);\n    compiler_addinstruction(c, ENCODE_LONG(OP_LCT, rEnum, cEnum), node);\n    ninstructions++;\n\n    // Place the object into the register after rEnum\n    registerindx rVal=compiler_regalloctop(c);\n    compiler_regsetsymbol(c, rVal, initnode->content);\n    compiler_addinstruction(c, ENCODE_LONG(OP_MOV, rVal, rObj), node);\n    ninstructions++;\n\n    // Parameter is -1 to query size of collection\n    registerindx rTmp=compiler_regalloctop(c);\n    registerindx cNegOne = compiler_addconstant(c, node, MORPHO_INTEGER(-1), false, false);\n    compiler_addinstruction(c, ENCODE_LONG(OP_LCT, rTmp, cNegOne), node);\n    ninstructions++;\n    \n    compiler_addinstruction(c, ENCODE(OP_INVOKE, rEnum, 1, 0), collnode);\n    ninstructions++;\n    \n    // Store the maximum value\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, rMax, rVal), collnode);\n    ninstructions++;\n    \n    /* Test index against the maximum value */\n    instructionindx tst=compiler_addinstruction(c, ENCODE(OP_LT, rTmp, rIndx, rMax), node);\n    condindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node); // Placeholder for branch\n    ninstructions+=2;\n    \n    /* Load enumerated value */\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, rVal, rObj), node);\n    compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, rTmp, rIndx), node);\n    compiler_addinstruction(c, ENCODE(OP_INVOKE, rEnum, 1, 0), collnode);\n    ninstructions+=3;\n    \n    compiler_beginloop(c);\n\n    /* Compile the body */\n    if (node->right==SYNTAXTREE_UNCONNECTED) {\n        compiler_error(c, node, COMPILE_MSSNGLOOPBDY);\n    } else {\n        body=compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n        ninstructions+=body.ninstructions;\n        compiler_releaseoperand(c, body);\n    }\n\n    compiler_endloop(c);\n\n    /* Increment the counter */\n    instructionindx inc=compiler_currentinstructionindex(c);\n\n    int cOne = compiler_addconstant(c, node, MORPHO_INTEGER(1), false, false);\n    compiler_addinstruction(c, ENCODE_LONG(OP_LCT, rTmp, cOne), node);\n    instructionindx add=compiler_addinstruction(c, ENCODE(OP_ADD, rIndx, rIndx, rTmp), node);\n    ninstructions+=2;\n    \n    /* Compile the unconditional branch back to the test instruction */\n    instructionindx end=compiler_addinstruction(c, ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, -(add-tst)-2), node);\n    ninstructions++;\n\n    /* Go back and generate the condition instruction */\n    compiler_setinstruction(c, condindx, ENCODE_LONG(OP_BIFF, rTmp, (add-tst) ));\n\n    compiler_fixloop(c, tst, inc, end+1);\n\n    compiler_regfreetemp(c, rObj);\n    compiler_regfreetemp(c, rIndx);\n    compiler_regfreetoend(c, rMax);\n\n    compiler_endscope(c);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** @brief   Compiles a do...while statement\n *  @details While statements are encoded as follows:\n *\n *           <pre>\n *                   DO\n *                  /     \\\n *              body        cond\n *           </pre>\n *\n *           and are compiled to bytecode that looks like\n *\n *           <pre>\n *           start:\n *               .. body ..\n *           test   rtst, rB, rC\n *               bif    rtst, end:    ; branch if condition isn't met\n *           end:\n *           </pre>\n*/\nstatic codeinfo compiler_do(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo cond = CODEINFO_EMPTY,\n             body = CODEINFO_EMPTY;\n    unsigned int ninstructions=0;\n    instructionindx startindx=compiler_currentinstructionindex(c);\n    instructionindx nextindx=REGISTER_UNALLOCATED; /* Where should continue jump to? */\n\n    compiler_beginloop(c);\n\n    /* Compile the body */\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        body = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n        ninstructions+=body.ninstructions;\n        compiler_releaseoperand(c, body);\n    }\n\n    compiler_endloop(c);\n\n    nextindx=compiler_currentinstructionindex(c);\n\n    /* The left node is the condition; compile it already */\n    if (node->right!=SYNTAXTREE_UNCONNECTED) {\n        cond=compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n        ninstructions+=cond.ninstructions; /* Keep track of instructions */\n\n        /* And make sure it's in a register */\n        if (!CODEINFO_ISREGISTER(cond)) {\n            cond=compiler_movetoregister(c, node, cond, REGISTER_UNALLOCATED);\n            ninstructions+=cond.ninstructions; /* Keep track of instructions */\n        }\n\n        /* Generate empty instruction to contain the conditional branch */\n        compiler_addinstruction(c, ENCODE_LONG(OP_BIF, cond.dest, -ninstructions-1), node);\n        ninstructions++;\n\n        compiler_releaseoperand(c, cond);\n    }\n\n    instructionindx end=compiler_currentinstructionindex(c);\n\n    compiler_fixloop(c, startindx, nextindx, end);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n\n/** @brief Compiles a break or continue statement.\n *  @details Break and continue statements are inserted as NOP instructions with the a register set to a marker.\n *  */\nstatic codeinfo compiler_break(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo out = CODEINFO_EMPTY;\n\n    if (compiler_inloop(c)) {\n        compiler_addinstruction(c, ENCODE(OP_NOP, (node->type==NODE_BREAK ? 'b' : 'c'), 0, 0), node);\n        out.ninstructions++;\n    } else compiler_error(c, node, (node->type==NODE_BREAK ? COMPILE_BRKOTSDLP : COMPILE_CNTOTSDLP));\n\n    return out;\n}\n\n/** Checks through a catch block, fixing any references\n * @param[in] c the current compiler\n * @param[in] start first instruction in the loop body\n * @param[in] inc position of the loop increment section (continue statements redirect here)\n * @param[in] end position of the first instruction AFTER the loop (break sections redirect here) */\nstatic void compiler_fixcatch(compiler *c, instructionindx start, instructionindx inc, instructionindx end) {\n    instruction *code=c->out->code.data;\n    for (instructionindx i=start; i<end; i++) {\n        if (DECODE_OP(code[i])==OP_NOP &&\n            (DECODE_A(code[i])=='s' || DECODE_A(code[i])=='b')) {\n            code[i]=ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, end-i-1);\n        }\n    }\n}\n\n/** @brief Compiles a try/catch block. */\nstatic codeinfo compiler_try(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo out = CODEINFO_EMPTY;\n\n    objectdictionary *cdict = object_newdictionary();\n    if (!cdict) { compiler_error(c, node, ERROR_ALLOCATIONFAILED); return out; }\n\n    program_bindobject(c->out, (object *) cdict);\n    \n    registerindx cdictindx = compiler_addconstant(c, node, MORPHO_OBJECT(cdict), false, false);\n\n    compiler_addinstruction(c, ENCODE_LONG(OP_PUSHERR, 0, cdictindx), node);\n    out.ninstructions++;\n\n    debugannotation_pusherr(&c->out->annotations, cdict);\n\n    /* Compile the body */\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        codeinfo body = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n        out.ninstructions+=body.ninstructions;\n        compiler_releaseoperand(c, body);\n    }\n\n    instructionindx popindx = compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n    out.ninstructions++;\n\n    /* Compile the catch dictionary */\n    varray_syntaxtreeindx switchnodes;\n    varray_syntaxtreeindx labelnodes;\n    varray_syntaxtreeindxinit(&switchnodes);\n    varray_syntaxtreeindxinit(&labelnodes);\n\n    syntaxtreenodetype match[] = { NODE_DICTIONARY };\n    syntaxtree_flatten(compiler_getsyntaxtree(c), node->right, 1, match, &switchnodes);\n\n    for (unsigned int i=0; i<switchnodes.count; i++) {\n        syntaxtreenode *entry = compiler_getnode(c, switchnodes.data[i]);\n        instructionindx entryindx = compiler_currentinstructionindex(c);\n\n        syntaxtreenode *body=compiler_getnode(c, entry->right);\n\n        if (body) {\n            codeinfo entrybody = compiler_nodetobytecode(c, entry->right, REGISTER_UNALLOCATED);\n            out.ninstructions+=entrybody.ninstructions;\n            compiler_releaseoperand(c, entrybody);\n        }\n\n        // Add an effective 'break' instruction after each entry body except for the last\n        if (i!=switchnodes.count-1) {\n            compiler_addinstruction(c, ENCODE(OP_NOP, 's', 0, 0), node);\n            out.ninstructions++;\n        }\n\n        /* Now flatten the label nodes */\n        labelnodes.count=0;\n        syntaxtreenodetype labelmatch[] = { NODE_SEQUENCE };\n        syntaxtree_flatten(compiler_getsyntaxtree(c), entry->left, 1, labelmatch, &labelnodes);\n\n        for (unsigned int j=0; j<labelnodes.count; j++) {\n            syntaxtreenode *label=compiler_getnode(c, labelnodes.data[j]);\n\n            if (label->type!=NODE_STRING) {\n                compiler_error(c, label, COMPILE_INVLDLBL);\n                break;\n            }\n\n            registerindx labelsymbol=compiler_addsymbol(c, entry, label->content);\n            value symbolkey = compiler_getconstant(c, labelsymbol);\n\n            dictionary_insert(&cdict->dict, symbolkey, MORPHO_INTEGER(entryindx));\n        }\n    }\n\n    instructionindx endindx = compiler_currentinstructionindex(c);\n\n    /* Fix the poperr instruction that jumps around the switch block */\n    compiler_setinstruction(c, popindx, ENCODE_LONG(OP_POPERR, 0, endindx-popindx-1));\n    /* Fix any nop instructions in the switch block to jump to the end of block */\n    compiler_fixcatch(c, popindx, popindx, endindx);\n\n    varray_syntaxtreeindxclear(&switchnodes);\n    varray_syntaxtreeindxclear(&labelnodes);\n\n    debugannotation_poperr(&c->out->annotations);\n\n    return out;\n}\n\n/** @brief   Compiles logical operators */\nstatic codeinfo compiler_logical(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    /* An AND operator must branch if the first operand is false,\n       an OR  operator must branch if the first operator is true */\n    bool biffflag = (node->type==NODE_AND ? true : false); // Generate a BIFF instruction\n\n    registerindx out = compiler_regtemp(c, reqout);\n    instructionindx condindx=0; /* Where is the condition located */\n    unsigned int linstructions=0, rinstructions=0; /* Size of code for both operands */\n\n    /* Generate code to get the left hand expression */\n    codeinfo left = compiler_nodetobytecode(c, node->left, out);\n    linstructions+=left.ninstructions;\n    if (!(CODEINFO_ISREGISTER(left) && left.dest==out)) {\n        compiler_releaseoperand(c, left);\n        compiler_regtempwithindx(c, out);\n        left=compiler_movetoregister(c, node, left, out);\n        linstructions+=left.ninstructions;\n    }\n\n    /* Generate empty instruction to contain the conditional branch */\n    condindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n    linstructions++;\n\n    /* Now evaluate right operand */\n    codeinfo right = compiler_nodetobytecode(c, node->right, out);\n    rinstructions+=right.ninstructions;\n    if (!(CODEINFO_ISREGISTER(right) && right.dest==out)) {\n        compiler_releaseoperand(c, right);\n        compiler_regtempwithindx(c, out);\n        right=compiler_movetoregister(c, node, right, out);\n        rinstructions+=right.ninstructions;\n    }\n\n    /* Generate the branch instruction */\n    compiler_setinstruction(c, condindx, ENCODE_LONG((biffflag ? OP_BIFF : OP_BIF), out, rinstructions));\n\n    return CODEINFO(REGISTER, out, linstructions+rinstructions);\n}\n\n/** Compile declarations */\nstatic codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreenode *decnode = node;\n    syntaxtreenode *typenode = NULL;\n    \n    if (node->type==NODE_TYPE) {\n        typenode=compiler_getnode(c, node->left);\n        decnode=compiler_getnode(c, node->right);\n    }\n    \n    syntaxtreenode *varnode = NULL;\n    syntaxtreenode *lftnode = NULL, *indxnode = NULL;\n    codeinfo right=CODEINFO_EMPTY;\n    value var=MORPHO_NIL, type=MORPHO_NIL;\n    registerindx reg;\n    unsigned int ninstructions = 0;\n    \n    varnode=compiler_getnode(c, decnode->left);\n    \n    /* Find the symbol */\n    if (varnode) {\n        if (varnode->type==NODE_SYMBOL) {\n            var = varnode->content;\n        } else if (varnode->type==NODE_INDEX) {\n            lftnode=compiler_getnode(c, varnode->left);\n            if (lftnode && lftnode->type==NODE_SYMBOL) {\n                indxnode=varnode;\n                varnode=lftnode;\n                var = varnode->content;\n            } else {\n                UNREACHABLE(\"Unexpected node type in variable declaration\");\n            }\n        }\n    }\n\n    if (!MORPHO_ISNIL(var)) {\n        /* Create the variable */\n        codeinfo vloc = compiler_addvariable(c, varnode, var);\n        codeinfo array = CODEINFO_EMPTY;\n\n        if (vloc.returntype==REGISTER) {\n            reg=vloc.dest; /* The variable was assigned to a register, so we can use it directly */\n        } else {\n            /* The variable was assigned somewhere else, so use that instead */\n            reg=compiler_regtemp(c, REGISTER_UNALLOCATED);\n        }\n\n        if (typenode &&\n            compiler_findtype(c, typenode->content, &type)) {\n            compiler_regsettype(c, reg, type);\n            if (vloc.returntype==GLOBAL) compiler_setglobaltype(c, vloc.dest, type);\n        }\n        \n        /* If this is an array, we must create it */\n        if (indxnode) {\n            /* Set up a call to the Array() function */\n            array=compiler_findbuiltin(c, decnode, ARRAY_CLASSNAME, reqout);\n            ninstructions+=array.ninstructions;\n\n            // Dimensions\n            registerindx istart=REGISTER_UNALLOCATED, iend=REGISTER_UNALLOCATED;\n            codeinfo indxinfo=compiler_compileindexlist(c, indxnode, &istart, &iend);\n            ninstructions+=indxinfo.ninstructions;\n\n            // Initializer\n            if (decnode->right!=SYNTAXTREE_UNCONNECTED) {\n                iend=compiler_regalloctop(c);\n\n                right = compiler_nodetobytecode(c, decnode->right, iend);\n                ninstructions+=right.ninstructions;\n\n                right=compiler_movetoregister(c, decnode, right, iend); // Ensure in register\n                ninstructions+=right.ninstructions;\n            }\n\n            // Call Array()\n            compiler_addinstruction(c, ENCODE_DOUBLE(OP_CALL, array.dest, iend-istart+1), node);\n            ninstructions++;\n\n            compiler_regfreetoend(c, istart);\n\n            if (vloc.returntype==REGISTER && array.dest!=vloc.dest) { // Move to correct register\n                codeinfo move=compiler_movetoregister(c, decnode, array, vloc.dest);\n                ninstructions+=move.ninstructions;\n            } else reg=array.dest;\n\n        } else if (decnode->right!=SYNTAXTREE_UNCONNECTED) { /* Not an array, but has an initializer */\n            right = compiler_nodetobytecode(c, decnode->right, reg);\n            ninstructions+=right.ninstructions;\n\n            /* Ensure operand is in the desired register  */\n            right=compiler_movetoregister(c, decnode, right, reg);\n            ninstructions+=right.ninstructions;\n        } else { /* Otherwise, we should zero out the register */\n            registerindx cnil = compiler_addconstant(c, decnode, MORPHO_NIL, false, false);\n            compiler_addinstruction(c, ENCODE_LONG(OP_LCT, reg, cnil), node);\n            ninstructions++;\n        }\n\n        if (vloc.returntype!=REGISTER) {\n            codeinfo mv=compiler_movefromregister(c, decnode, vloc, reg);\n            ninstructions+=mv.ninstructions;\n\n            compiler_regfreetemp(c, reg);\n        }\n        \n        compiler_releaseoperand(c, right);\n    }\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** Compiles an parameter declaration */\nstatic registerindx compiler_functionparameters(compiler *c, syntaxtreeindx indx) {\n    syntaxtreenode *node = compiler_getnode(c, indx);\n    if (!node) return REGISTER_UNALLOCATED;\n\n    switch(node->type) {\n        case NODE_SYMBOL:\n            return compiler_addpositionalarg(c, node, node->content);\n            break;\n        case NODE_TYPE:\n        {\n            value type=MORPHO_NIL;\n            syntaxtreenode *typenode = compiler_getnode(c, node->left);\n            if (!typenode) UNREACHABLE(\"Incorrectly formed type node.\");\n            if (typenode->type==NODE_DOT) {\n                syntaxtreenode *nsnode = compiler_getnode(c, typenode->left);\n                syntaxtreenode *labelnode = compiler_getnode(c, typenode->right);\n                \n                if (!(nsnode &&\n                    labelnode &&\n                    MORPHO_ISSTRING(nsnode->content) &&\n                    MORPHO_ISSTRING(labelnode->content))) UNREACHABLE(\"Incorrectly formed type namespace node.\");\n                \n                if (!compiler_isnamespace(c, nsnode->content)) {\n                    compiler_error(c, nsnode, COMPILE_UNKNWNNMSPC, MORPHO_GETCSTRING(nsnode->content));\n                    return REGISTER_UNALLOCATED;\n                }\n                \n                if (!compiler_findclasswithnamespace(c, typenode, nsnode->content, labelnode->content, &type)) {\n                    compiler_error(c, typenode, COMPILE_SYMBOLNOTDEFINEDNMSPC, MORPHO_GETCSTRING(nsnode->content), MORPHO_GETCSTRING(labelnode->content));\n                    return REGISTER_UNALLOCATED;\n                }\n                    \n            } else if (MORPHO_ISSTRING(typenode->content)) {\n                if (!compiler_findtype(c, typenode->content, &type)) {\n                    compiler_error(c, node, COMPILE_UNKNWNTYPE, MORPHO_GETCSTRING(typenode->content));\n                    return REGISTER_UNALLOCATED;\n                }\n            } else UNREACHABLE(\"Type node should have string label.\");\n            \n            registerindx reg = compiler_functionparameters(c, node->right);\n            compiler_regsettype(c, reg, type);\n            compiler_regsetcurrenttype(c, node, reg, type);\n        }\n            break;\n        case NODE_ASSIGN:\n        {\n            syntaxtreenode *name=compiler_getnode(c, node->left);\n            syntaxtreenode *def=compiler_getnode(c, node->right);\n\n            if (SYNTAXTREE_ISLEAF(def->type)) {\n                compiler_addoptionalarg(c, node, name->content, def->content);\n            } else compiler_error(c, def, COMPILE_OPTPRMDFLT);\n\n            break;\n        }\n        case NODE_ARGLIST:\n        {\n            compiler_functionparameters(c, node->left);\n            compiler_functionparameters(c, node->right);\n        }\n            break;\n        case NODE_RANGE:\n        {\n            syntaxtreenode *name=compiler_getnode(c, node->right);\n            compiler_addvariadicarg(c, node, name->content);\n            break;\n        }\n        default:\n            compiler_error(c, node, COMPILE_ARGSNOTSYMBOLS);\n            break;\n    }\n    \n    return REGISTER_UNALLOCATED;;\n}\n\nvalue _selfsymbol;\n\n/** Compiles a function declaration */\nstatic codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreeindx body=node->right; /* Function body */\n    codeinfo bodyinfo = CODEINFO_EMPTY; /* Code info generated by the body */\n    indx closure = REGISTER_UNALLOCATED;\n    registerindx kindx=REGISTER_UNALLOCATED;\n    registerindx reg=REGISTER_UNALLOCATED; /* Register where the function is stored */\n    instructionindx bindx;\n    unsigned int ninstructions=0;\n    bool ismethod = (c->currentmethod==node);\n    bool isanonymous = MORPHO_ISNIL(node->content);\n    bool isinitializer = false;\n\n    objectstring initlabel = MORPHO_STATICSTRING(MORPHO_INITIALIZER_METHOD);\n\n    if (!isanonymous) isinitializer=MORPHO_ISEQUAL(MORPHO_OBJECT(&initlabel), node->content);\n\n    /* We preface the function code with a branch;\n       for now simply create a blank instruction and store the indx */\n    bindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node);\n\n    objectfunction *func = object_newfunction(bindx+1, node->content, compiler_getcurrentfunction(c), 0);\n    if (!func) { compiler_error(c, node, ERROR_ALLOCATIONFAILED); return CODEINFO_EMPTY; }\n    \n    program_bindobject(c->out, (object *) func);\n    \n    /* Record the class is a method */\n    if (ismethod) func->klass=compiler_getcurrentclass(c);\n    \n    /* Add the function as a constant */\n    kindx=compiler_addconstant(c, node, MORPHO_OBJECT(func), false, false);\n    \n    /* Keep a reference to the function */\n    if (!ismethod) compiler_addfunctionref(c, func);\n\n    /* Begin the new function definition, finding whether the current function\n       is a regular function or a method declaration by looking at currentmethod */\n    functiontype ftype = (ismethod ? METHOD : FUNCTION);\n    if (ismethod &&\n        MORPHO_ISSTRING(node->content) &&\n        isinitializer ) ftype=INITIALIZER;\n    compiler_beginfunction(c, func, ftype);\n\n    /* The function has a reference to itself in r0, which may be 'self' if we're in a method */\n    value r0symbol=node->content;\n    if (ismethod) r0symbol=_selfsymbol;\n    compiler_regalloc(c, r0symbol);\n\n    /* -- Compile the parameters -- */\n    compiler_functionparameters(c, node->left);\n    \n    value signature[func->nargs+1];\n    for (int i=0; i<func->nargs; i++) compiler_regtype(c, i+1, &signature[i]);\n    function_setsignature(func, signature);\n    signature_setvarg(&func->sig, function_hasvargs(func));\n\n    /* Check we don't have too many arguments */\n    if (func->nargs+func->nopt>MORPHO_MAXARGS) {\n        compiler_error(c, node, COMPILE_TOOMANYPARAMS);\n        return CODEINFO_EMPTY;\n    }\n\n    /* -- Compile the body -- */\n    if (body!=REGISTER_UNALLOCATED) bodyinfo=compiler_nodetobytecode(c, body, REGISTER_UNALLOCATED);\n    ninstructions+=bodyinfo.ninstructions;\n\n    /* Add a return instruction if necessary */\n    if (ismethod) { // Methods automatically return self unless another argument is specified\n        compiler_addinstruction(c, ENCODE_DOUBLE(OP_RETURN, 1, 0), node); /* Add a return */\n    } else {\n        compiler_addinstruction(c, ENCODE_BYTE(OP_RETURN), node); /* Add a return */\n    }\n    ninstructions++;\n\n    /* Verify if we have any outstanding forward references */\n    compiler_checkoutstandingforwardreference(c);\n\n    /* Correct the branch instruction before the function definition code */\n    compiler_setinstruction(c, bindx, ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, ninstructions));\n    ninstructions++;\n\n    /* Restore the old function */\n    compiler_endfunction(c);\n\n    if (!ismethod) {\n        /* Generate a closure prototype if necessary */\n        closure=compiler_closure(c, node, REGISTER_UNALLOCATED);\n\n        /* Allocate a variable to refer to the function definition, but only in global\n           context */\n        /* TODO: Do we need to do this now functionstates capture function info? */\n        codeinfo fvar=CODEINFO_EMPTY;\n        fvar.dest=compiler_regtemp(c, reqout);\n        fvar.returntype=REGISTER;\n        \n        if (!isanonymous) {\n            if (!compiler_resolveforwardreference(c, func->name, &fvar) &&\n                compiler_checkglobal(c)) {\n                compiler_regfreetemp(c, fvar.dest);\n                fvar=compiler_addvariable(c, node, node->content);\n            }\n        }\n        reg=fvar.dest;\n\n        /* If it's not in a register, allocate a temporary register */\n        if (fvar.returntype!=REGISTER) reg=compiler_regtemp(c, REGISTER_UNALLOCATED);\n\n        /* Move function into register */\n        compiler_addinstruction(c, ENCODE_LONG(OP_LCT, reg, kindx), node);\n        ninstructions++;\n\n        /* Wrap in a closure if necessary */\n        if (closure!=REGISTER_UNALLOCATED) {\n            // Save the register where the closure is to be found\n            compiler_regsetsymbol(c, reg, func->name);\n            compiler_regsettype(c, reg, _closuretype);\n            function_setclosure(func, reg);\n            compiler_addinstruction(c, ENCODE_DOUBLE(OP_CLOSURE, reg, (registerindx) closure), node);\n            ninstructions++;\n        }\n\n        /* If the variable wasn't a local one, move to the correct place */\n        if (fvar.returntype!=REGISTER) {\n            codeinfo mv=compiler_movefromregister(c, node, fvar, reg);\n            ninstructions+=mv.ninstructions;\n            compiler_regfreetemp(c, reg);\n        }\n    }\n\n    return CODEINFO(REGISTER, (isanonymous ? reg : REGISTER_UNALLOCATED), ninstructions);\n}\n\n/** Compiles a list of arguments, flattening child nodes */\nstatic codeinfo compiler_arglist(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo arginfo;\n    unsigned int ninstructions=0;\n\n    varray_syntaxtreeindx argnodes;\n    varray_syntaxtreeindxinit(&argnodes);\n    /* Collapse all these types of nodes */\n    syntaxtreenodetype match[] = {node->type};\n    /* Flatten both left and right nodes */\n    syntaxtree_flatten(compiler_getsyntaxtree(c), node->left, 1, match, &argnodes);\n    syntaxtree_flatten(compiler_getsyntaxtree(c), node->right, 1, match, &argnodes);\n\n    for (unsigned int i=0; i<argnodes.count; i++) {\n        bool isOptional=false;\n        /* Claim a new register */\n        registerindx reg=compiler_regalloctop(c);\n\n        syntaxtreenode *arg = compiler_getnode(c, argnodes.data[i]);\n        if (arg->type==NODE_ASSIGN) {\n            syntaxtreenode *symbol = compiler_getnode(c, arg->left);\n\n            /* Intern the symbol and add to the constant table */\n            value s=program_internsymbol(c->out, symbol->content);\n            registerindx sym=compiler_addconstant(c, arg, s, true, false);\n            /* For the call, move the interned symbol to a register */\n            codeinfo info=CODEINFO(CONSTANT, sym, 0);\n            \n            info=compiler_movetoregister(c, arg, info, reg);\n            ninstructions+=info.ninstructions;\n            compiler_regsetoptionalarg(c, info.dest);\n            \n            reg=compiler_regalloctop(c);\n            arginfo=compiler_nodetobytecode(c, arg->right, reg);\n            if (CODEINFO_ISREGISTER(arginfo)) compiler_regsetoptionalarg(c, arginfo.dest);\n            \n            isOptional=true;\n        } else {\n            arginfo=compiler_nodetobytecode(c, argnodes.data[i], reg);\n        }\n        ninstructions+=arginfo.ninstructions;\n\n        /* If the child node didn't put it in the right place, move to the register */\n        if (!(CODEINFO_ISREGISTER(arginfo) && arginfo.dest==reg)) {\n            compiler_releaseoperand(c, arginfo);\n            compiler_regtempwithindx(c, reg);\n            arginfo=compiler_movetoregister(c, node, arginfo, reg);\n            ninstructions+=arginfo.ninstructions;\n            if (isOptional) compiler_regsetoptionalarg(c, arginfo.dest);\n        }\n\n        if (!compiler_haserror(c) && compiler_regtop(c)!=reg) {\n            compiler_regshow(c);\n            UNREACHABLE(\"Incorrectly freed registers in compiling argument list.\");\n        }\n    }\n    \n    varray_syntaxtreeindxclear(&argnodes);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\nbool _islocalfunction(compiler *c, value symbol) {\n    functionstate *fc = compiler_currentfunctionstate(c);\n    // Go backwards to prioritize recent def'ns\n    for (functionstate *f=fc;\n         f>c->fstack; // Note inequality: don't include the global functionstate \n         f--) {\n        for (int i=f->functionref.count-1; i>=0; i--) { // Go backwards\n            functionref *ref=&f->functionref.data[i];\n            if (MORPHO_ISEQUAL(ref->symbol, symbol)) return true;\n        }\n    }\n    return false;\n}\n\n/** Is this a method invocation? */\nstatic bool compiler_isinvocation(compiler *c, syntaxtreenode *call) {\n    bool isinvocation=false;\n    syntaxtreenode *selector, *target, *method;\n    /* Get the selector node */\n    selector=compiler_getnode(c, call->left);\n    if (selector->type==NODE_DOT) {\n        /* Check that if the target is a namespace */\n        target=compiler_getnode(c, selector->left);\n        if (target->type==NODE_SYMBOL &&\n            compiler_isnamespace(c, target->content)) {\n            return false;\n        }\n        \n        /* Check that the method is a symbol */\n        method=compiler_getnode(c, selector->right);\n        if (method->type==NODE_SYMBOL) isinvocation=true;\n    } else if (selector->type==NODE_SYMBOL) {\n        objectclass *klass = compiler_getcurrentclass(c);\n        \n        if (klass &&\n            !_islocalfunction(c, selector->content) &&\n            dictionary_get(&klass->methods, selector->content, NULL)) {\n            isinvocation=true;\n        }\n    }\n    return isinvocation;\n}\n\n/** Compiles a function call */\nstatic codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    unsigned int ninstructions=0;\n    if (compiler_haserror(c)) return CODEINFO_EMPTY;\n\n    if (compiler_isinvocation(c, node)) {\n        return compiler_invoke(c, node, reqout);\n    }\n    registerindx top=compiler_regtop(c);\n\n    compiler_beginargs(c);\n    \n    // Check if the call is a constructor\n    syntaxtreenode *selnode=compiler_getnode(c, node->left);\n    \n    value rtype=MORPHO_NIL;\n    if (selnode->type==NODE_SYMBOL) { // A regular call from a symbol\n        compiler_findtype(c, selnode->content, &rtype);\n    } else if (selnode->type==NODE_DOT) { // An constructor in a namespace?\n        syntaxtreenode *nsnode = compiler_getnode(c, selnode->left);\n        syntaxtreenode *snode = compiler_getnode(c, selnode->right);\n        if (nsnode && snode &&\n            compiler_isnamespace(c, nsnode->content)) {\n            compiler_findclasswithnamespace(c, snode, nsnode->content, snode->content, &rtype);\n            compiler_catch(c, COMPILE_SYMBOLNOTDEFINEDNMSPC); // We don't care if it wasn't there\n        }\n    }\n    \n    // Compile the selector\n    codeinfo func = compiler_nodetobytecode(c, node->left, (reqout<top ? REGISTER_UNALLOCATED : reqout));\n    \n    // Detect possible forward reference\n    if (selnode->type==NODE_SYMBOL && compiler_catch(c, COMPILE_SYMBOLNOTDEFINED)) {\n        syntaxtreenode *symbol=compiler_getnode(c, node->left);\n        func=compiler_addforwardreference(c, symbol, symbol->content);\n    }\n    ninstructions+=func.ninstructions;\n\n    /* Move selector into a temporary register unless we already have one\n       that's at the top of the stack */\n    if (!compiler_iscodeinfotop(c, func)) {\n        registerindx otop = compiler_regalloctop(c);\n        func=compiler_movetoregister(c, node, func, otop);\n        ninstructions+=func.ninstructions;\n    }\n\n    /* Compile the arguments */\n    codeinfo args = CODEINFO_EMPTY;\n    if (node->right!=SYNTAXTREE_UNCONNECTED) args=compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n    ninstructions+=args.ninstructions;\n\n    /* Remember the last argument */\n    registerindx lastarg=compiler_regtop(c);\n\n    /* Check we don't have too many arguments */\n    if (lastarg-func.dest>MORPHO_MAXARGS) {\n        compiler_error(c, node, COMPILE_TOOMANYARGS);\n        return CODEINFO_EMPTY;\n    }\n\n    compiler_endargs(c);\n\n    /* Generate the call instruction */\n    int nposn=0, nopt=0;\n    compiler_regcountargs(c, func.dest+1, lastarg, &nposn, &nopt);\n    compiler_addinstruction(c, ENCODE(OP_CALL, func.dest, nposn, nopt), node);\n    ninstructions++;\n\n    /* Free all the registers used for the call */\n    compiler_regfreetoend(c, func.dest+1);\n    \n    /* Set the current type of the register */\n    compiler_regsetcurrenttype(c, selnode, func.dest, rtype);\n\n    /* Move the result to the requested register */\n    if (reqout!=REGISTER_UNALLOCATED && func.dest!=reqout) {\n        codeinfo mv = compiler_movetoregister(c, node, func, reqout);\n        ninstructions+=mv.ninstructions;\n        compiler_regfreetemp(c, func.dest);\n        func.dest=reqout;\n    }\n\n    return CODEINFO(REGISTER, func.dest, ninstructions);\n}\n\n#include <stdint.h>\n\n/* Compiles a method invocation:\n        node              |          node\n      /     \\             |         /    \\\n     DOT     args         |    method     args\n   /    \\                 |    (self)\nobject   method */\nstatic codeinfo compiler_invoke(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    unsigned int ninstructions=0;\n    codeinfo object=CODEINFO_EMPTY;\n\n    /* Retrieve the selector node */\n    syntaxtreenode *selectornode=compiler_getnode(c, node->left),\n                   *methodnode=NULL,\n                   *objectnode=NULL;\n    \n    if (selectornode->type==NODE_DOT) {\n        objectnode=compiler_getnode(c, selectornode->left);\n        methodnode=compiler_getnode(c, selectornode->right);\n    } else if (selectornode->type==NODE_SYMBOL) {\n        methodnode=selectornode;\n    }\n    \n    compiler_beginargs(c);\n    \n    registerindx rSel = compiler_regalloctop(c);\n    registerindx rObj = compiler_regalloctop(c);\n    \n    // Fetch the method\n    codeinfo cSel = CODEINFO(CONSTANT, 0, 0);\n    cSel.dest = compiler_addsymbol(c, methodnode, methodnode->content);\n    codeinfo method=compiler_movetoregister(c, methodnode, cSel, rSel);\n    ninstructions+=method.ninstructions;\n\n    // Fetch the object\n    if (objectnode) {\n        bool invokeclass=false;\n        // Patch to ensure that builtin classes are prioritized over constructor functions.\n        if (objectnode->type==NODE_SYMBOL) {\n            value klass=builtin_findclass(objectnode->content);\n            if (MORPHO_ISCLASS(klass)) {\n                registerindx kindx = compiler_addconstant(c, objectnode, klass, true, false);\n                object=CODEINFO(CONSTANT, kindx, 0);\n                invokeclass=true;\n            }\n        }\n        \n        // Otherwise just fetch the object normally\n        if (!invokeclass) {\n            object=compiler_nodetobytecode(c, selectornode->left, rObj);\n            ninstructions+=object.ninstructions;\n        }\n        \n        // Ensure register allocations remain correct\n        if (object.returntype==REGISTER && object.dest!=rObj) {\n            compiler_regfreetemp(c, object.dest);\n            compiler_regtempwithindx(c, rObj); // Ensure rObj remains allocated\n        }\n    } else { // If no objectnode, fetch self from r0\n        object=CODEINFO(REGISTER, 0 /* <- r0 */, 0);\n    }\n\n    // Move the object into place for the invocation\n    object=compiler_movetoregister(c, selectornode, object, rObj);\n    ninstructions+=object.ninstructions;\n    \n    // Compile the arguments\n    codeinfo args = CODEINFO_EMPTY;\n    if (node->right!=SYNTAXTREE_UNCONNECTED) args=compiler_nodetobytecode(c, node->right, REGISTER_UNALLOCATED);\n    ninstructions+=args.ninstructions;\n\n    // Remember the last argument\n    registerindx lastarg=compiler_regtop(c);\n\n    // Check we don't have too many arguments\n    if (lastarg-rSel>MORPHO_MAXARGS) {\n        compiler_error(c, node, COMPILE_TOOMANYARGS);\n        return CODEINFO_EMPTY;\n    }\n\n    compiler_endargs(c);\n\n    // Generate the call instruction\n    int nposn=0, nopt=0;\n    compiler_regcountargs(c, object.dest+1, lastarg, &nposn, &nopt);\n    compiler_addinstruction(c, ENCODE(OP_INVOKE, rSel, nposn, nopt), node);\n    ninstructions++;\n\n    // Free all the registers used for the call\n    compiler_regfreetemp(c, rSel);\n    compiler_regfreetoend(c, rObj+1);\n\n    // Move the result to the requested register\n    if (reqout!=REGISTER_UNALLOCATED && object.dest!=reqout) {\n        compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, reqout, rObj), node);\n        ninstructions++;\n        compiler_regfreetemp(c, rObj);\n        object.dest=reqout;\n    }\n\n    return CODEINFO(REGISTER, object.dest, ninstructions);\n}\n\n/** Compile a return statement */\nstatic codeinfo compiler_return(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo left = CODEINFO_EMPTY;\n    unsigned int ninstructions=0;\n    bool isinitializer = compiler_ininitializer(c);\n\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        if (isinitializer) {\n            compiler_error(c, node, COMPILE_RETURNININITIALIZER);\n        } else {\n            left=compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n            ninstructions=left.ninstructions;\n\n            /* Ensure we're working with a register */\n            if (!(CODEINFO_ISREGISTER(left))) {\n                left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n                ninstructions+=left.ninstructions;\n            }\n\n            compiler_addinstruction(c, ENCODE_DOUBLE(OP_RETURN, 1,  left.dest), node);\n            ninstructions++;\n        }\n        compiler_releaseoperand(c, left);\n    } else {\n        /* Methods return self unless a return value is specified */\n        if (compiler_getcurrentclass(c)) {\n            compiler_addinstruction(c, ENCODE_DOUBLE(OP_RETURN, 1, 0), node); /* Add a return */\n        } else {\n            compiler_addinstruction(c, ENCODE_DOUBLE(OP_RETURN, 0, 0), node);\n        }\n        ninstructions++;\n    }\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** Overrides or adds to an existing method implementation */\nvoid compiler_overridemethod(compiler *c, syntaxtreenode *node, objectfunction *method, value prev) {\n    value symbol = method->name;\n    objectclass *klass=compiler_getcurrentclass(c);\n    \n    if (MORPHO_ISMETAFUNCTION(prev)) {\n        objectmetafunction *f = MORPHO_GETMETAFUNCTION(prev);\n        if (f->klass!=klass) {\n            f=metafunction_clone(f);\n            if (f) program_bindobject(c->out, (object *) f);\n        }\n        \n        if (f) {\n            metafunction_setclass(f, klass);\n            dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(f));\n            \n            for (int i=0; i<f->fns.count; i++) { // Check if this overrides\n                signature *sig = metafunction_getsignature(f->fns.data[i]);\n                if (sig && signature_isequal(sig, &method->sig)) {\n                    // TODO: Should check for duplicate implementation here\n                    f->fns.data[i] = MORPHO_OBJECT(method);\n                    return;\n                }\n            }\n            \n            metafunction_add(f, MORPHO_OBJECT(method));\n        }\n        \n    } else if (MORPHO_ISFUNCTION(prev)) {\n        objectfunction *prevmethod = MORPHO_GETFUNCTION(prev);\n        \n        if (signature_isequal(&prevmethod->sig, &method->sig)) { // Does the method overshadow an old one?\n            if (prevmethod->klass!=klass) { // If so, is the old one in the parent or ancestor class?\n                dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(method));\n            } else { // It's a redefinition\n                compiler_error(c, node, COMPILE_CLSSDPLCTIMPL, MORPHO_GETCSTRING(symbol), MORPHO_GETCSTRING(klass->name));\n            }\n        } else { // It doesn't override the old definition so wrap in a metafunction\n            objectmetafunction *f = object_newmetafunction(symbol);\n            \n            if (f) {\n                metafunction_add(f, prev);\n                metafunction_add(f, MORPHO_OBJECT(method));\n                metafunction_setclass(f, klass);\n                dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(f));\n                program_bindobject(c->out, (object *) f);\n            }\n        }\n    } else if (MORPHO_ISBUILTINFUNCTION(prev)) { // A builtin function can only come from a parent class, so overwrite it\n        dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(method));\n    }\n}\n\n/** Compiles a list of method declarations. */\nstatic codeinfo compiler_classbody(compiler *c, syntaxtreeindx startindx, registerindx reqout) {\n    codeinfo out;\n    unsigned int ninstructions=0;\n    objectclass *klass=compiler_getcurrentclass(c);\n\n    syntaxtreenodetype seqtype[] = { NODE_SEQUENCE };\n    varray_syntaxtreeindx entries;\n    varray_syntaxtreeindxinit(&entries);\n    \n    syntaxtree_flatten(compiler_getsyntaxtree(c), startindx, 1, seqtype, &entries);\n    \n    // Pass through body declaration to ensure all method labels are defined\n    for (int i=0; i<entries.count; i++) {\n        syntaxtreenode *node=syntaxtree_nodefromindx(compiler_getsyntaxtree(c), entries.data[i]);\n        \n        if (node->type==NODE_FUNCTION) {\n            if (!dictionary_get(&klass->methods, node->content, NULL)) {\n                value symbol = program_internsymbol(c->out, node->content);\n                dictionary_insert(&klass->methods, symbol, MORPHO_NIL);\n            }\n        } else UNREACHABLE(\"Incorrect node type found in class declaration\");\n    }\n    \n    // Now compile method definitions\n    for (int i=0; i<entries.count; i++) {\n        syntaxtreenode *node=syntaxtree_nodefromindx(compiler_getsyntaxtree(c), entries.data[i]);\n        \n        if (node->type==NODE_FUNCTION) {\n            // Store the current method so that compiler_function can recognize that\n            // it is in a method definition\n            c->currentmethod=node;\n\n            // Compile the method declaration\n            out=compiler_function(c, node, reqout);\n            ninstructions+=out.ninstructions;\n\n            // Insert the compiled function into the method dictionary, making sure the method name is interned\n            objectfunction *method = compiler_getpreviousfunction(c);\n            if (method) {\n                value omethod = MORPHO_OBJECT(method);\n                value symbol = program_internsymbol(c->out, node->content),\n                      prev=MORPHO_NIL;\n                \n                dictionary_get(&klass->methods, symbol, &prev);\n                \n                if (MORPHO_ISNIL(prev)) { // Just insert if we don't have any definition\n                    dictionary_insert(&klass->methods, symbol, omethod);\n                } else compiler_overridemethod(c, node, method, prev); // Override or create a metafunction\n            }\n        }\n    }\n    \n    varray_syntaxtreeindxclear(&entries);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** Compiles a class declaration */\nstatic codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    unsigned int ninstructions=0;\n    registerindx kindx;\n    codeinfo mout=CODEINFO_EMPTY;\n\n    if (compiler_getcurrentclass(c)) {\n        compiler_error(c, node, COMPILE_NSTDCLSS);\n        return CODEINFO_EMPTY;\n    }\n\n    objectclass *klass=object_newclass(node->content);\n    if (!klass) { compiler_error(c, node, ERROR_ALLOCATIONFAILED); return CODEINFO_EMPTY; }\n    \n    compiler_beginclass(c, klass);\n\n    /** Store the object class as a constant */\n    kindx=compiler_addconstant(c, node, MORPHO_OBJECT(klass), false, false);\n    \n    /** Add the class to the class table */\n    if (ERROR_SUCCEEDED(c->err)) compiler_addclass(c, klass);\n    \n    /* Is there a superclass and/or mixins? */\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        syntaxtreenodetype dictentrytype[] = { NODE_SEQUENCE };\n        varray_syntaxtreeindx entries;\n        varray_syntaxtreeindxinit(&entries);\n        \n        syntaxtree_flatten(compiler_getsyntaxtree(c), node->left, 1, dictentrytype, &entries);\n        \n        for (int i=entries.count-1; i>=0; i--) { // Loop over super and mixins in reverse order\n                                                 // As super will be LAST in this list\n            syntaxtreenode *snode = syntaxtree_nodefromindx(compiler_getsyntaxtree(c), entries.data[i]) ;\n            \n            objectclass *superclass=NULL;\n            value classlabel=MORPHO_NIL;\n            \n            if (snode->type==NODE_SYMBOL) {\n                classlabel=snode->content;\n                superclass=compiler_findclass(c, classlabel);\n            } else if (snode->type==NODE_DOT) {\n                syntaxtreenode *left = compiler_getnode(c, snode->left),\n                               *right = compiler_getnode(c, snode->right);\n                \n                if (left->type!=NODE_SYMBOL || right->type!=NODE_SYMBOL) UNREACHABLE(\"Superclass or mixin namespace node should have symbols\");\n                \n                classlabel=right->content;\n                \n                value klass=MORPHO_NIL;\n                compiler_findclasswithnamespace(c, snode, left->content, classlabel, &klass);\n                \n                if (MORPHO_ISCLASS(klass)) superclass=MORPHO_GETCLASS(klass);\n            } else {\n                UNREACHABLE(\"Superclass or mixin node should be a symbol.\");\n            }\n                \n            if (superclass) {\n                if (superclass!=klass) {\n                    if (!klass->superclass) klass->superclass=superclass; // Only the first class is the super class, all others are mixins.\n                    compiler_addparent(c, klass, superclass);\n                    dictionary_copy(&superclass->methods, &klass->methods);\n                } else {\n                    compiler_error(c, snode, COMPILE_CLASSINHERITSELF);\n                }\n            } else {\n                if (MORPHO_ISSTRING(classlabel)) {\n                    compiler_error(c, snode, COMPILE_SUPERCLASSNOTFOUND, MORPHO_GETCSTRING(classlabel));\n                } else UNREACHABLE(\"No class label available\");\n            }\n        }\n        \n        varray_syntaxtreeindxclear(&entries);\n    } else {\n        klass->superclass=baseclass;\n        if (baseclass) dictionary_copy(&baseclass->methods, &klass->methods);\n    }\n    \n    /* Now compute the class linearization */\n    if (!class_linearize(klass)) {\n        compiler_error(c, node, COMPILE_CLSSLNRZ, MORPHO_GETCSTRING(klass->name));\n    }\n\n    /* Compile the body */\n    if (node->right!=SYNTAXTREE_UNCONNECTED) {\n        mout=compiler_classbody(c, node->right, reqout);\n        ninstructions+=mout.ninstructions;\n    }\n\n    /* End class definition */\n    compiler_endclass(c);\n\n    compiler_checkoutstandingforwardreference(c);\n    \n    /* Allocate a variable to refer to the class definition */\n    codeinfo cvar=compiler_addvariable(c, node, node->content);\n    registerindx reg=cvar.dest;\n\n    /* If it's not in a register, allocate a temporary register */\n    if (cvar.returntype!=REGISTER) reg=compiler_regtemp(c, REGISTER_UNALLOCATED);\n\n    /* Move function into register */\n    compiler_addinstruction(c, ENCODE_LONG(OP_LCT, reg, kindx), node);\n    ninstructions++;\n\n    /* If the variable wasn't a local one, move to the correct place */\n    if (cvar.returntype!=REGISTER) {\n        codeinfo mv=compiler_movefromregister(c, node, cvar, reg);\n        ninstructions+=mv.ninstructions;\n        compiler_regfreetemp(c, reg);\n    }\n    \n    /* Bind the klass to the program to be freed on exit */\n    if (klass) program_bindobject(c->out, (object *) klass);\n    \n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions);\n}\n\n/** Compile a reference to self */\nstatic codeinfo compiler_self(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    objectclass *klass = compiler_getcurrentclass(c);\n\n    /* If we're in a method, self is available in r0 */\n    codeinfo ret=CODEINFO(REGISTER, 0, 0);\n\n    /* Check that we're in a class definition */\n    if (!klass) {\n        compiler_error(c, node, COMPILE_SELFOUTSIDECLASS);\n    }\n\n    /* If we're inside a function embedded in a method, we need to capture self as an upvalue */\n    functionstate *fstate = compiler_currentfunctionstate(c);\n    if (fstate->type==FUNCTION) {\n        ret.dest = compiler_resolveself(c);\n        if (ret.dest!=REGISTER_UNALLOCATED) {\n            ret.returntype=UPVALUE;\n        }\n    }\n\n    return ret;\n}\n\n/** Compile a reference to super */\nstatic codeinfo compiler_super(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    objectclass *klass = compiler_getcurrentclass(c);\n    codeinfo ret=CODEINFO_EMPTY;\n\n    /* Check that we're in a class definition */\n    if (klass) {\n        if (klass->superclass) {\n           ret.returntype=CONSTANT;\n           ret.dest=compiler_addconstant(c, node, MORPHO_OBJECT(klass->superclass), false, false);\n        } else {\n            compiler_error(c, node, COMPILE_NOSUPER);\n        }\n    } else {\n        compiler_error(c, node, COMPILE_SUPEROUTSIDECLASS);\n    }\n\n    return ret;\n}\n\n/** Lookup a symbol */\nstatic codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo ret=CODEINFO_EMPTY;\n    value type;\n    \n    /* Is it a local variable? */\n    ret.dest=compiler_getlocal(c, node->content);\n    if (ret.dest!=REGISTER_UNALLOCATED &&\n        compiler_regtype(c, ret.dest, &type) && // If it's a closure it should be resolved later\n        !MORPHO_ISEQUAL(type, _closuretype)) {\n        return ret;\n    }\n\n    /* Is it a reference to a function? */\n    if (compiler_resolvefunctionref(c, node, node->content, &ret)) {\n        return ret;\n    }\n    \n    /* Is it an upvalue? */\n    ret.dest = compiler_resolveupvalue(c, node->content);\n    if (ret.dest!=REGISTER_UNALLOCATED) {\n        ret.returntype=UPVALUE;\n        return ret;\n    }\n    \n    /* Is it a global variable */\n    ret.dest=compiler_findglobal(c, node->content, true);\n    if (ret.dest!=REGISTER_UNALLOCATED) {\n        ret.returntype=GLOBAL;\n        return ret;\n    }\n\n    /* Is it a builtin function or class? */\n    value binf = builtin_findfunction(node->content);\n    if (MORPHO_ISNIL(binf)) binf = builtin_findclass(node->content);\n\n    if (!MORPHO_ISNIL(binf)) {\n        /* It is; so add it to the constant table */\n        ret.returntype=CONSTANT;\n        ret.dest=compiler_addconstant(c, node, binf, false, false);\n        return ret;\n    }\n\n    /* Is it a class? */\n    objectclass *klass = compiler_findclass(c, node->content);\n    if (klass) {\n        /* It is; so add it to the constant table */\n        ret.returntype=CONSTANT;\n        ret.dest=compiler_addconstant(c, node, MORPHO_OBJECT(klass), false, false);\n        return ret;\n    }\n\n    char *label = MORPHO_GETCSTRING(node->content);\n    compiler_error(c, node, COMPILE_SYMBOLNOTDEFINED, label);\n\n    return ret;\n}\n\nstatic codeinfo compiler_movetoproperty(compiler *c, syntaxtreenode *node, codeinfo in, syntaxtreenode *obj);\n\n/** Assign to a symbol */\nstatic codeinfo compiler_assign(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreenode *varnode = compiler_getnode(c, node->left);\n    syntaxtreenode *indxnode = NULL;\n    codeinfo ret, right=CODEINFO_EMPTY;\n    value var=MORPHO_NIL;\n    registerindx reg=REGISTER_UNALLOCATED, istart=0, iend=0, tmp=REGISTER_UNALLOCATED;\n    enum { ASSIGN_VAR, ASSIGN_UPVALUE, ASSIGN_OBJ, ASSIGN_GLBL, ASSIGN_INDEX, ASSIGN_UPINDEX } mode=ASSIGN_VAR;\n    unsigned int ninstructions = 0;\n\n    /* Find the symbol or check if it's an object */\n    if (varnode) {\n        if (varnode->type==NODE_SYMBOL) {\n            var = varnode->content;\n        } else if (varnode->type==NODE_DOT) {\n            mode=ASSIGN_OBJ; /* Or object */\n        } else if (varnode->type==NODE_INDEX) {\n            mode=ASSIGN_INDEX;\n            indxnode=varnode;\n            varnode=compiler_getnode(c, varnode->left);\n            var = varnode->content;\n\n            if (varnode->type==NODE_DOT || varnode->type==NODE_SELF) {\n                codeinfo mv=compiler_nodetobytecode(c, indxnode->left, reg);\n                ninstructions+=mv.ninstructions;\n                reg=mv.dest;\n            }\n        }\n    }\n\n    if (!MORPHO_ISNIL(var) || mode==ASSIGN_OBJ || mode==ASSIGN_INDEX) {\n        if (mode!=ASSIGN_OBJ) {\n            /* Find the local variable and get the assigned register */\n            if (reg==REGISTER_UNALLOCATED) reg=compiler_getlocal(c, var);\n\n            /* Perhaps it's an upvalue? */\n            if (reg==REGISTER_UNALLOCATED) {\n                reg=compiler_resolveupvalue(c, var);\n                if (reg!=REGISTER_UNALLOCATED) mode=(mode==ASSIGN_INDEX ? ASSIGN_UPINDEX : ASSIGN_UPVALUE);\n            }\n\n            /* .. or a global? */\n            if (reg==REGISTER_UNALLOCATED) {\n                reg=compiler_findglobal(c, var, true);\n                if (reg!=REGISTER_UNALLOCATED) {\n                    if (indxnode) {\n                        /* If an indexed global, move the global into a register */\n                        tmp=compiler_regalloctop(c);\n                        codeinfo mv=compiler_movetoregister(c, node, CODEINFO(GLOBAL, reg, 0), tmp);\n                        ninstructions+=mv.ninstructions;\n                        reg=tmp;\n                        mode=ASSIGN_INDEX;\n                    } else mode=ASSIGN_GLBL;\n                }\n            }\n\n            /* Still couldn't resolve it, so generate an error */\n            if (reg==REGISTER_UNALLOCATED) {\n                compiler_error(c, node, COMPILE_SYMBOLNOTDEFINED, (MORPHO_ISSTRING(var) ? MORPHO_GETCSTRING(var) : \"\"));\n                return CODEINFO_EMPTY;\n            }\n        }\n\n        if (indxnode) {\n            right=compiler_compileindexlist(c, indxnode, &istart, &iend);\n            ninstructions+=right.ninstructions;\n        }\n\n        /* Evaluate the rhs */\n        right = compiler_nodetobytecode(c, node->right, (mode!=ASSIGN_VAR ? REGISTER_UNALLOCATED : reg));\n        ninstructions+=right.ninstructions;\n\n        switch (mode) {\n            case ASSIGN_VAR:\n                /* Move to a register */\n                ret=compiler_movetoregister(c, node, right, reg);\n                ninstructions+=ret.ninstructions;\n                break;\n            case ASSIGN_UPVALUE:\n                ret=compiler_movetoupvalue(c, node, right, reg);\n                ninstructions+=ret.ninstructions;\n                break;\n            case ASSIGN_OBJ:\n                ret=compiler_movetoproperty(c, node, right, varnode);\n                ninstructions+=ret.ninstructions;\n                break;\n            case ASSIGN_GLBL:\n                ret=compiler_movetoglobal(c, node, right, reg);\n                ninstructions+=ret.ninstructions;\n                break;\n            case ASSIGN_INDEX:\n            {\n                /* Make sure the rhs is in the register after the last index */\n                if (!(CODEINFO_ISREGISTER(right) && right.dest==iend+1)) {\n                    registerindx last=compiler_regtempwithindx(c, iend+1);\n                    compiler_releaseoperand(c, right);\n                    right=compiler_movetoregister(c, node, right, last);\n                    ninstructions+=right.ninstructions;\n                }\n                compiler_regfreetoend(c, right.dest+1);\n                if (right.dest!=iend+1) {\n                    UNREACHABLE(\"Failed register allocation in compiling SIX instruction.\");\n                }\n                compiler_addinstruction(c, ENCODE(OP_SIX, reg, istart, right.dest), node);\n                ninstructions++;\n            }\n                break;\n            case ASSIGN_UPINDEX:\n                UNREACHABLE(\"Assign to indexed upvalue not implemented.\");\n                break;\n        }\n    } else {\n        compiler_error(c, node, COMPILE_INVALIDASSIGNMENT);\n        return CODEINFO_EMPTY;\n    }\n\n    if (tmp!=REGISTER_UNALLOCATED) compiler_regfreetemp(c, tmp);\n\n    /* Make sure the correct number of instructions is returned */\n    ret=right;\n    ret.ninstructions=ninstructions;\n\n    return ret;\n}\n\n/* Compiles property lookup */\nstatic codeinfo compiler_property(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo left = CODEINFO_EMPTY, prop = CODEINFO_EMPTY;\n    registerindx out = compiler_regtemp(c, reqout);\n\n    /* The left hand side should evaluate to the object in question */\n    left = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED);\n    unsigned int ninstructions=left.ninstructions;\n\n    if (!(CODEINFO_ISREGISTER(left))) {\n        /* Ensure we're working with a register */\n        left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n        ninstructions+=left.ninstructions;\n    }\n\n    /* The right hand side should be a method name */\n    syntaxtreenode *selector = compiler_getnode(c, node->right);\n    if (selector->type==NODE_SYMBOL) {\n        prop=compiler_addsymbolwithsizecheck(c, selector, selector->content);\n        ninstructions+=prop.ninstructions;\n    } else {\n        compiler_error(c, selector, COMPILE_PROPERTYNAMERQD);\n    }\n\n    if (out !=REGISTER_UNALLOCATED) {\n        compiler_addinstruction(c, ENCODE(OP_LPR, out, left.dest, prop.dest), node);\n        ninstructions++;\n        compiler_releaseoperand(c, left);\n        if (CODEINFO_ISREGISTER(prop)) compiler_releaseoperand(c, prop);\n    }\n\n    return CODEINFO(REGISTER, out, ninstructions);\n}\n\n/** Compiles the dot operator, which may be property lookup or a method call */\nstatic codeinfo compiler_dot(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    syntaxtreenode *left = compiler_getnode(c, node->left),\n                   *right = compiler_getnode(c, node->right);\n    value out=MORPHO_NIL;\n    \n    if (left->type==NODE_SYMBOL && \n        right->type==NODE_SYMBOL &&\n        compiler_findsymbolwithnamespace(c, node, left->content, right->content, &out)) {\n        \n        if (MORPHO_ISINTEGER(out)) {\n            return CODEINFO(GLOBAL, MORPHO_GETINTEGERVALUE(out), 0);\n        } else if (MORPHO_ISFUNCTION(out) ||\n                   MORPHO_ISMETAFUNCTION(out) ||\n                   MORPHO_ISBUILTINFUNCTION(out) ||\n                   MORPHO_ISCLASS(out)) {\n            registerindx indx = compiler_addconstant(c, node, out, true, false);\n            return CODEINFO(CONSTANT, indx, 0);\n        } else {\n            UNREACHABLE(\"Namespace dictionary contains noninteger value\");\n        }\n    }\n    \n    return compiler_property(c, node, reqout);\n}\n\n/* Moves the result of a calculation to an object property */\nstatic codeinfo compiler_movetoproperty(compiler *c, syntaxtreenode *node, codeinfo in, syntaxtreenode *obj) {\n    codeinfo prop = CODEINFO_EMPTY;\n\n    /* The left hand side of obj should evaluate to the object in question */\n    codeinfo left = compiler_nodetobytecode(c, obj->left, REGISTER_UNALLOCATED);\n    unsigned int ninstructions=left.ninstructions;\n\n    if (!(CODEINFO_ISREGISTER(left) || CODEINFO_ISSHORTCONSTANT(left))) {\n        /* Ensure we're working with a register or a constant */\n        left=compiler_movetoregister(c, node, left, REGISTER_UNALLOCATED);\n        ninstructions+=left.ninstructions;\n    }\n\n    /* The right hand side of obj should be a property name */\n    syntaxtreenode *selector = compiler_getnode(c, obj->right);\n    if (selector->type==NODE_SYMBOL) {\n        prop=compiler_addsymbolwithsizecheck(c, selector, selector->content);\n        ninstructions+=prop.ninstructions;\n    } else {\n        compiler_error(c, selector, COMPILE_PROPERTYNAMERQD);\n        return CODEINFO_EMPTY;\n    }\n\n    codeinfo store = in;\n    if (!CODEINFO_ISREGISTER(in)) {\n        /* Ensure we're working with a register */\n        store=compiler_movetoregister(c, node, store, REGISTER_UNALLOCATED);\n        ninstructions+=store.ninstructions;\n        compiler_releaseoperand(c, store);\n    }\n\n    compiler_addinstruction(c, ENCODE(OP_SPR, left.dest, prop.dest, store.dest), node);\n    ninstructions++;\n\n    if (CODEINFO_ISREGISTER(prop)) compiler_releaseoperand(c, prop);\n\n    return CODEINFO(CODEINFO_ISCONSTANT(in), in.dest, ninstructions);\n}\n\n/** Compiles a node to bytecode */\nstatic codeinfo compiler_nodetobytecode(compiler *c, syntaxtreeindx indx, registerindx reqout) {\n    syntaxtreenode *node = NULL;\n    codeinfo ret = CODEINFO_EMPTY;\n\n    if (indx!=SYNTAXTREE_UNCONNECTED) {\n        node=compiler_getnode(c, indx);\n    } else {\n        UNREACHABLE(\"compiling an unexpectedly blank node [run with debugger]\");\n    }\n\n    if (!node) return CODEINFO_EMPTY;\n\n#ifdef MORPHO_DEBUG_DISPLAYREGISTERALLOCATION\n    compiler_regshow(c);\n#endif\n\n    compiler_nodefn compilenodefn = compiler_getrule(node->type)->nodefn;\n\n    if (compilenodefn!=NULL) {\n        ret = (*compilenodefn) (c, node, reqout);\n        if (CODEINFO_ISREGISTER(ret) && ret.dest!=REGISTER_UNALLOCATED &&!compiler_isregalloc(c, ret.dest)) {\n            UNREACHABLE(\"compiler node returned an unallocated register\");\n        }\n    } else {\n        UNREACHABLE(\"unhandled syntax tree node type [Check bytecode compiler definition table]\");\n    }\n\n    return ret;\n}\n\n/** Compiles the current syntax tree to bytecode */\nstatic bool compiler_tobytecode(compiler *c, program *out) {\n    if (c->tree.tree.count>0 && c->tree.entry!=SYNTAXTREE_UNCONNECTED) {\n        codeinfo info=compiler_nodetobytecode(c, c->tree.entry, REGISTER_UNALLOCATED);\n        compiler_releaseoperand(c, info);\n    }\n    compiler_checkoutstandingforwardreference(c);\n    if (c->tree.tree.count==0) {\n        compiler_addinstruction(c, ENCODE_BYTE(OP_END), NULL);\n    } else if (c->tree.entry>=0) {\n        compiler_addinstruction(c, ENCODE_BYTE(OP_END), syntaxtree_nodefromindx(&c->tree, c->tree.entry));\n    }\n\n    return true;\n}\n\n/* **********************************************************************\n* Modules\n* ********************************************************************** */\n\n/** Strip an 'end' instruction at the end of the program */\nvoid compiler_stripend(compiler *c) {\n    program *out = c->out;\n    instructionindx last = out->code.count; /* End of old code */\n\n    if (last>0 && out->code.data[last-1] == ENCODE_BYTE(OP_END)) {\n        out->code.count--;\n        debugannotation_stripend(&out->annotations);\n    }\n}\n\n/** Copies the globals across from one compiler to another. The globals dictionary maps keys to global numbers\n * @param[in] src source dictionary\n * @param[in] dest destination dictionary\n * @param[in] compare (optional) a dictionary to check the contents against; globals are only copied if they also appear in compare */\nvoid compiler_copysymbols(dictionary *src, dictionary *dest, dictionary *compare) {\n    for (unsigned int i=0; i<src->capacity; i++) {\n        value key = src->contents[i].key;\n        if (!MORPHO_ISNIL(key)) {\n            if (compare && !dictionary_get(compare, key, NULL)) continue;\n            \n            if (MORPHO_ISSTRING(key) &&\n                MORPHO_GETCSTRING(key)[0]=='_') continue;\n\n            dictionary_insert(dest, key, src->contents[i].val);\n        }\n    }\n}\n\n/** Copies the global function ref into the destination compiler's current function ref */\nvoid compiler_copyfunctionref(compiler *src, compiler *dest, dictionary *fordict) {\n    functionstate *in=compiler_currentfunctionstate(src);\n    functionstate *out=compiler_currentfunctionstate(dest);\n    \n    if (fordict) {\n        for (int i=0; i<in->functionref.count; i++) {\n            functionref *ref=&in->functionref.data[i];\n            if (!dictionary_get(fordict, ref->function->name, NULL)) continue;\n            \n            varray_functionrefwrite(&out->functionref, in->functionref.data[i]);\n        }\n    } else varray_functionrefadd(&out->functionref, in->functionref.data, in->functionref.count);\n}\n\n/** Copies the global function ref into the designated namespace, checking whether the functions are present in the dictionary, and creating metafunctions where necessary */\nvoid compiler_copyfunctionreftonamespace(compiler *src, namespc *dest, dictionary *fordict) {\n    functionstate *f=compiler_currentfunctionstate(src);\n    \n    dictionary symbols;\n    dictionary_init(&symbols);\n    \n    for (int i=0; i<f->functionref.count; i++) {\n        functionref *ref=&f->functionref.data[i];\n        // Skip if not in the fordict\n        if (fordict && !dictionary_get(fordict, ref->function->name, NULL)) continue;\n        \n        value fn=MORPHO_OBJECT(ref->function);\n        if (dictionary_get(&symbols, ref->function->name, &fn)) {\n            // If the function already exists, wrap in a metafunction\n            _addmatchingfunctionref(src, ref->function->name, MORPHO_OBJECT(ref->function), &fn);\n        }\n        dictionary_insert(&symbols, ref->function->name, fn);\n    }\n    \n    compiler_copysymbols(&symbols, &dest->symbols, NULL);\n    dictionary_clear(&symbols);\n}\n\n/** Searches for a module with given name, returns the file name for inclusion. */\nbool compiler_findmodule(char *name, varray_char *fname) {\n    value out=MORPHO_NIL;\n    bool success=morpho_findresource(MORPHO_RESOURCE_MODULE, name, &out);\n    \n    if (success) {\n        fname->count=0;\n        if (MORPHO_ISSTRING(out)) {\n            varray_charadd(fname, MORPHO_GETCSTRING(out), (int) MORPHO_GETSTRINGLENGTH(out));\n            varray_charwrite(fname, '\\0');\n        }\n        morpho_freeobject(out);\n    }\n    \n    return success;\n}\n\n/** Import a module */\nstatic codeinfo compiler_import(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    varray_char filename;\n    syntaxtreenode *module = compiler_getnode(c, node->left);\n    syntaxtreenode *qual = compiler_getnode(c, node->right);\n    dictionary fordict;\n    namespc *nmspace=NULL;\n    char *fname=NULL;\n    unsigned int start=0, end=0;\n    FILE *f = NULL;\n\n    dictionary_init(&fordict);\n    varray_charinit(&filename);\n\n    if (compiler_checkerror(c)) return CODEINFO_EMPTY;\n\n    while (qual) {\n        if (qual->type==NODE_FOR) {\n            syntaxtreenode *l = compiler_getnode(c, qual->left);\n            if (l && l->type==NODE_SYMBOL) {\n                dictionary_insert(&fordict, l->content, MORPHO_NIL);\n            } else UNREACHABLE(\"Incorrect syntax tree structure in FOR node.\");\n        } else if (qual->type==NODE_AS) {\n            syntaxtreenode *l = compiler_getnode(c, qual->left);\n            if (l && l->type==NODE_SYMBOL) {\n                nmspace=compiler_addnamespace(c, l->content);\n                \n                if (!nmspace) { compiler_error(c, node, ERROR_ALLOCATIONFAILED); return CODEINFO_EMPTY; }\n            } else UNREACHABLE(\"Incorrect syntax tree structure in AS node.\");\n        } else UNREACHABLE(\"Unexpected node type.\");\n        qual=compiler_getnode(c, qual->right);\n    }\n\n    if (module) {\n        if (module->type==NODE_SYMBOL) {\n            dictionary *fndict, *clssdict;\n            \n            if (extension_load(MORPHO_GETCSTRING(module->content), &fndict, &clssdict)) {\n                compiler_copysymbols(clssdict, (nmspace ? &nmspace->symbols: builtin_getclasstable()), (fordict.count>0 ? &fordict : NULL));\n                compiler_copysymbols(fndict, (nmspace ? &nmspace->symbols: builtin_getfunctiontable()), (fordict.count>0 ? &fordict : NULL));\n                \n                if (nmspace) { // Copy classes into the namespace's class table\n                    compiler_copysymbols(clssdict, &nmspace->classes, (fordict.count>0 ? &fordict : NULL));\n                }\n            } else if (compiler_findmodule(MORPHO_GETCSTRING(module->content), &filename)) {\n                fname=filename.data;\n            } else {\n                compiler_error(c, module, COMPILE_MODULENOTFOUND, MORPHO_GETCSTRING(module->content));\n            }\n        } else if (module->type==NODE_STRING) {\n            fname=MORPHO_GETCSTRING(module->content);\n        }\n\n        compiler *root = c;\n        while (root->parent!=NULL) root=root->parent;\n\n        // Check if the module was previously imported\n        if (fname) {\n            objectstring chkmodname = MORPHO_STATICSTRING(fname);\n            value symboldict=MORPHO_NIL;\n            \n            if (dictionary_get(&root->modules, MORPHO_OBJECT(&chkmodname), &symboldict)) {\n                // If so, copy its symbols into the compiler\n                compiler_copysymbols(MORPHO_GETDICTIONARYSTRUCT(symboldict), (nmspace ? &nmspace->symbols: &c->globals), (fordict.count>0 ? &fordict : NULL));\n                \n                goto compiler_import_cleanup;\n            }\n        }\n\n        if (fname) f=file_openrelative(fname, \"r\");\n        else goto compiler_import_cleanup;\n\n        if (f) {\n            value modname=object_stringfromcstring(fname, strlen(fname));\n            value symboldict=MORPHO_NIL;\n\n            /* Read in source */\n            varray_char src;\n            varray_charinit(&src);\n            if (!file_readintovarray(f, &src)) {\n                compiler_error(c, module, COMPILE_IMPORTFLD, fname);\n                goto compiler_import_cleanup;\n            }\n\n            /* Remember the initial position of the code */\n            start=c->out->code.count;\n\n            /* Set up the compiler */\n            compiler cc;\n            compiler_init(src.data, c->out, &cc);\n            compiler_setmodule(&cc, modname);\n            debugannotation_setmodule(&c->out->annotations, modname);\n            cc.parent=c; /* Ensures global variables can be found */\n\n            morpho_compile(src.data, &cc, false, &c->err);\n\n            if (ERROR_SUCCEEDED(c->err)) {\n                compiler_stripend(c);\n                compiler_copysymbols(&cc.globals, (nmspace ? &nmspace->symbols: &c->globals), (fordict.count>0 ? &fordict : NULL));\n                if (nmspace) { // If we're in a namespace, copy the class table into that\n                    compiler_copysymbols(&cc.classes, &nmspace->classes, (fordict.count>0 ? &fordict : NULL));\n                    compiler_copyfunctionreftonamespace(&cc, nmspace, (fordict.count>0 ? &fordict : NULL));\n                } else { // Otherwise just put it into the parent compiler's class table\n                    compiler_copysymbols(&cc.classes, &c->classes, (fordict.count>0 ? &fordict : NULL));\n                    compiler_copyfunctionref(&cc, c, (fordict.count>0 ? &fordict : NULL));\n                }\n                \n                objectdictionary *dict = object_newdictionary(); // Preserve all symbols for further imports\n                if (dict) {\n                    compiler_copysymbols(&cc.globals, &dict->dict, NULL);\n                    symboldict = MORPHO_OBJECT(dict);\n                }\n                \n            } else {\n                c->err.file = (cc.err.file ? cc.err.file : MORPHO_GETCSTRING(modname));\n            }\n            \n            debugannotation_setmodule(&c->out->annotations, compiler_getmodule(c));\n            \n            end=c->out->code.count;\n            \n            compiler_clear(&cc);\n            varray_charclear(&src);\n            \n            dictionary_insert(&root->modules, modname, symboldict);\n        } else compiler_error(c, module, COMPILE_FILENOTFOUND, fname);\n    }\n\ncompiler_import_cleanup:\n    if (f) fclose(f);\n    varray_charclear(&filename);\n    dictionary_clear(&fordict);\n\n    return CODEINFO(REGISTER, REGISTER_UNALLOCATED, end-start);\n}\n\n/** Compile a breakpoint */\nstatic codeinfo compiler_breakpoint(compiler *c, syntaxtreenode *node, registerindx reqout) {\n    codeinfo info = CODEINFO_EMPTY;\n\n    compiler_addinstruction(c, ENCODE_BYTE(OP_BREAK), node);\n\n    if (node->left!=SYNTAXTREE_UNCONNECTED) {\n        info=compiler_nodetobytecode(c, node->left, reqout);\n    }\n\n    info.ninstructions++;\n\n    return info;\n}\n\n/* **********************************************************************\n* Compiler initialization/finalization\n* ********************************************************************** */\n\n/** @brief Initialize a compiler\n *  @param[in]  source  source code to compile\n *  @param[in]  out     destination program to compile to\n *  @param[out] c       compiler structure is filled out */\nvoid compiler_init(const char *source, program *out, compiler *c) {\n    lex_init(&c->lex, source, 1);\n    error_init(&c->err);\n    syntaxtree_init(&c->tree);\n    parse_init(&c->parse, &c->lex, &c->err, &c->tree);\n    compiler_fstackinit(c);\n    dictionary_init(&c->globals);\n    dictionary_init(&c->classes);\n    dictionary_init(&c->modules);\n    if (out) c->fstack[0].func=out->global; /* The global pseudofunction */\n    c->out = out;\n    c->prevfunction = NULL;\n    c->currentclass = NULL;\n    c->currentmethod = NULL;\n    c->namespaces = NULL; \n    c->currentmodule = MORPHO_NIL;\n    c->parent = NULL;\n    c->line = 1; // Count from 1\n}\n\n/** @brief Clear attached data structures from a compiler\n *  @param[in]  c        compiler to clear */\nvoid compiler_clear(compiler *c) {\n    lex_clear(&c->lex);\n    parse_clear(&c->parse);\n    compiler_fstackclear(c);\n    syntaxtree_clear(&c->tree);\n    compiler_clearnamespacelist(c);\n    dictionary_clear(&c->globals); // Keys are bound to the program\n    dictionary_freecontents(&c->modules, true, true);\n    dictionary_clear(&c->modules);\n    dictionary_clear(&c->classes);\n}\n\n/* **********************************************************************\n* Interfaces\n* ********************************************************************** */\n\n/** Interface to the compiler\n * @param[in]  in    A string to compile\n * @param[in]  c     The compiler\n * @param[in]  opt   Whether or not to invoke the optimizer\n * @param[out] err   Pointer to error block on failure\n * @returns    A bool indicating success or failure\n */\nbool morpho_compile(char *in, compiler *c, bool opt, error *err) {\n    program *out = c->out;\n    bool success = false;\n\n    error_clear(&c->err);\n    error_clear(err);\n\n    /* Remove any previous END instruction */\n    compiler_stripend(c);\n    instructionindx last = out->code.count; /* End of old code */\n    \n    /* Initialize lexer */\n    lex_init(&c->lex, in, c->line); /* Count lines from 1. */\n\n    if ((!morpho_parse(&c->parse)) || !ERROR_SUCCEEDED(c->err)) {\n        *err = c->err;\n    } else {\n#ifdef MORPHO_DEBUG_DISPLAYSYNTAXTREE\n        syntaxtree_print(&c->tree);\n#endif\n#ifdef MORPHO_DEBUG_FILLGLOBALCONSTANTTABLE\n        if (out->global->konst.count<255) {\n            for (unsigned int i=0; i<256; i++) compiler_addconstant(c, NULL, MORPHO_INTEGER(i), false, false);\n        }\n#endif\n\n        compiler_tobytecode(c, out);\n        if (ERROR_SUCCEEDED(c->err)) {\n            compiler_setfunctionregistercount(c);\n            program_setentry(out, last);\n            success=true;\n        } else {\n            *err = c->err;\n        }\n    }\n\n    if (success) {\n        if (opt && optimizer) (*optimizer) (c->out);\n        \n        c->line=c->lex.line+1; // Update the line counter if compilation was a success; assumes a new line every time morpho_compile is called.\n    }\n\n    return success;\n}\n\n/* **********************************************************************\n * Public interfaces\n * ********************************************************************** */\n\n/** Creates a new compiler */\ncompiler *morpho_newcompiler(program *out) {\n    compiler *new = MORPHO_MALLOC(sizeof(compiler));\n\n    if (new) compiler_init(\"\", out, new);\n\n    return new;\n}\n\n/** Frees a compiler */\nvoid morpho_freecompiler(compiler *c) {\n    compiler_clear(c);\n    MORPHO_FREE(c);\n}\n\n/* **********************************************************************\n* Initialization/Finalization\n* ********************************************************************** */\n\nvoid morpho_setbaseclass(value klss) {\n    if (MORPHO_ISCLASS(klss)) baseclass=MORPHO_GETCLASS(klss);\n}\n\nvoid morpho_setoptimizer(optimizerfn *opt) {\n    optimizer = opt;\n}\n\n/** Initializes the compiler */\nvoid compile_initialize(void) {\n    _selfsymbol=builtin_internsymbolascstring(\"self\");\n    \n    /** Types we need to refer to */\n    _closuretype = MORPHO_OBJECT(object_getveneerclass(OBJECT_CLOSURE));\n    \n    optimizer = NULL;\n\n    /* Compile errors */\n    morpho_defineerror(COMPILE_SYMBOLNOTDEFINED, ERROR_COMPILE, COMPILE_SYMBOLNOTDEFINED_MSG);\n    morpho_defineerror(COMPILE_SYMBOLNOTDEFINEDNMSPC, ERROR_COMPILE, COMPILE_SYMBOLNOTDEFINEDNMSPC_MSG);\n    morpho_defineerror(COMPILE_TOOMANYCONSTANTS, ERROR_COMPILE, COMPILE_TOOMANYCONSTANTS_MSG);\n    morpho_defineerror(COMPILE_ARGSNOTSYMBOLS, ERROR_COMPILE, COMPILE_ARGSNOTSYMBOLS_MSG);\n    morpho_defineerror(COMPILE_PROPERTYNAMERQD, ERROR_COMPILE, COMPILE_PROPERTYNAMERQD_MSG);\n    morpho_defineerror(COMPILE_SELFOUTSIDECLASS, ERROR_COMPILE, COMPILE_SELFOUTSIDECLASS_MSG);\n    morpho_defineerror(COMPILE_RETURNININITIALIZER, ERROR_COMPILE, COMPILE_RETURNININITIALIZER_MSG);\n    morpho_defineerror(COMPILE_SUPERCLASSNOTFOUND, ERROR_COMPILE, COMPILE_SUPERCLASSNOTFOUND_MSG);\n    morpho_defineerror(COMPILE_SUPEROUTSIDECLASS, ERROR_COMPILE, COMPILE_SUPEROUTSIDECLASS_MSG);\n    morpho_defineerror(COMPILE_NOSUPER, ERROR_COMPILE, COMPILE_NOSUPER_MSG);\n    morpho_defineerror(COMPILE_INVALIDASSIGNMENT, ERROR_COMPILE, COMPILE_INVALIDASSIGNMENT_MSG);\n    morpho_defineerror(COMPILE_CLASSINHERITSELF, ERROR_COMPILE, COMPILE_CLASSINHERITSELF_MSG);\n    morpho_defineerror(COMPILE_TOOMANYARGS, ERROR_COMPILE, COMPILE_TOOMANYARGS_MSG);\n    morpho_defineerror(COMPILE_TOOMANYPARAMS, ERROR_COMPILE, COMPILE_TOOMANYPARAMS_MSG);\n    morpho_defineerror(COMPILE_ISOLATEDSUPER, ERROR_COMPILE, COMPILE_ISOLATEDSUPER_MSG);\n    morpho_defineerror(COMPILE_VARALREADYDECLARED, ERROR_COMPILE, COMPILE_VARALREADYDECLARED_MSG);\n    morpho_defineerror(COMPILE_FILENOTFOUND, ERROR_COMPILE, COMPILE_FILENOTFOUND_MSG);\n    morpho_defineerror(COMPILE_MODULENOTFOUND, ERROR_COMPILE, COMPILE_MODULENOTFOUND_MSG);\n    morpho_defineerror(COMPILE_IMPORTFLD, ERROR_COMPILE, COMPILE_IMPORTFLD_MSG);\n    morpho_defineerror(COMPILE_BRKOTSDLP, ERROR_COMPILE, COMPILE_BRKOTSDLP_MSG);\n    morpho_defineerror(COMPILE_CNTOTSDLP, ERROR_COMPILE, COMPILE_CNTOTSDLP_MSG);\n    morpho_defineerror(COMPILE_OPTPRMDFLT, ERROR_COMPILE, COMPILE_OPTPRMDFLT_MSG);\n    morpho_defineerror(COMPILE_FORWARDREF, ERROR_COMPILE, COMPILE_FORWARDREF_MSG);\n    morpho_defineerror(COMPILE_MLTVARPRMTR, ERROR_COMPILE, COMPILE_MLTVARPRMTR_MSG);\n    morpho_defineerror(COMPILE_MSSNGLOOPBDY, ERROR_COMPILE, COMPILE_MSSNGLOOPBDY_MSG);\n    morpho_defineerror(COMPILE_NSTDCLSS, ERROR_COMPILE, COMPILE_NSTDCLSS_MSG);\n    morpho_defineerror(COMPILE_VARPRMLST, ERROR_COMPILE, COMPILE_VARPRMLST_MSG);\n    morpho_defineerror(COMPILE_INVLDLBL, ERROR_COMPILE, COMPILE_INVLDLBL_MSG);\n    morpho_defineerror(COMPILE_MSSNGINDX, ERROR_COMPILE, COMPILE_MSSNGINDX_MSG);\n    morpho_defineerror(COMPILE_TYPEVIOLATION, ERROR_COMPILE, COMPILE_TYPEVIOLATION_MSG);\n    morpho_defineerror(COMPILE_UNKNWNTYPE, ERROR_COMPILE, COMPILE_UNKNWNTYPE_MSG);\n    morpho_defineerror(COMPILE_UNKNWNNMSPC, ERROR_COMPILE, COMPILE_UNKNWNNMSPC_MSG);\n    morpho_defineerror(COMPILE_UNKNWNTYPENMSPC, ERROR_COMPILE, COMPILE_UNKNWNTYPENMSPC_MSG);\n    morpho_defineerror(COMPILE_CLSSLNRZ, ERROR_COMPILE, COMPILE_CLSSLNRZ_MSG);\n    morpho_defineerror(COMPILE_CLSSDPLCTIMPL, ERROR_COMPILE, COMPILE_CLSSDPLCTIMPL_MSG);\n    \n    morpho_addfinalizefn(compile_finalize);\n}\n\n/** Finalizes the compiler */\nvoid compile_finalize(void) {\n}\n"
  },
  {
    "path": "src/core/compile.h",
    "content": "/** @file compile.h\n *  @author T J Atherton\n *\n *  @brief Compiles raw input to Morpho instructions\n*/\n\n#ifndef compile_h\n#define compile_h\n\n#define MORPHO_CORE\n\n#include \"core.h\"\n#include \"syntaxtree.h\"\n#include \"parse.h\"\n#include \"debug.h\"\n\n/* **********************************************************************\n * Compiler error messages\n * ********************************************************************** */\n\n#define COMPILE_SYMBOLNOTDEFINED          \"SymblUndf\"\n#define COMPILE_SYMBOLNOTDEFINED_MSG      \"Symbol '%s' not defined.\"\n\n#define COMPILE_SYMBOLNOTDEFINEDNMSPC     \"SymblUndfNmSpc\"\n#define COMPILE_SYMBOLNOTDEFINEDNMSPC_MSG \"Symbol '%s' not defined in namespace '%s'.\"\n\n#define COMPILE_TOOMANYCONSTANTS          \"TooMnyCnst\"\n#define COMPILE_TOOMANYCONSTANTS_MSG      \"Too many constants.\"\n\n#define COMPILE_ARGSNOTSYMBOLS            \"FnPrmSymb\"\n#define COMPILE_ARGSNOTSYMBOLS_MSG        \"Function parameters must be symbols.\"\n\n#define COMPILE_PROPERTYNAMERQD           \"PptyNmRqd\"\n#define COMPILE_PROPERTYNAMERQD_MSG       \"Property name required.\"\n\n#define COMPILE_SELFOUTSIDECLASS          \"SlfOtsdClss\"\n#define COMPILE_SELFOUTSIDECLASS_MSG      \"Cannot use 'self' outside of a class.\"\n\n#define COMPILE_RETURNININITIALIZER       \"InitRtn\"\n#define COMPILE_RETURNININITIALIZER_MSG   \"Cannot return a value in an initializer.\"\n\n#define COMPILE_SUPERCLASSNOTFOUND        \"SprNtFnd\"\n#define COMPILE_SUPERCLASSNOTFOUND_MSG    \"Superclass '%s' not found.\"\n\n#define COMPILE_SUPEROUTSIDECLASS         \"SprOtsdClss\"\n#define COMPILE_SUPEROUTSIDECLASS_MSG     \"Cannot use 'super' outside of a class.\"\n\n#define COMPILE_NOSUPER                   \"SprSelMthd\"\n#define COMPILE_NOSUPER_MSG               \"Can only use 'super' to select a method.\"\n\n#define COMPILE_INVALIDASSIGNMENT         \"InvldAssgn\"\n#define COMPILE_INVALIDASSIGNMENT_MSG     \"Invalid assignment target.\"\n\n#define COMPILE_CLASSINHERITSELF          \"ClssCrcRf\"\n#define COMPILE_CLASSINHERITSELF_MSG      \"A class cannot inherit from itself.\"\n\n#define COMPILE_TOOMANYARGS               \"TooMnyArg\"\n#define COMPILE_TOOMANYARGS_MSG           \"Too many arguments.\"\n\n#define COMPILE_TOOMANYPARAMS             \"TooMnyPrm\"\n#define COMPILE_TOOMANYPARAMS_MSG         \"Too many parameters.\"\n\n#define COMPILE_ISOLATEDSUPER             \"IsoSpr\"\n#define COMPILE_ISOLATEDSUPER_MSG         \"Expect '.' after 'super'.\"\n\n#define COMPILE_VARALREADYDECLARED        \"VblDcl\"\n#define COMPILE_VARALREADYDECLARED_MSG    \"Variable with this name already declared in this scope.\"\n\n#define COMPILE_FILENOTFOUND              \"FlNtFnd\"\n#define COMPILE_FILENOTFOUND_MSG          \"File '%s' not found.\"\n\n#define COMPILE_MODULENOTFOUND            \"MdlNtFnd\"\n#define COMPILE_MODULENOTFOUND_MSG        \"Module '%s' not found.\"\n\n#define COMPILE_IMPORTFLD                 \"ImprtFld\"\n#define COMPILE_IMPORTFLD_MSG             \"Import of file '%s' failed.\"\n\n#define COMPILE_BRKOTSDLP                 \"BrkOtsdLp\"\n#define COMPILE_BRKOTSDLP_MSG             \"Break encountered outside a loop.\"\n\n#define COMPILE_CNTOTSDLP                 \"CntOtsdLp\"\n#define COMPILE_CNTOTSDLP_MSG             \"Continue encountered outside a loop.\"\n\n#define COMPILE_OPTPRMDFLT                \"OptPrmDflt\"\n#define COMPILE_OPTPRMDFLT_MSG            \"Optional parameter default values must be constants.\"\n\n#define COMPILE_FORWARDREF                \"UnrslvdFrwdRf\"\n#define COMPILE_FORWARDREF_MSG            \"Function '%s' is called but not defined in the same scope.\"\n\n#define COMPILE_MLTVARPRMTR               \"MltVarPrmtr\"\n#define COMPILE_MLTVARPRMTR_MSG           \"Functions can have at most one variadic parameter.\"\n\n#define COMPILE_VARPRMLST                 \"VarPrLst\"\n#define COMPILE_VARPRMLST_MSG             \"Cannot have fixed parameters after a variadic parameter.\"\n\n#define COMPILE_MSSNGLOOPBDY              \"MssngLoopBdy\"\n#define COMPILE_MSSNGLOOPBDY_MSG          \"Missing loop body.\"\n\n#define COMPILE_NSTDCLSS                  \"NstdClss\"\n#define COMPILE_NSTDCLSS_MSG              \"Cannot define a class within another class.\"\n\n#define COMPILE_INVLDLBL                  \"InvldLbl\"\n#define COMPILE_INVLDLBL_MSG              \"Invalid label in catch statment.\"\n\n#define COMPILE_MSSNGINDX                 \"MssngIndx\"\n#define COMPILE_MSSNGINDX_MSG             \"Missing index or indices.\"\n\n#define COMPILE_TYPEVIOLATION             \"TypeErr\"\n#define COMPILE_TYPEVIOLATION_MSG         \"Type violation: Attempting to assign type %s to %s variable %s.\"\n\n#define COMPILE_UNKNWNTYPE                \"UnknwnType\"\n#define COMPILE_UNKNWNTYPE_MSG            \"Unknown type '%s'.\"\n\n#define COMPILE_UNKNWNNMSPC               \"UnknwnNmSpc\"\n#define COMPILE_UNKNWNNMSPC_MSG           \"Unknown namespace '%s'.\"\n\n#define COMPILE_UNKNWNTYPENMSPC           \"UnknwnTypeNmSpc\"\n#define COMPILE_UNKNWNTYPENMSPC_MSG       \"Unknown type '%s' in namespace '%s'.\"\n\n#define COMPILE_CLSSLNRZ                  \"ClssLnrz\"\n#define COMPILE_CLSSLNRZ_MSG              \"Can't linearize class %s: Check parent and ancestor classes for conflicting inheritance order.\"\n\n#define COMPILE_CLSSDPLCTIMPL             \"ClssDplctImpl\"\n#define COMPILE_CLSSDPLCTIMPL_MSG         \"Duplicate implementation of method %s with same signature in class %s\"\n\n/* **********************************************************************\n * Compiler typedefs\n * ********************************************************************** */\n\n/* -------------------------------------------------------\n * Track globals\n * ------------------------------------------------------- */\n\n/** @brief Value to indicate a global has not been allocated */\n#define GLOBAL_UNALLOCATED -1\n\n/* -------------------------------------------------------\n * Track register allocation\n * ------------------------------------------------------- */\n\n/** @brief Index of a register */\ntypedef int registerindx;\n\n/** @brief Value to indicate a register has not been allocated */\n#define REGISTER_UNALLOCATED -1\n\n/** This structure tracks the contents of each register as the function\n    is being compiled. */\ntypedef struct {\n    bool isallocated; /** Whether the register has been allocated */\n    bool iscaptured; /** Whether the register becomes an upvalue */\n    bool isoptionalarg; /** Whether the register contains an optional argument */\n    unsigned int scopedepth; /** Scope depth at which the register was allocated */\n    value symbol; /** Symbol associated with the register */\n    value type; /** Type associated with the register */\n    value currenttype; /** Current type held by the register */\n} registeralloc;\n\n#define REGISTERALLOC_EMPTY(sdepth, symb) ((registeralloc) {.isallocated=true, .iscaptured=false, .isoptionalarg=false, .scopedepth=sdepth, .symbol=symb, .type=MORPHO_NIL, .currenttype=MORPHO_NIL})\n\nDECLARE_VARRAY(registeralloc, registeralloc)\n\n/* -------------------------------------------------------\n * Codeinfo\n * ------------------------------------------------------- */\n\ntypedef enum {\n    REGISTER,\n    CONSTANT,\n    UPVALUE,\n    GLOBAL,\n    VALUE,  // Used by the optimizer\n    NOTHING //\n} returntype;\n\ntypedef struct {\n    returntype returntype;\n    registerindx dest;\n    unsigned int ninstructions;\n} codeinfo;\n\n#define CODEINFO_ISREGISTER(info) (info.returntype==REGISTER)\n#define CODEINFO_ISCONSTANT(info) (info.returntype==CONSTANT)\n#define CODEINFO_ISSHORTCONSTANT(info) (info.returntype==CONSTANT && info.dest<MORPHO_MAXREGISTERS)\n#define CODEINFO_ISUPVALUE(info) (info.returntype==UPVALUE)\n#define CODEINFO_ISGLOBAL(info) (info.returntype==GLOBAL)\n\n#define CODEINFO(c, d, n) ((codeinfo) { .returntype=(c), .dest=(d), .ninstructions=(n)})\n#define CODEINFO_EMPTY CODEINFO(REGISTER, REGISTER_UNALLOCATED, 0)\n\n/* -------------------------------------------------------\n * Forward function references\n * ------------------------------------------------------- */\n\ntypedef struct {\n    value symbol; /** Symbol associated with the reference */\n    syntaxtreenode *node; /** Syntax tree node associated with the forward reference */\n    returntype returntype; /** Return type */\n    registerindx dest; /** Index into constant table */\n    unsigned int scopedepth; /** Scope depth at which the function with the forward reference occurred */\n} forwardreference;\n\nDECLARE_VARRAY(forwardreference, forwardreference)\n\n/* -------------------------------------------------------\n * Visible functions\n * ------------------------------------------------------- */\n\ntypedef struct {\n    value symbol; /** Symbol associated with the reference */\n    objectfunction *function; /** The function itself */\n    unsigned int scopedepth; /** Scope depth at which the function was seen */\n    registerindx reg; /** Register corresponding to the closure */\n} functionref;\n\nDECLARE_VARRAY(functionref, functionref)\n\n/* -------------------------------------------------------\n * Function types\n * ------------------------------------------------------- */\n\n/** The type of the current function */\ntypedef enum {\n    FUNCTION,\n    METHOD, /* For methods */\n    INITIALIZER /* For initializer methods */\n} functiontype;\n\n#define FUNCTIONTYPE_ISMETHOD(f) (f==FUNCTION ? false : true)\n\n#define FUNCTIONTYPE_ISINITIALIZER(f) (f==INITIALIZER)\n\n/** This structure tracks compiler information for the current function. */\ntypedef struct {\n    objectfunction *func;\n    functiontype type;\n    varray_registeralloc registers;\n    varray_upvalue upvalues;\n    varray_forwardreference forwardref;\n    varray_functionref functionref; /* Functions visible within this state */\n    registerindx varg;\n    unsigned int nreg; /* Largest number of registers used */\n    unsigned int scopedepth;\n    unsigned int loopdepth; /* Count number of nesting depths of a loop */\n    bool inargs; /* Set while compiling function calls to ensure allocations are at the top of the stack */\n    //unsigned int nposn; /* Number of positional args recorded in latest call */\n    //unsigned int nopt; /* Number of optional args recorded in latest call */\n} functionstate;\n\n/* -------------------------------------------------------\n * Lists\n * ------------------------------------------------------- */\n\n/** This structure holds a list as it is being created */\ntypedef struct scompilerlist {\n    varray_value entries;\n    struct scompilerlist *next;\n} compilerlist;\n\n/* -------------------------------------------------------\n * Namespaces\n * ------------------------------------------------------- */\n\ntypedef struct _namespc {\n    value label; /** Label  */\n    dictionary symbols; /** Symbol table */\n    dictionary classes; /** Class table */\n    \n    struct _namespc *next; /** Make a linked list of namespaces */\n} namespc;\n\n/* -------------------------------------------------------\n * Overall state of the compiler\n * ------------------------------------------------------- */\n\n/** @brief A structure that stores the state of a compiler */\ntypedef struct scompiler {\n    lexer lex;\n    parser parse;\n    \n    syntaxtree tree;\n    \n    error err;\n    \n    /* Line number */\n    int line; \n    \n    /* Globals */\n    dictionary globals;\n    \n    /* Classes */\n    dictionary classes;\n    \n    /* Function state stack */\n    functionstate fstack[MORPHO_CALLFRAMESTACKSIZE];\n    indx fstackp;\n    \n    /* Most recently completed function declaration; used to bind method definitions */\n    objectfunction *prevfunction;\n    \n    /* Current class being compiled */\n    objectclass *currentclass;\n    \n    /* Syntax tree node of the current method being compiled */\n    syntaxtreenode *currentmethod;\n    \n    /* Namespaces */\n    namespc *namespaces;\n    \n    /* Current module */\n    value currentmodule;\n    \n    /* Program we're compiling to */\n    program *out;\n    \n    /* Modules included */\n    dictionary modules;\n    \n    /* The parent compiler */\n    struct scompiler *parent;\n} compiler;\n\n/* -------------------------------------------------------\n * AST nodes are now compiled by the bytecode compiler\n * ------------------------------------------------------- */\n\n/** A compiler_nodefn takes a syntax tree node and compiles it to bytecode */\ntypedef codeinfo (*compiler_nodefn) (compiler *c, syntaxtreenode *node, registerindx reg);\n\n/** @brief A compilenoderule rule will be defined for each syntax tree node type,\n *  providing a function to compile the node. */\ntypedef struct {\n    compiler_nodefn nodefn;\n} compilenoderule;\n\nvoid compiler_init(const char *source, program *out, compiler *c);\nvoid compiler_clear(compiler *c);\n\n#endif /* compile_h */\n"
  },
  {
    "path": "src/core/core.h",
    "content": "/** @file core.h\n *  @author T J Atherton\n *\n *  @brief Data types for core Morpho components\n*/\n\n#ifndef core_h\n#define core_h\n\n#include <stdio.h>\n#include <stddef.h>\n\ntypedef struct svm vm;\n\n#include \"error.h\"\n#include \"random.h\"\n#include \"varray.h\"\n#include \"value.h\"\n#include \"object.h\"\n#include \"common.h\"\n#include \"dictionary.h\"\n#include \"builtin.h\"\n#include \"platform.h\"\n\n#include \"program.h\"\n\n/* **********************************************************************\n * Instruction format\n * ********************************************************************** */\n\n/** @brief A Morpho instruction\n *\n *  @details Each instruction fits into a 32 bit unsigned int with the following arrangement\n *  <pre>\n *         24      16      8       0\n *  .......|.......|.......|.......|\n *                          ***op*** Opcode (gives 256 instructions)\n *                  ***A****         Operand A\n *          ***B****                 } Operands B & C\n *  ***C****                         }\n *                                      OR\n *  **Bx************                 } Operand Bx as unsigned short\n *  *sBx************                 } Signed operand Bx\n *                                      OR\n *  ***********LBx**********         } 24 bit unsigned integer\n * </pre>\n */\n\n/* ---------------------------------------------\n * Macros for encoding and decoding instructions\n * --------------------------------------------- */\n\n/** Encodes an instruction with operands A, B and C. */\n#define ENCODE(op, A, B, C) ( (((unsigned) op) & 0xff) | ((A & 0xff) << 8) | ((B & 0xff) << 16) | ((C & 0xff) << 24) )\n\n/** Encodes an instruction with no operands */\n#define ENCODE_BYTE(op) (((unsigned) op) & 0xff)\n\n/** Encodes an instruction with operand A */\n#define ENCODE_SINGLE(op, A) ( (((unsigned) op) & 0xff)  | ((A & 0xff) << 8))\n\n/** Encodes an instruction with two operands A and B */\n#define ENCODE_DOUBLE(op, A, B) ( (((unsigned) op) & 0xff)  | ((A & 0xff) << 8) | ((B & 0xff) << 16))\n\n/** Encodes an instruction with operand A and long operand Bx */\n#define ENCODE_LONG(op, A, Bx) ( (instruction) (((unsigned) op) & 0xff) | (instruction) ((A & 0xff) << 8) | (instruction) ((Bx & 0xffff) << 16) )\n\n/** Encodes an instruction with operand Ax and extended operand Bl */\n#define ENCODE_EXTENDED(op, Ax) ( (((unsigned) op) & 0xff) | ((A & 0xffffff) << 8) )\n\n#define MASK_OP     (0xff)\n#define MASK_A      (0xff << 8)\n#define MASK_B      (0xff << 16)\n#define MASK_C      (0xff << 24)\n\n/** Encodes an empty operand */\n#define ENCODE_EMPTYOPERAND 0\n\n/** Decode the opcode */\n#define DECODE_OP(x) (x & 0xff)\n\n/** Decode operand A */\n#define DECODE_A(x) ((x>>8) & (0xff))\n/** Decode operand B */\n#define DECODE_B(x) ((x>>16) & (0xff))\n/** Decode operand C */\n#define DECODE_C(x) ((x>>24) & (0xff))\n\n/** Decode long operand Bx */\n#define DECODE_Bx(x) ((x>>16) & (0xffff))\n\n/** Decode signed long operand Bx */\n#define DECODE_sBx(x) ((short) ((x>>16) & (0xffff)))\n\n/* -----------------------------\n * Opcodes (built automatically)\n * ----------------------------- */\n\ntypedef enum {\n  #define OPCODE(name) OP_##name,\n  #include \"opcodes.h\"\n  #undef OPCODE\n} opcode;\n\n/* **********************************************************************\n * Virtual machine\n * ********************************************************************** */\n\n/** @brief Maximum number of registers per call frame. */\n#define MORPHO_MAXREGISTERS 255\n\ntypedef struct {\n    objectfunction *function;\n    objectclosure *closure;\n    ptrdiff_t roffset; // Offset of register from base\n    instruction *pc;\n    unsigned int stackcount;\n    unsigned int returnreg; // Stores where any return value should be placed\n    bool ret; // Should the interpreter return from this frame?\n    int nopt; // Number of optional arguments called \n#ifdef MORPHO_PROFILER\n    objectbuiltinfunction *inbuiltinfunction; // Keep track if we're in a built in function\n#endif\n} callframe;\n\n/* **********************************************************************\n * Error handlers\n * ********************************************************************** */\n\ntypedef struct {\n    callframe *fp;\n    value dict;\n} errorhandler;\n\n/* **********************************************************************\n * Debugger backend\n * ********************************************************************** */\n\ntypedef struct {\n    bool singlestep; /** Is single step mode on? */\n    \n    struct svm *currentvm; /** Current virtual machine on entry */\n    int currentline; /** Record current line */\n    objectfunction *currentfunc; /** Record current function */\n    value currentmodule; /** Current module */\n    instructionindx iindx; /** Record current instruction */\n    \n    error *err; /** Report errors */\n    \n    int nbreakpoints; /** Number of active breakpoints */\n    varray_char breakpoints; /** Keep track of breakpoints */\n} debugger;\n\n/* **********************************************************************\n * Profiler\n * ********************************************************************** */\n\n#ifdef MORPHO_PROFILER\n/** @brief Morpho profiler */\ntypedef struct {\n    MorphoThread profiler;\n    MorphoMutex profile_lock;\n    bool profiler_quit;\n    dictionary profile_dict;\n    clock_t start;\n    clock_t end;\n    program *program; \n} profiler;\n#endif\n\n/* **********************************************************************\n * Virtual machines\n * ********************************************************************** */\n\n/** Gray list for garbage collection */\ntypedef struct {\n    unsigned int graycount;\n    unsigned int graycapacity;\n    object **list;\n} graylist;\n\n/** @brief Highest register addressable in a window. */\n#define VM_MAXIMUMREGISTERNUMBER 255\n\n/** Varrays of vms */\nDECLARE_VARRAY(vm, vm*)\n\n/** @brief A Morpho virtual machine and its current state */\nstruct svm {\n    program *current; /** The current program being executed */\n\n    varray_value globals; /** Global variables */\n    varray_value tlvars; /** Thread-local variables */\n    varray_value stack; /** The stack */\n    varray_value retain; /** List of values to retain across GC */\n    callframe frame[MORPHO_CALLFRAMESTACKSIZE]; /** The call frame stack */\n    errorhandler errorhandlers[MORPHO_ERRORHANDLERSTACKSIZE]; /** Error handler stack */\n\n    instruction *instructions; /* Base of instructions */\n    value *konst; /* Current constant table */\n    callframe *fp; /* Frame pointer saved on exit */\n    callframe *fpmax; /* Maximum value of the frame pointer */\n    errorhandler *ehp; /* Error handler pointer */\n\n    error err; /** An error struct that will be filled out when an error occurs */\n    callframe *errfp; /** Record frame pointer when an error occured */\n\n    object *objects; /** Linked list of objects */\n    graylist gray; /** Graylist for garbage collection */\n    size_t bound; /** Estimated size of bound bytes */\n    size_t nextgc; /** Next garbage collection threshold */\n\n    debugger *debug; \n\n#ifdef MORPHO_PROFILER\n    profiler *profiler;\n    enum { VM_RUNNING, VM_INGC } status; \n#endif\n    \n    objectupvalue *openupvalues; /** Linked list of open upvalues */\n    \n    vm *parent; /** Parent vm */\n    varray_vm subkernels; /** Subkernels */\n    \n    morphoprintfn printfn; /** Print callback */\n    void *printref; /** Print callback reference */\n    varray_char buffer; /** Buffer for printing */\n    \n    morphoinputfn inputfn; /** Input callback */\n    void *inputref; /** Input callback reference */\n    \n    morphowarningfn warningfn; /** Warning callback */\n    void *warningref; /** Warning callback reference */\n    \n    morphodebuggerfn debuggerfn; /** Debugger callback */\n    void *debuggerref; /** Debugger callback reference */\n    \n    _MORPHO_PADDING; /** Ensure subkernels do not cause false sharing */\n};\n\n/* **********************************************************************\n* Initializers\n* ********************************************************************** */\n\nvoid compile_initialize(void);\nvoid compile_finalize(void);\n\n#endif /* core_h */\n"
  },
  {
    "path": "src/core/gc.c",
    "content": "/** @file gc.c\n *  @author T J Atherton\n *\n *  @brief Morpho garbage collector\n */\n\n#include \"vm.h\"\n#include \"gc.h\"\n\nextern vm *globalvm;\n\n#include \"compile.h\"\n#include \"morpho.h\"\n\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\nextern dictionary sizecheck;\n#endif\n\n/* **********************************************************************\n * Gray list\n * ********************************************************************** */\n\n/* Initialize the gray list */\nvoid vm_graylistinit(graylist *g) {\n    g->graycapacity=0;\n    g->graycount=0;\n    g->list=NULL;\n}\n\n/* Clear the gray list */\nvoid vm_graylistclear(graylist *g) {\n    if (g->list) free(g->list);\n    vm_graylistinit(g);\n}\n\n/* Add an object to the gray list */\nvoid vm_graylistadd(graylist *g, object *obj) {\n    if (g->graycount+1>=g->graycapacity) {\n        g->graycapacity*=2;\n        if (g->graycapacity<8) g->graycapacity=8;\n        g->list=realloc(g->list, g->graycapacity*sizeof(object *));\n    }\n\n    if (g->list) {\n        g->list[g->graycount]=obj;\n        g->graycount++;\n    }\n}\n\n/* **********************************************************************\n * Garbage collector\n * ********************************************************************** */\n\n/** Recalculates the size of bound objects to the VM */\nsize_t vm_gcrecalculatesize(vm *v) {\n    size_t size = 0;\n    for (object *ob=v->objects; ob!=NULL; ob=ob->next) {\n        size+=object_size(ob);\n    }\n    return size;\n}\n\n/** Marks an object as reachable */\nvoid vm_gcmarkobject(vm *v, object *obj) {\n    if (!obj || obj->status!=OBJECT_ISUNMARKED) return;\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"Marking %p \", obj);\n    morpho_printvalue(v, MORPHO_OBJECT(obj));\n    morpho_printf(v, \"\\n\");\n#endif\n    obj->status=OBJECT_ISMARKED;\n\n    vm_graylistadd(&v->gray, obj);\n}\n\n/** Marks a value as reachable */\nvoid vm_gcmarkvalue(vm *v, value val) {\n    if (MORPHO_ISOBJECT(val)) {\n        vm_gcmarkobject(v, MORPHO_GETOBJECT(val));\n    }\n}\n\n/** Marks all entries in a dictionary */\nvoid vm_gcmarkdictionary(vm *v, dictionary *dict) {\n    for (unsigned int i=0; i<dict->capacity; i++) {\n        if (!MORPHO_ISNIL(dict->contents[i].key)) {\n            vm_gcmarkvalue(v, dict->contents[i].key);\n            vm_gcmarkvalue(v, dict->contents[i].val);\n        }\n    }\n}\n\n/** Marks all entries in an array */\nvoid vm_gcmarkarray(vm *v, varray_value *array) {\n    if (array) for (unsigned int i=0; i<array->count; i++) {\n        vm_gcmarkvalue(v, array->data[i]);\n    }\n}\n\n/** Public veneers */\nvoid morpho_markobject(void *v, object *obj) {\n    vm_gcmarkobject((vm *) v, obj);\n}\n\nvoid morpho_markvalue(void *v, value val) {\n    vm_gcmarkvalue((vm *) v, val);\n}\n\nvoid morpho_markdictionary(void *v, dictionary *dict) {\n    vm_gcmarkdictionary((vm *) v, dict);\n}\n\nvoid morpho_markvarrayvalue(void *v, varray_value *array) {\n    vm_gcmarkarray((vm *) v, array);\n}\n\n/** Searches a vm for all reachable objects */\nvoid vm_gcmarkroots(vm *v) {\n    /** Mark anything on the stack */\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> Stack.\\n\");\n#endif\n    value *stacktop = v->stack.data+v->fp->roffset+v->fp->function->nregs-1;\n    \n    /* Find the largest stack position currently in play */\n    /*for (callframe *f=v->frame; f<v->fp; f++) {\n        value *ftop = v->stack.data+f->roffset+f->function->nregs-1;\n        if (ftop>stacktop) stacktop=ftop;\n    }*/\n\n    //debug_showstack(v);\n\n    for (value *s=stacktop; s>=v->stack.data; s--) {\n        if (MORPHO_ISOBJECT(*s)) vm_gcmarkvalue(v, *s);\n    }\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> Globals.\\n\");\n#endif\n    for (unsigned int i=0; i<v->globals.count; i++) {\n        vm_gcmarkvalue(v, v->globals.data[i]);\n    }\n\n    /** Mark closure objects in use */\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> Closures.\\n\");\n#endif\n    for (callframe *f=v->frame; f && v->fp && f<=v->fp; f++) {\n        if (f->closure) vm_gcmarkobject(v, (object *) f->closure);\n    }\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> Open upvalues.\\n\");\n#endif\n    for (objectupvalue *u=v->openupvalues; u!=NULL; u=u->next) {\n        vm_gcmarkobject(v, (object *) u);\n    }\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> Retain list.\\n\");\n#endif\n    for (int i=0; i<v->retain.count; i++) {\n        vm_gcmarkvalue(v, v->retain.data[i]);\n    }\n    \n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> Thread local storage.\\n\");\n#endif\n    for (int i=0; i<v->tlvars.count; i++) {\n        vm_gcmarkvalue(v, v->tlvars.data[i]);\n    }\n    \n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"> End mark roots.\\n\");\n#endif\n}\n\nvoid vm_gcmarkretainobject(vm *v, object *obj) {\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"Searching object %p \", (void *) obj);\n    morpho_printvalue(v, MORPHO_OBJECT(obj));\n    morpho_printf(v, \"\\n\");\n#endif\n    objecttypedefn *defn=object_getdefn(obj);\n    if (defn->markfn) defn->markfn(obj, v);\n}\n\n/** Forces the GC to search an unmanaged object */\nvoid morpho_searchunmanagedobject(void *v, object *obj) {\n    vm_gcmarkretainobject((vm *) v, obj);\n}\n\n/** Trace all objects on the graylist */\nvoid vm_gctrace(vm *v) {\n    while (v->gray.graycount>0) {\n        object *obj=v->gray.list[v->gray.graycount-1];\n        v->gray.graycount--;\n        vm_gcmarkretainobject(v, obj);\n    }\n}\n\n/** Go through the VM's object list and free all unmarked objects */\nvoid vm_gcsweep(vm *v) {\n    object *prev=NULL;\n    object *obj = v->objects;\n    while (obj!=NULL) {\n        if (obj->status==OBJECT_ISMARKED) {\n            prev=obj;\n            obj->status=OBJECT_ISUNMARKED; /* Clear for the next cycle */\n            obj=obj->next;\n        } else {\n            object *unreached = obj;\n            size_t size=object_size(obj);\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n            value xsize;\n            if (dictionary_get(&sizecheck, MORPHO_OBJECT(unreached), &xsize)) {\n                size_t isize = MORPHO_GETINTEGERVALUE(xsize);\n                if (size!=isize) {\n                    morpho_printvalue(v, MORPHO_OBJECT(unreached));\n                    UNREACHABLE(\"Object doesn't match its declared size\");\n                }\n            }\n#endif\n\n            v->bound-=size;\n\n            /* Delink */\n            obj=obj->next;\n            if (prev!=NULL) {\n                prev->next=obj;\n            } else {\n                v->objects=obj;\n            }\n\n#ifndef MORPHO_DEBUG_GCSIZETRACKING\n            object_free(unreached);\n#endif\n        }\n    }\n}\n\n/** Collects garbage */\nvoid vm_collectgarbage(vm *v) {\n#ifdef MORPHO_DEBUG_DISABLEGARBAGECOLLECTOR\n    return;\n#endif\n    vm *vc = (v!=NULL ? v : globalvm);\n    if (!vc) return;\n    \n    if (vc->parent) return; // Don't garbage collect in subkernels\n    \n#ifdef MORPHO_PROFILER\n    vc->status=VM_INGC;\n#endif\n\n    if (vc && vc->bound>0) {\n        size_t init=vc->bound;\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n        morpho_printf(v, \"--- begin garbage collection ---\\n\");\n#endif\n        vm_gcmarkroots(vc);\n        vm_gctrace(vc);\n        vm_gcsweep(vc);\n\n        if (vc->bound>init) {\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n            morpho_printf(v, \"GC collected %ld bytes (from %zu to %zu) next at %zu.\\n\", init-vc->bound, init, vc->bound, vc->bound*MORPHO_GCGROWTHFACTOR);\n            UNREACHABLE(\"VM bound object size < 0\");\n#else\n            // This catch has been put in to prevent the garbarge collector from completely seizing up.\n            vc->bound=vm_gcrecalculatesize(v);\n#endif\n        }\n\n        vc->nextgc=vc->bound*MORPHO_GCGROWTHFACTOR;\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n        morpho_printf(v, \"--- end garbage collection ---\\n\");\n        if (vc) morpho_printf(v, \"    collected %ld bytes (from %zu to %zu) next at %zu.\\n\", init-vc->bound, init, vc->bound, vc->nextgc);\n#endif\n    }\n    \n#ifdef MORPHO_PROFILER\n    vc->status=VM_RUNNING;\n#endif\n}\n"
  },
  {
    "path": "src/core/gc.h",
    "content": "/** @file gc.h\n *  @author T J Atherton\n *\n *  @brief Morpho garbage collector\n */\n\n#ifndef gc_h\n#define gc_h\n\n/* **********************************************************************\n* Interface\n* ********************************************************************** */\n\nvoid vm_graylistinit(graylist *g);\nvoid vm_graylistclear(graylist *g);\nvoid vm_graylistadd(graylist *g, object *obj);\n\nvoid vm_unbindobject(vm *v, value obj);\nvoid vm_freeobjects(vm *v);\nvoid vm_collectgarbage(vm *v);\n\n#endif /* vm_h */\n"
  },
  {
    "path": "src/core/opcodes.h",
    "content": "/** @file opcodes.h\n *  @author T J Atherton\n *\n *  @brief Morpho opcodes\n */\n\n#ifdef OPCODE\n\n/** No operation */\nOPCODE(NOP)\n\n/** Moves contents between registers */\nOPCODE(MOV)\n\n/** Moves a constant into a register */\nOPCODE(LCT)\n\n/** Add the contents of two registers */\nOPCODE(ADD)\n\n/** Subtract the contents of two registers */\nOPCODE(SUB)\n\n/** Multiply the contents of two registers */\nOPCODE(MUL)\n\n/** Divide the contents of two registers */\nOPCODE(DIV)\n\n/** Raise to a power */\nOPCODE(POW)\n\n/** Comparison test */\nOPCODE(EQ)\n\n/** Comparison test */\nOPCODE(NEQ)\n\n/** Comparison test */\nOPCODE(LT)\n\n/** Comparison test */\nOPCODE(LE)\n\n/** Logical NOT of a register */\nOPCODE(NOT)\n\n/** Branching */\nOPCODE(B)\n\n/** Branch if true */\nOPCODE(BIF)\n\n/** Branch if false */\nOPCODE(BIFF)\n\n/** Call */\nOPCODE(CALL)\n\n/** Invoke */\nOPCODE(INVOKE)\n\n/** Method call */\nOPCODE(METHOD)\n\n/** Return */\nOPCODE(RETURN)\n\n/** Create a closure */\nOPCODE(CLOSURE)\n\n/** Load upvalue */\nOPCODE(LUP)\n\n/** Store upvalue */\nOPCODE(SUP)\n\n/** Close upvalues */\nOPCODE(CLOSEUP)\n\n/** Load property */\nOPCODE(LPR)\n\n/** Store property */\nOPCODE(SPR)\n\n/** Load index */\nOPCODE(LIX)\n\n/** Load index list */\nOPCODE(LIXL)\n\n/** Store index */\nOPCODE(SIX)\n\n/** Load global */\nOPCODE(LGL)\n\n/** Store global */\nOPCODE(SGL)\n\n/** Push error handler */\nOPCODE(PUSHERR)\n\n/** Pop error handler */\nOPCODE(POPERR)\n\n/** Creates an array */\n//OPCODE(ARRAY)\n\n/** Converts a sequence of registers to strings if necessary and concatenates them */\nOPCODE(CAT)\n\n/** Print the cotents of a register */\nOPCODE(PRINT)\n\n/** Raise error */\n//OPCODE(RAISE)\n\n/** Breakpoint */\nOPCODE(BREAK)\n\n/** End program */\nOPCODE(END)\n\n#endif\n"
  },
  {
    "path": "src/core/vm.c",
    "content": "/** @file vm.c\n *  @author T J Atherton\n *\n *  @brief Morpho virtual machine\n */\n\n#include <stdarg.h>\n#include <time.h>\n#include \"vm.h\"\n#include \"gc.h\"\n#include \"compile.h\"\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"debug.h\"\n#include \"profile.h\"\n#include \"resources.h\"\n#include \"extensions.h\"\n\nvalue initselector = MORPHO_NIL;\nvalue indexselector = MORPHO_NIL;\nvalue setindexselector = MORPHO_NIL;\nvalue addselector = MORPHO_NIL;\nvalue addrselector = MORPHO_NIL;\nvalue subselector = MORPHO_NIL;\nvalue subrselector = MORPHO_NIL;\nvalue mulselector = MORPHO_NIL;\nvalue mulrselector = MORPHO_NIL;\nvalue divselector = MORPHO_NIL;\nvalue divrselector = MORPHO_NIL;\nvalue powselector = MORPHO_NIL;\nvalue powrselector = MORPHO_NIL;\nvalue printselector = MORPHO_NIL;\nvalue enumerateselector = MORPHO_NIL;\nvalue countselector = MORPHO_NIL;\nvalue cloneselector = MORPHO_NIL;\n\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\ndictionary sizecheck;\n#endif\n\n/* **********************************************************************\n* VM objects\n* ********************************************************************** */\n\nvm *globalvm=NULL;\n\n/** Initializes a virtual machine */\nstatic void vm_init(vm *v) {\n    globalvm=v;\n    v->current=NULL;\n    v->instructions=NULL;\n    v->objects=NULL;\n    v->openupvalues=NULL;\n    v->fp=NULL;\n    v->fpmax=&v->frame[MORPHO_CALLFRAMESTACKSIZE-1]; // Last valid value of v->fp\n    v->ehp=NULL;\n    v->bound=0;\n    v->nextgc=MORPHO_GCINITIAL;\n    v->debug=NULL;\n    vm_graylistinit(&v->gray);\n    varray_valueinit(&v->stack);\n    varray_valueinit(&v->tlvars);\n    varray_valueinit(&v->globals);\n    varray_valueinit(&v->retain);\n    varray_valueresize(&v->stack, MORPHO_STACKINITIALSIZE);\n    varray_charinit(&v->buffer);\n    error_init(&v->err);\n    v->errfp=NULL;\n#ifdef MORPHO_PROFILER\n    v->profiler=NULL;\n    v->status=VM_RUNNING;\n#endif\n    v->parent=NULL;\n    varray_vminit(&v->subkernels);\n    \n    v->printfn=NULL;\n    v->printref=NULL;\n    v->inputfn=NULL;\n    v->inputref=NULL;\n    v->warningfn=NULL;\n    v->warningref=NULL;\n    v->debuggerfn=NULL;\n    v->debuggerref=NULL;\n}\n\n/** Clears a virtual machine */\nstatic void vm_clear(vm *v) {\n    varray_valueclear(&v->stack);\n    varray_valueclear(&v->globals);\n    varray_valueclear(&v->tlvars);\n    varray_valueclear(&v->retain);\n    vm_graylistclear(&v->gray);\n    varray_charclear(&v->buffer);\n    vm_freeobjects(v);\n    \n    for (int i=0; i<v->subkernels.count; i++) {\n        vm *subkernel = v->subkernels.data[i];\n        subkernel->globals.capacity=0; // Globals duplicates the parent vm so ignore\n        subkernel->globals.data=NULL;\n        vm_clear(subkernel);\n        MORPHO_FREE(subkernel);\n    }\n    varray_vmclear(&v->subkernels);\n}\n\n/** Prepares a vm to run program p */\nbool vm_start(vm *v, program *p) {\n    /* Set the current program */\n    v->current=p;\n\n    /* Clear current error state */\n    error_clear(&v->err);\n    v->errfp=NULL;\n\n    /* Set up the callframe stack */\n    v->fp=v->frame; /* Set the frame pointer to the bottom of the stack */\n    v->fp->function=p->global;\n    v->fp->closure=NULL;\n    v->fp->roffset=0;\n    v->fp->nopt=0;\n    \n#ifdef MORPHO_PROFILER\n    v->fp->inbuiltinfunction=NULL;\n#endif\n    \n    /* Set instruction base */\n    v->instructions = p->code.data;\n    if (!v->instructions) return false;\n\n    /* Set up the constant table */\n    varray_value *konsttable=object_functiongetconstanttable(p->global);\n    if (!konsttable) return false;\n    v->konst = konsttable->data;\n    \n    return true;\n}\n\n/** Frees all objects bound to a virtual machine */\nvoid vm_freeobjects(vm *v) {\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    long k = 0;\n    morpho_printf(v, \"--- Freeing objects bound to VM ---\\n\");\n#endif\n    object *next=NULL;\n    for (object *e=v->objects; e!=NULL; e=next) {\n        next = e->next;\n        object_free(e);\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n        k++;\n#endif\n    }\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    morpho_printf(v, \"--- Freed %li objects bound to VM ---\\n\", k);\n#endif\n}\n\n/* **********************************************************************\n* Binding and unbinding objects to the VM\n* ********************************************************************** */\n\n/** Unbinds an object from a VM. */\nvoid vm_unbindobject(vm *v, value obj) {\n    object *ob=MORPHO_GETOBJECT(obj);\n    \n    if (v->objects==ob) {\n        v->objects=ob->next;\n    } else {\n        for (object *e=v->objects; e!=NULL; e=e->next) {\n            if (e->next==ob) { e->next=ob->next; break; }\n        }\n    }\n    // Correct estimate of bound size.\n    if (MORPHO_ISGARBAGECOLLECTED(obj)) {\n        v->bound-=object_size(ob);\n        ob->status=OBJECT_ISUNMANAGED;\n    }\n}\n\n/** @brief Binds an object to a Virtual Machine.\n *  @details Any object created during execution should be bound to a VM; this object is then managed by the garbage collector.\n *  @param v      the virtual machine\n *  @param obj    object to bind */\nstatic void vm_bindobject(vm *v, value obj) {\n    object *ob = MORPHO_GETOBJECT(obj);\n    ob->status=OBJECT_ISUNMARKED;\n    ob->next=v->objects;\n    v->objects=ob;\n    size_t size=object_size(ob);\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n    dictionary_insert(&sizecheck, obj, MORPHO_INTEGER(size));\n#endif\n\n    v->bound+=size;\n\n#ifdef MORPHO_DEBUG_STRESSGARBAGECOLLECTOR\n    vm_collectgarbage(v);\n#else\n    if (v->bound>v->nextgc) vm_collectgarbage(v);\n#endif\n}\n\n/** @brief Binds an object to a Virtual Machine without garbage collection.\n *  @details Any object created during execution should be bound to a VM; this object is then managed by the garbage collector.\n *  @param v      the virtual machine\n *  @param obj    object to bind\n *  @warning: This should only be used in circumstances where the internal state of the VM is not consistent (i.e. calling the GC could cause a sigsev) */\nstatic void vm_bindobjectwithoutcollect(vm *v, value obj) {\n    object *ob = MORPHO_GETOBJECT(obj);\n    ob->status=OBJECT_ISUNMARKED;\n    ob->next=v->objects;\n    v->objects=ob;\n    size_t size=object_size(ob);\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n    dictionary_insert(&sizecheck, obj, MORPHO_INTEGER(size));\n#endif\n\n    v->bound+=size;\n}\n\n/* **********************************************************************\n* Virtual machine\n* ********************************************************************** */\n\n/** @brief Raises a runtime error\n * @param v        the virtual machine\n * @param id       error id\n * @param ...      additional data for sprintf. */\nvoid vm_runtimeerror(vm *v, ptrdiff_t iindx, errorid id, ...) {\n    va_list args;\n    int line=ERROR_POSNUNIDENTIFIABLE, posn=ERROR_POSNUNIDENTIFIABLE;\n    value module=MORPHO_NIL;\n    debug_infofromindx(v->current, iindx, &module, &line, &posn, NULL, NULL);\n    \n    va_start(args, id);\n    morpho_writeerrorwithidvalist(&v->err, id, NULL, line, posn, args);\n    va_end(args);\n}\n\n/** @brief Raises a BadOp error\n * @param v        the virtual machine\n * @param id       error id\n * @param op       the opertion that went bad in (human readable)\n * @param left     the left hand side of the bad operation\n * @param right    the right hand side of the bad operation */\nvoid vm_throwOpError(vm *v, ptrdiff_t iindx, errorid id, char* op, value left, value right){\n    varray_char left_buffer;\n    varray_char right_buffer;\n    varray_charinit(&left_buffer);\n    varray_charinit(&right_buffer);\n    morpho_printtobuffer(v, left, &left_buffer);\n    morpho_printtobuffer(v, right, &right_buffer);\n    varray_charresize(&left_buffer,left_buffer.count);\n    varray_charresize(&right_buffer,right_buffer.count);\n\n    // ensure the the rest of the alocated data is empty\n    for (int i = left_buffer.count; i<left_buffer.capacity; i++ ){\n        varray_charwrite(&left_buffer,'\\0');\n    }\n\n    for (int i = right_buffer.count; i<right_buffer.capacity; i++ ){\n        varray_charwrite(&right_buffer,'\\0');\n    }\n\n    vm_runtimeerror(v,iindx,id,op,left_buffer.data,right_buffer.data);\n    varray_charclear(&left_buffer);\n    varray_charclear(&right_buffer);\n    }\n\n\n/** @brief Captures an upvalue\n *  @param v        the virtual machine\n *  @param reg      register to capture\n *  @returns an objectupvalue */\nstatic inline objectupvalue *vm_captureupvalue(vm *v, value *reg) {\n    objectupvalue *prev = NULL;\n    objectupvalue *up = v->openupvalues;\n    objectupvalue *new = NULL;\n\n    /* Is there an existing open upvalue that points to the same location? */\n    for (;up!=NULL && up->location>reg;up=up->next) {\n        prev=up;\n    }\n\n    /* If so, return it */\n    if (up != NULL && up->location==reg) return up;\n\n    /* If not create a new one */\n    new=object_newupvalue(reg);\n\n    if (new) {\n        /* And link it into the list */\n        new->next=up;\n        if (prev) {\n            prev->next=new;\n        } else {\n            v->openupvalues=new;\n        }\n        vm_bindobject(v, MORPHO_OBJECT(new));\n    }\n\n    return new;\n}\n\n/** @brief Closes upvalues that refer beyond a specified register\n *  @param v        the virtual machine\n *  @param reg      register to capture */\nstatic inline void vm_closeupvalues(vm *v, value *reg) {\n    while (v->openupvalues!=NULL && v->openupvalues->location>=reg) {\n        objectupvalue *up = v->openupvalues;\n\n        up->closed=*up->location; /* Store closed value */\n        up->location=&up->closed; /* Point to closed value */\n        v->openupvalues=up->next; /* Delink from openupvalues list */\n        up->next=NULL;\n    }\n}\n\n/** Check if we should break at a given pc */\nbool vm_shouldbreakatpc(vm *v, instruction *pc) {\n    if (!v->debug) return false;\n    if (debugger_insinglestep(v->debug)) return true;\n    instructionindx iindx = pc-v->current->code.data-1;\n    if (debugger_shouldbreakat(v->debug, iindx)) return true;\n    return false;\n}\n\n/** Return the previous instruction index */\ninstructionindx vm_previnstruction(vm *v) {\n    if (v->fp->pc>v->current->code.data) return v->fp->pc-v->current->code.data-1;\n    return 0;\n}\n\n/** Return the current instruction index */\ninstructionindx vm_currentinstruction(vm *v) {\n    return v->fp->pc-v->current->code.data-1;\n}\n\n/** Return the current debugger */\ndebugger *vm_getdebugger(vm *v) {\n    return v->debug;\n}\n\n/** @brief Expands the stack by a specified amount\n *  @param v        the virtual machine\n *  @param reg      the current register base\n *  @param n        Number of stack spaces to expand by */\nstatic inline void vm_expandstack(vm *v, value **reg, unsigned int n) {\n    if (v->stack.count+n>v->stack.capacity) {\n        /* Calculate new size */\n        unsigned int newsize=MORPHO_STACKGROWTHFACTOR*v->stack.capacity;\n        if (newsize<morpho_powerof2ceiling(n)) newsize=morpho_powerof2ceiling(n);\n\n        /* Preserve the offset of the old stack pointer into the stack */\n        ptrdiff_t roffset=*reg-v->stack.data;\n\n        varray_ptrdiff diff;\n        varray_ptrdiffinit(&diff);\n\n        /* Preserve open upvalue offsets */\n        for (objectupvalue *u=v->openupvalues; u!=NULL; u=u->next) {\n            ptrdiff_t p=u->location-v->stack.data;\n            varray_ptrdiffadd(&diff, &p, 1);\n        }\n\n        /* Resize the stack */\n        varray_valueresize(&v->stack, newsize);\n\n        /* Recalculate upvalues */\n        unsigned int k=0;\n        for (objectupvalue *u=v->openupvalues; u!=NULL; u=u->next) {\n            u->location=v->stack.data+diff.data[k];\n            k++;\n        }\n\n        /* Free our varray of ptrdiffs */\n        varray_ptrdiffclear(&diff);\n\n        /* Correct the stack pointer */\n        *reg = v->stack.data+roffset;\n    }\n    v->stack.count+=n;\n}\n\n/** Process optional args */\nbool vm_optargs(vm *v, ptrdiff_t iindx, objectfunction *func, unsigned int nopt, value *args, value *outreg) {\n    unsigned int nfopt = func->opt.count;\n    \n    // Copy across default values\n    for (unsigned int i=0; i<nfopt; i++) {\n        outreg[i]=func->konst.data[func->opt.data[i].def];\n    }\n\n    for (unsigned int i=0; i<nopt; i++) {\n        value key = args[2*i];\n        // TODO: Is a dictionary lookup faster here?\n        unsigned int k=0;\n        for (; k<func->nopt; k++) if (MORPHO_ISSAME(func->opt.data[k].symbol, key)) break;\n        if (k>=func->nopt) { // If we didn't find a match, we're done with optional arguments\n            if (MORPHO_ISSTRING(key)) {\n                vm_runtimeerror(v, iindx, VM_UNKNWNOPTARG, MORPHO_GETCSTRING(key));\n                return false;\n            }\n            break;\n        }\n        outreg[k]=args[2*i+1];\n    }\n    return true;\n}\n\n/** Process variadic and optional arguments\n * @param[in] v          - the VM\n * @param[in] iindx - instruction index (used to raise errors if need be)\n * @param[in] func    - function being called\n * @param[in] regcall - Register for the call\n * @param[in] nargs  - number of arguments being called with\n * @param[in] args - arguments used\n * @param[in] reg       - register base\n * @param[in] newreg - new register base\n */\nstatic inline bool vm_vargs(vm *v, ptrdiff_t iindx, objectfunction *func, unsigned int nargs, value *args, value *newreg) {\n    int nfixed = func->nargs, // No. of fixed params\n        rVarg = nfixed+1, // Position of first optional parameter in output\n        nposn=nargs; // No. of positional arguments this function was called with\n\n    if (func->varg>=0) {\n        if (nposn<nfixed) {\n            vm_runtimeerror(v, iindx, VM_INVALIDARGS, nfixed-1, nposn);\n            return false;\n        }\n\n        objectlist *new = object_newlist(nposn-nfixed, args+nfixed);\n        if (new) {\n            newreg[rVarg] = MORPHO_OBJECT(new);\n            vm_bindobjectwithoutcollect(v, newreg[rVarg]);\n        }\n    } else if (nposn!=nfixed) { // Verify number of fixed args is correct\n        vm_runtimeerror(v, iindx, VM_INVALIDARGS, nfixed, nposn);\n        return false;\n    }\n\n    return true;\n}\n\n\n/** @brief Performs a function call\n *  @details A function call involves:\n *           1. Saving the program counter, register index and stacksize to the callframe stack;\n *           2. Advancing the frame pointer;\n *           3. Extracting the function from a closure if necessary;\n *           4. Expanding the stack if necessary\n *             Copy arguments to appropriate registers\n *           5. Loading the constant table from the function definition\n *           6. Shifting the register base\n *           7. Moving the program counter to the function\n * @param[in]  v                         The virtual machine\n * @param[in]  fn                       Function to call\n * @param[in]  regcall            rshift becomes r0 in the new call frame\n * @param[in]  nargs                number of positional arguments\n * @param[in]  nopt                  number of optional arguments\n * @param[in]  args                  pointer to the list of args\n * @param[out] pc                       program counter, updated\n * @param[out] reg                     register/stack pointer, updated */\nstatic inline bool vm_call(vm *v, value fn, unsigned int regcall, unsigned int nargs, unsigned int nopt, value *args, instruction **pc, value **reg) {\n    objectfunction *func = MORPHO_GETFUNCTION(fn);\n    bool argsonstack=true;\n    ptrdiff_t aoffset=0;\n\n    value *arglist=args;\n    if (arglist) {\n        /** Determine whether the arguments provided are on the stack or not */\n        argsonstack=(v->stack.data && arglist>v->stack.data && arglist<v->stack.data+v->stack.capacity);\n    } else { /** Otherwise use the registers after regcall */\n        arglist=(*reg)+regcall+1;\n    }\n    \n    if (argsonstack) aoffset=arglist-v->stack.data; /** If args on stack retain where they are */\n    \n    /* In the old frame... */\n    v->fp->pc=*pc; /* Save the program counter */\n    v->fp->stackcount=v->fp->function->nregs+(unsigned int) v->fp->roffset; /* Store the stacksize */\n    v->fp->returnreg=regcall; /* Store the return register */\n    unsigned int oldnregs = v->fp->function->nregs; /* Get the old number of registers */\n\n    if (v->fp==v->fpmax) { // Detect stack overflow\n        vm_runtimeerror(v, (*pc) - v->instructions, VM_STCKOVFLW);\n        return false;\n    }\n    v->fp++; /* Advance frame pointer */\n    v->fp->pc=*pc; /* We will also store the program counter in the new frame;\n                      this will be used to detect whether the VM should return on OP_RETURN */\n#ifdef MORPHO_PROFILER\n    v->fp->inbuiltinfunction=NULL;\n#endif\n\n    if (MORPHO_ISCLOSURE(fn)) {\n        objectclosure *closure=MORPHO_GETCLOSURE(fn); /* Closure object in use */\n        func=closure->func;\n        v->fp->closure=closure;\n    } else {\n        v->fp->closure=NULL;\n    }\n\n    v->fp->ret=false; /* Interpreter should not return from this frame */\n    v->fp->function=func; /* Store the function */\n\n    /* Do we need to expand the stack? */\n    if (v->stack.count+func->nregs>v->stack.capacity) {\n        vm_expandstack(v, reg, func->nregs); /* Expand the stack */\n    } else {\n        v->stack.count+=func->nregs;\n    }\n\n    v->konst = func->konst.data; /* Load the constant table */\n    *reg += oldnregs; /* Shift the register frame */\n    v->fp->roffset=*reg-v->stack.data; /* Store the register index */\n    \n    /* Copy arguments into new register window */\n    if (argsonstack) {\n        arglist = v->stack.data+aoffset; // If they were on the stack, the pointer may be invalid so update it.\n        (*reg)[0] = arglist[-1]; // Copy the caller into r0\n    } else {\n        (*reg)[0] = fn;\n    }\n    \n    for (unsigned int i=0; i<nargs; i++) (*reg)[i+1] = arglist[i];\n\n    int nvarg=0;\n    if (func->varg>=0) {\n        if (!vm_vargs(v, (*pc) - v->instructions, func, nargs, arglist, *reg)) return false;\n        nvarg=1;\n    } else if (func->nargs!=nargs) {\n        vm_runtimeerror(v, (*pc) - v->instructions, VM_INVALIDARGS, func->nargs, nargs);\n        return false;\n    }\n    \n    /* Handle optional args */\n    if (func->opt.count>0) {\n        if (!vm_optargs(v, (*pc) - v->instructions, func, nopt, arglist+nargs, (*reg)+func->nargs+nvarg+1)) return false;\n    } else if (nopt>0) {\n        vm_runtimeerror(v, (*pc) - v->instructions, VM_NOOPTARG);\n        return false;\n    }\n\n    /* Zero out registers beyond args up to the top of the stack\n       This has to be fast: memset was too slow. Zero seems to be faster than MORPHO_NIL */\n    for (value *r = *reg + func->nregs-1; r > *reg + func->nargs + func->nopt + nvarg; r--) *r = MORPHO_INTEGER(0);\n\n    *pc=v->instructions+func->entry; /* Jump to the function */\n    return true;\n}\n\n/** Invokes a method on a given object by name */\nstatic inline bool vm_invoke(vm *v, value obj, value method, int nargs, value *args, value *out) {\n    if (MORPHO_ISINSTANCE(obj)) {\n        /* Look up the method */\n        objectinstance *instance=MORPHO_GETINSTANCE(obj);\n        value fn=MORPHO_NIL;\n        if (dictionary_getintern(&instance->klass->methods, method, &fn)) {\n            return morpho_invoke(v, obj, fn, nargs, args, out);\n        }\n    } else if (MORPHO_ISCLASS(obj)) {\n        objectclass *klass=MORPHO_GETCLASS(obj);\n        value fn=MORPHO_NIL;\n        if (dictionary_getintern(&klass->methods, method, &fn)) {\n            return morpho_invoke(v, obj, fn, nargs, args, out);\n        }\n    } else if (MORPHO_ISOBJECT(obj)) {\n        /* If it's an object, it may have a veneer class */\n        objectclass *klass = object_getveneerclass(MORPHO_GETOBJECTTYPE(obj));\n        if (klass) {\n            value ifunc;\n            if (dictionary_getintern(&klass->methods, method, &ifunc)) {\n                if (MORPHO_ISBUILTINFUNCTION(ifunc)) {\n                    value sargs[nargs+1];\n                    sargs[0]=obj;\n                    for (unsigned int i=0; i<nargs; i++) sargs[i+1]=args[i];\n                    *out = (MORPHO_GETBUILTINFUNCTION(ifunc)->function) (v, nargs, sargs);\n                    return true;\n                }\n            }\n        }\n    }\n    return false;\n}\n\n/** Recovers the number of optional arguments */\nint vm_getoptionalargs(vm *v) {\n    return v->fp->nopt;\n}\n\n/** @brief   Executes a sequence of code\n *  @param   v       The virtual machine to use\n *  @param   rstart  Starting register pointer\n *  @param   istart  Instruction to begin at\n *  @returns A morpho error */\nbool morpho_interpret(vm *v, value *rstart, instructionindx istart) {\n    /* Set the register pointer to the bottom of the stack */\n    value *reg = rstart;\n\n    /* Set the program counter to start */\n    instruction *pc=v->instructions+istart; /* Pointer to the next instruction to be executed */\n\n    /* Temporary variables to */\n    int op=OP_NOP, a, b, c; /* Opcode and operands a, b, c */\n    instruction bc; /* The current bytecode */\n    value left, right;\n\n#ifdef MORPHO_DEBUG_PRINT_INSTRUCTIONS\n#define MORPHO_DISASSEMBLE_INSRUCTION(bc,pc,k,r) { morpho_printf(v, \"  \");  debugger_disassembleinstruction(v, bc, pc-1, k, r); morpho_printf(v, \"\\n\"); }\n#else\n#define MORPHO_DISASSEMBLE_INSRUCTION(bc,pc,k,r);\n#endif\n\n#ifdef MORPHO_OPCODE_USAGE\n    unsigned long opcount[OP_END+1];\n    unsigned long opopcount[OP_END+1][OP_END+1];\n    for (unsigned int i=0; i<OP_END+1; i++) {\n        opcount[i]=0;\n        for (unsigned int j=0; j<OP_END+1; j++) { opopcount[i][j]=0; }\n    }\n    #define OPCODECNT(p) { opcount[p]++; }\n    #define OPOPCODECNT(p, bc) { opopcount[op][DECODE_OP(bc)]++; }\n#else\n    #define OPCODECNT(p)\n    #define OPOPCODECNT(p, bc)\n#endif\n\n#define ENTERDEBUGGER() { \\\n    v->fp->pc=pc; \\\n    v->fp->roffset=reg-v->stack.data; \\\n    debugger_enter(v->debug, v); \\\n}\n\n/** Define the interpreter loop. Computed gotos or regular switch statements can be used here. */\n#ifdef MORPHO_COMPUTED_GOTO\n    /** The dispatch table, containing the entry points for each opcode */\n    static void* dispatchtable[] = {\n      #define OPCODE(name) &&code_##name,\n      #include \"opcodes.h\"\n      #undef OPCODE\n    };\n    \n    static void* debugdispatchtable[] = { // Backup copy of the dispatch table\n      #define OPCODE(name) &&code_##name,\n      #include \"opcodes.h\"\n      #undef OPCODE\n    };\n    \n    /** The interpret loop begins by dispatching an instruction */\n    #define INTERPRET_LOOP    DISPATCH();\n\n    /** Create a label corresponding to each opcode */\n    #define CASE_CODE(name)   code_##name\n    \n    int nopcodes;\n    for (nopcodes=0; dispatchtable[nopcodes]!=&&code_END; ) nopcodes++;\n    \n    #define DEBUG_ENABLE() { if (v->debug) for (int i=0; i<nopcodes; i++) dispatchtable[i]=&&code_BREAK; }\n    #define DEBUG_DISABLE() { if (v->debug) for (int i=0; i<nopcodes; i++) dispatchtable[i]=debugdispatchtable[i]; }\n    \n    /** Dispatch here means fetch the next instruction, decode and jump */\n    #define FETCHANDDECODE()                                                 \\\n        {                                                                    \\\n            bc=*pc++;                                                        \\\n            OPOPCODECNT(pp, bc)                                              \\\n            op=DECODE_OP(bc);                                                \\\n            OPCODECNT(op)                                                    \\\n            MORPHO_DISASSEMBLE_INSRUCTION(bc,pc-v->instructions,v->konst, reg)     \\\n        }\n    \n    #define DISPATCH()                                                       \\\n        do {                                                                 \\\n            FETCHANDDECODE()                                                 \\\n            goto *dispatchtable[op];                                         \\\n        } while(false);\n    \n#else\n    /** Every iteration of the interpret loop we fetch, decode and switch */\n    #define INTERPRET_LOOP                                                   \\\n        loop:                                                                \\\n        bc=*pc++;                                                            \\\n        OPOPCODECNT(pp, bc)                                                  \\\n        op=DECODE_OP(bc);                                                    \\\n        OPCODECNT(op)                                                        \\\n        MORPHO_DISASSEMBLE_INSRUCTION(bc,pc-v->instructions,v->konst, reg)   \\\n        if (vm_shouldbreakatpc(v, pc)) ENTERDEBUGGER();                   \\\n        switch (op)\n\n    /** Each opcode generates a case statement */\n    #define CASE_CODE(name)  case OP_##name\n\n    /** Dispatch means return to the beginning of the loop */\n    #define DISPATCH() goto loop;\n#endif\n\n/** Macros for error checking */\n#define ERROR(id) { vm_runtimeerror(v, pc-v->instructions, id); goto vm_error; }\n#define VERROR(id, ...) { vm_runtimeerror(v, pc-v->instructions, id, __VA_ARGS__); goto vm_error; }\n#define OPERROR(op){vm_throwOpError(v,pc-v->instructions,VM_INVLDOP,op,left,right); goto vm_error; }\n#define ERRORCHK() if (v->err.cat!=ERROR_NONE) goto vm_error;\n    \n/** Macro to redirect an opcode to a method call on an object */\n#define OPREDIRECT(leftselector, rightselector, regout) \\\n    if (MORPHO_ISOBJECT(left)) { \\\n        if (vm_invoke(v, left, leftselector, 1, &right, &reg[regout])) { \\\n            ERRORCHK(); \\\n            if (!MORPHO_ISNIL(reg[a])) DISPATCH(); \\\n        } \\\n    } \\\n    if (MORPHO_ISOBJECT(right)) { \\\n        if (vm_invoke(v, right, rightselector, 1, &left, &reg[regout])) { \\\n            ERRORCHK(); \\\n            DISPATCH(); \\\n        } \\\n    }\n    \n    INTERPRET_LOOP\n    {\n        CASE_CODE(NOP):\n            DISPATCH();\n\n        CASE_CODE(MOV):\n            a=DECODE_A(bc); b=DECODE_B(bc);\n            reg[a] = reg[b];\n            DISPATCH();\n\n        CASE_CODE(LCT):\n            a=DECODE_A(bc); b=DECODE_Bx(bc);\n            reg[a] = v->konst[b];\n            DISPATCH();\n\n        CASE_CODE(ADD):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if (MORPHO_ISFLOAT(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) + MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) + (double) MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            } else if (MORPHO_ISINTEGER(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( (double) MORPHO_GETINTEGERVALUE(left) + MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_INTEGER( MORPHO_GETINTEGERVALUE(left) + MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            } else if (MORPHO_ISSTRING(left) && MORPHO_ISSTRING(right)) {\n                reg[a] = object_concatenatestring(left, right);\n                if (!MORPHO_ISNIL(reg[a])) {\n                    vm_bindobject(v, reg[a]);\n                    DISPATCH();\n                } else {\n                    ERROR(VM_CNCTFLD);\n                    DISPATCH();\n                }\n            }\n\n            OPREDIRECT(addselector, addrselector, a);\n            \n            OPERROR(\"Add\");\n            DISPATCH();\n\n        CASE_CODE(SUB):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if (MORPHO_ISFLOAT(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) - MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) - (double) MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            } else if (MORPHO_ISINTEGER(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( (double) MORPHO_GETINTEGERVALUE(left) - MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_INTEGER( MORPHO_GETINTEGERVALUE(left) - MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            }\n        \n            OPREDIRECT(subselector, subrselector, a);\n\n            OPERROR(\"Subtract\")\n            DISPATCH();\n\n        CASE_CODE(MUL):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if (MORPHO_ISFLOAT(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) * MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) * (double) MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            } else if (MORPHO_ISINTEGER(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( (double) MORPHO_GETINTEGERVALUE(left) * MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_INTEGER( MORPHO_GETINTEGERVALUE(left) * MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            }\n\n            OPREDIRECT(mulselector, mulrselector, a);\n\n            OPERROR(\"Multiply\")\n            DISPATCH();\n\n        CASE_CODE(DIV):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if (MORPHO_ISFLOAT(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) / MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( MORPHO_GETFLOATVALUE(left) / (double) MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            } else if (MORPHO_ISINTEGER(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( (double) MORPHO_GETINTEGERVALUE(left) / MORPHO_GETFLOATVALUE(right));\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( (double) MORPHO_GETINTEGERVALUE(left) / (double) MORPHO_GETINTEGERVALUE(right));\n                    DISPATCH();\n                }\n            }\n        \n            OPREDIRECT(divselector, divrselector, a);\n\n            OPERROR(\"Divide\");\n            DISPATCH();\n\n        CASE_CODE(POW):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if (MORPHO_ISFLOAT(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( pow(MORPHO_GETFLOATVALUE(left), MORPHO_GETFLOATVALUE(right)) );\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( pow(MORPHO_GETFLOATVALUE(left), (double) MORPHO_GETINTEGERVALUE(right)) );\n                    DISPATCH();\n                }\n            } else if (MORPHO_ISINTEGER(left)) {\n                if (MORPHO_ISFLOAT(right)) {\n                    reg[a] = MORPHO_FLOAT( pow((double) MORPHO_GETINTEGERVALUE(left), MORPHO_GETFLOATVALUE(right)) );\n                    DISPATCH();\n                } else if (MORPHO_ISINTEGER(right)) {\n                    reg[a] = MORPHO_FLOAT( pow((double) MORPHO_GETINTEGERVALUE(left), (double) MORPHO_GETINTEGERVALUE(right)) );\n                    DISPATCH();\n                }\n            }\n\n            OPREDIRECT(powselector, powrselector, a);\n\n            OPERROR(\"Exponentiate\")\n            DISPATCH();\n\n\n        CASE_CODE(NOT):\n            a=DECODE_A(bc); b=DECODE_B(bc);\n            left = reg[b];\n\n            if (MORPHO_ISBOOL(left)) {\n                reg[a] = MORPHO_BOOL(!MORPHO_GETBOOLVALUE(left));\n            } else {\n                reg[a] = MORPHO_BOOL(MORPHO_ISNIL(left));\n            }\n            DISPATCH();\n\n        CASE_CODE(EQ):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            reg[a] = (morpho_extendedcomparevalue(left, right)==0 ? MORPHO_BOOL(true) : MORPHO_BOOL(false));\n            DISPATCH();\n\n        CASE_CODE(NEQ):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            reg[a] = (morpho_extendedcomparevalue(left, right)!=0 ? MORPHO_BOOL(true) : MORPHO_BOOL(false));\n            DISPATCH();\n\n        CASE_CODE(LT):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if ( !( (MORPHO_ISFLOAT(left) || MORPHO_ISINTEGER(left)) &&\n                   (MORPHO_ISFLOAT(right) || MORPHO_ISINTEGER(right)) ) ) {\n                OPERROR(\"Compare\");\n            }\n\n            reg[a] = (morpho_extendedcomparevalue(left, right)>0 ? MORPHO_BOOL(true) : MORPHO_BOOL(false));\n            DISPATCH();\n\n        CASE_CODE(LE):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if ( !( (MORPHO_ISFLOAT(left) || MORPHO_ISINTEGER(left)) &&\n                   (MORPHO_ISFLOAT(right) || MORPHO_ISINTEGER(right)) ) ) {\n                OPERROR(\"Compare\");\n            }\n\n            reg[a] = (morpho_extendedcomparevalue(left, right)>=0 ? MORPHO_BOOL(true) : MORPHO_BOOL(false));\n            DISPATCH();\n\n        CASE_CODE(B):\n            b=DECODE_sBx(bc);\n            pc+=b;\n            DISPATCH();\n\n        CASE_CODE(BIF):\n            a=DECODE_A(bc);\n            left=reg[a];\n\n            if (MORPHO_ISTRUE(left)) pc+=DECODE_sBx(bc);\n            DISPATCH();\n\n        CASE_CODE(BIFF):\n            a=DECODE_A(bc);\n            left=reg[a];\n\n            if (MORPHO_ISFALSE(left)) pc+=DECODE_sBx(bc);\n            DISPATCH();\n\n        CASE_CODE(CALL):\n            a=DECODE_A(bc);\n            left=reg[a];\n            b=DECODE_B(bc); // Number of positional arguments\n            c=DECODE_C(bc); // Number of optional arguments\n        \ncallfunction: // Jump here if an instruction becomes a call\n            if (MORPHO_ISINVOCATION(left)) {\n                /* An method invocation */\n                objectinvocation *inv = MORPHO_GETINVOCATION(left);\n                left=inv->method;\n                reg[a]=inv->receiver;\n            }\n        \n            if (MORPHO_ISMETAFUNCTION(left) &&\n                !metafunction_resolve(MORPHO_GETMETAFUNCTION(left), b, reg+a+1,                 &v->err, &left)) {\n                ERRORCHK();\n                ERROR(VM_MLTPLDSPTCHFLD);\n            }\n\n            if (MORPHO_ISFUNCTION(left) || MORPHO_ISCLOSURE(left)) {\n                if (!vm_call(v, left, a, b, c, NULL, &pc, &reg)) goto vm_error;\n\n            } else if (MORPHO_ISBUILTINFUNCTION(left)) {\n                /* Save program counter in the old callframe */\n                v->fp->pc=pc;\n                v->fp->nopt=c;\n\n                objectbuiltinfunction *f = MORPHO_GETBUILTINFUNCTION(left);\n\n#ifdef MORPHO_PROFILER\n                v->fp->inbuiltinfunction=f;\n#endif\n                value ret = (f->function) (v, b, reg+a);\n#ifdef MORPHO_PROFILER\n                v->fp->inbuiltinfunction=NULL;\n#endif\n                ERRORCHK();\n                reg=v->stack.data+v->fp->roffset; /* Ensure register pointer is correct */\n                reg[a]=ret;\n\n            } else if (MORPHO_ISCLASS(left)) {\n                /* A function call on a class instantiates it */\n                objectclass *klass = MORPHO_GETCLASS(left);\n                objectinstance *instance = object_newinstance(klass);\n                if (instance) {\n                    reg[a] = MORPHO_OBJECT(instance);\n                    vm_bindobject(v, reg[a]);\n\n                    /* Call the initializer if class provides one */\n                    value ifunc;\n                    if (dictionary_getintern(&klass->methods, initselector, &ifunc)) {\n                        if (MORPHO_ISMETAFUNCTION(ifunc) &&\n                            !metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), b, reg+a+1, &v->err, &ifunc)) {\n                            ERRORCHK();\n                            ERROR(VM_MLTPLDSPTCHFLD);\n                        }\n                        \n                        /* If so, call it */\n                        if (MORPHO_ISFUNCTION(ifunc)) {\n                            if (!vm_call(v, ifunc, a, b, c, NULL, &pc, &reg)) goto vm_error;\n                        } else if (MORPHO_ISBUILTINFUNCTION(ifunc)) {\n                            v->fp->nopt=c;\n#ifdef MORPHO_PROFILER\n                            v->fp->inbuiltinfunction=MORPHO_GETBUILTINFUNCTION(ifunc);\n#endif\n                            (MORPHO_GETBUILTINFUNCTION(ifunc)->function) (v, b, reg+a);\n#ifdef MORPHO_PROFILER\n                            v->fp->inbuiltinfunction=NULL;\n#endif\n                            ERRORCHK();\n                        }\n                    } else {\n                        if (b>0) {\n                            VERROR(VM_NOINITIALIZER, MORPHO_GETCSTRING(klass->name));\n                        }\n                    }\n                } else {\n                    ERROR(VM_INSTANTIATEFAILED);\n                }\n            } else {\n                ERROR(VM_UNCALLABLE);\n            }\n            DISPATCH();\n        \n        CASE_CODE(METHOD): // Direct method call\n            a=DECODE_A(bc);\n            b=DECODE_B(bc);\n            c=DECODE_C(bc);\n        \n            right=reg[a]; // The method\n            left=reg[a+1]; // The object\n        \n            if (MORPHO_ISMETAFUNCTION(right)) {\n                if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(right), b, reg+a+2, &v->err, &right)) {\n                    ERRORCHK();\n                    ERROR(VM_MLTPLDSPTCHFLD);\n                }\n            }\n        \n            if (MORPHO_ISFUNCTION(right)) {\n                if (!vm_call(v, right, a+1, b, c, NULL, &pc, &reg)) goto vm_error;\n            } else if (MORPHO_ISBUILTINFUNCTION(right)) {\n                v->fp->nopt=c;\n    #ifdef MORPHO_PROFILER\n                v->fp->inbuiltinfunction=MORPHO_GETBUILTINFUNCTION(right);\n    #endif\n                value ret = (MORPHO_GETBUILTINFUNCTION(right)->function) (v, b, reg+a+1);\n                reg=v->fp->roffset+v->stack.data; /* Restore registers */\n                reg[a+1] = ret;\n    #ifdef MORPHO_PROFILER\n                v->fp->inbuiltinfunction=NULL;\n    #endif\n                ERRORCHK();\n            }\n        \n        DISPATCH();\n\n        CASE_CODE(INVOKE):\n            a=DECODE_A(bc);\n            b=DECODE_B(bc);\n            c=DECODE_C(bc);\n            right=reg[a]; // The method\n            left=reg[a+1]; // The object\n\n            if (MORPHO_ISINSTANCE(left)) {\n                objectinstance *instance = MORPHO_GETINSTANCE(left);\n                value ifunc;\n\n                /* Check if we have this method */\n                if (dictionary_getintern(&instance->klass->methods, right, &ifunc)) {\n\n                    if (MORPHO_ISMETAFUNCTION(ifunc)) {\n                        if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), b, reg+a+2, &v->err, &ifunc)) {\n                            ERRORCHK();\n                            ERROR(VM_MLTPLDSPTCHFLD);\n                        }\n                    }\n                    \n                    /* If so, call it */\n                    if (MORPHO_ISFUNCTION(ifunc)) {\n                        if (!vm_call(v, ifunc, a+1, b, c, NULL, &pc, &reg)) goto vm_error;\n                    } else if (MORPHO_ISBUILTINFUNCTION(ifunc)) {\n                        v->fp->nopt=c;\n#ifdef MORPHO_PROFILER\n                        v->fp->inbuiltinfunction=MORPHO_GETBUILTINFUNCTION(ifunc);\n#endif\n                        value ret = (MORPHO_GETBUILTINFUNCTION(ifunc)->function) (v, b, reg+a+1);\n                        reg=v->fp->roffset+v->stack.data; /* Restore registers */\n                        reg[a+1] = ret;\n#ifdef MORPHO_PROFILER\n                        v->fp->inbuiltinfunction=NULL;\n#endif\n                        ERRORCHK();\n                    }\n                } else if (dictionary_getintern(&instance->fields, right, &left)) {\n                    \n                    /* Otherwise, if it's a property, try to call it */\n                    if (morpho_iscallable(left)) {\n                        a+=1;\n                        reg[a]=left; // Make sure the function is in r0\n                        goto callfunction; // Transmute into a call instruction\n                    } else {\n                        ERROR(VM_UNCALLABLE);\n                    }\n                } else {\n                    /* Otherwise, raise an error */\n                    char *p = (MORPHO_ISSTRING(right) ? MORPHO_GETCSTRING(right) : \"\");\n                    VERROR(VM_OBJECTLACKSPROPERTY, p);\n                }\n            } else if (MORPHO_ISCLASS(left)) {\n                objectclass *klass = MORPHO_GETCLASS(left);\n                value ifunc;\n\n                if (dictionary_getintern(&klass->methods, right, &ifunc)) {\n                    /* If we're not in the global context, invoke the method on self which is in r0 */\n                    if (v->fp>v->frame) reg[a+1]=reg[0]; /* Copy self into r[a+1] and call */\n\n                    if (MORPHO_ISMETAFUNCTION(ifunc)) {\n                        if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), b, reg+a+2, &v->err, &ifunc)) {\n                            ERRORCHK();\n                            ERROR(VM_MLTPLDSPTCHFLD);\n                        }\n                    }\n                    \n                    if (MORPHO_ISFUNCTION(ifunc)) {\n                        if (!vm_call(v, ifunc, a+1, b, c, NULL, &pc, &reg)) goto vm_error;\n                    } else if (MORPHO_ISBUILTINFUNCTION(ifunc)) {\n                        v->fp->nopt=c;\n#ifdef MORPHO_PROFILER\n                        v->fp->inbuiltinfunction=MORPHO_GETBUILTINFUNCTION(ifunc);\n#endif\n                        value ret = (MORPHO_GETBUILTINFUNCTION(ifunc)->function) (v, b, reg+a+1);\n                        reg=v->fp->roffset+v->stack.data; /* Restore registers */\n                        reg[a+1] = ret;\n#ifdef MORPHO_PROFILER\n                        v->fp->inbuiltinfunction=NULL;\n#endif\n                        ERRORCHK();\n                    }\n                } else {\n                    /* Otherwise, raise an error */\n                    char *p = (MORPHO_ISSTRING(right) ? MORPHO_GETCSTRING(right) : \"\");\n                    VERROR(VM_CLASSLACKSPROPERTY, p);\n                }\n            } else {\n                /* Check if the operand has a veneer class */\n                objectclass *klass;\n                if (MORPHO_ISOBJECT(left)) klass = object_getveneerclass(MORPHO_GETOBJECTTYPE(left));\n                else klass = value_getveneerclass(left);\n                \n                if (klass) {\n                    value ifunc;\n                    if (dictionary_getintern(&klass->methods, right, &ifunc)) {\n                        if (MORPHO_ISMETAFUNCTION(ifunc)) {\n                            if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), b,  reg+a+2, &v->err, &ifunc)) {\n                                ERRORCHK();\n                                ERROR(VM_MLTPLDSPTCHFLD);\n                            }\n                        }\n                        \n                        if (MORPHO_ISBUILTINFUNCTION(ifunc)) {\n                            v->fp->nopt=c;\n#ifdef MORPHO_PROFILER\n                            v->fp->inbuiltinfunction=MORPHO_GETBUILTINFUNCTION(ifunc);\n#endif\n                            value ret = (MORPHO_GETBUILTINFUNCTION(ifunc)->function) (v, b, reg+a+1);\n                            reg=v->fp->roffset+v->stack.data; /* Restore registers */\n                            reg[a+1] = ret;\n#ifdef MORPHO_PROFILER\n                            v->fp->inbuiltinfunction=NULL;\n#endif\n                            ERRORCHK();\n                        }\n                    } else {\n                        char *p = (MORPHO_ISSTRING(right) ? MORPHO_GETCSTRING(right) : \"\");\n                        VERROR(VM_CLASSLACKSPROPERTY, p);\n                    }\n                } else {\n                    ERROR(VM_NOTANINSTANCE);\n                }\n            }\n\n            DISPATCH();\n\n        CASE_CODE(RETURN):\n            a=DECODE_A(bc);\n\n            if (v->openupvalues) { /* Close upvalues */\n                vm_closeupvalues(v, reg);\n            }\n        \n            if (v->ehp) { /* Remove any error handlers from this call frame */\n                while (v->ehp->fp==v->fp &&\n                       v->ehp>=v->errorhandlers) v->ehp--;\n                if (v->ehp<v->errorhandlers) v->ehp=NULL; // If the stack is empty rest to NULL\n            }\n\n            value retvalue;\n\n            if (a>0) {\n                b=DECODE_B(bc);\n                retvalue = reg[b];\n            } else {\n                retvalue = MORPHO_NIL; /* No return value; returns nil */\n            }\n\n            if (v->fp>v->frame) {\n                bool shouldreturn = (v->fp->ret);\n                // value *or = reg + v->fp->function->nargs;\n                v->fp--;\n                v->konst=v->fp->function->konst.data; /* Restore the constant table */\n                reg=v->fp->roffset+v->stack.data; /* Restore registers */\n                v->stack.count=v->fp->stackcount; /* Restore the stack size */\n\n                reg[v->fp->returnreg]=retvalue; /* Copy the return value */\n                // Clear registers\n                // for (value *r = reg + v->fp->function->nregs-1; r > or; r--) *r = MORPHO_INTEGER(0);\n\n                pc=v->fp->pc; /* Jump back */\n                if (shouldreturn) return true;\n                DISPATCH();\n            } else {\n                ERROR(VM_GLBLRTRN);\n            }\n\n        CASE_CODE(CLOSURE):\n        {\n            a=DECODE_A(bc);\n            b=DECODE_B(bc);\n            objectclosure *closure = object_newclosure(v->fp->function, MORPHO_GETFUNCTION(reg[a]), (indx) b);\n            /* Now capture or copy upvalues from this frame */\n            if (closure) {\n                for (unsigned int i=0; i<closure->nupvalues; i++) {\n                    upvalue *up = &v->fp->function->prototype.data[b].data[i];\n                    if (up->islocal) {\n                        closure->upvalues[i]=vm_captureupvalue(v, &reg[up->reg]);\n                    } else {\n                        if (v->fp->closure) closure->upvalues[i]=v->fp->closure->upvalues[up->reg];\n                    }\n                }\n\n                reg[a] = MORPHO_OBJECT(closure);\n                vm_bindobject(v, MORPHO_OBJECT(closure));\n            }\n        }\n            DISPATCH();\n\n        CASE_CODE(LUP):\n            a=DECODE_A(bc);\n            b=DECODE_B(bc);\n            if (v->fp->closure && v->fp->closure->upvalues[b]) {\n                reg[a]=*v->fp->closure->upvalues[b]->location;\n            } else {\n                UNREACHABLE(\"Closure unavailable\");\n            }\n            DISPATCH();\n\n        CASE_CODE(SUP):\n            a=DECODE_A(bc);\n            b=DECODE_B(bc);\n            right = reg[b];\n            if (v->fp->closure && v->fp->closure->upvalues[a]) {\n                *v->fp->closure->upvalues[a]->location=right;\n            } else {\n                UNREACHABLE(\"Closure unavailable\");\n            }\n            DISPATCH();\n\n        CASE_CODE(LGL):\n            a=DECODE_A(bc);\n            b=DECODE_Bx(bc);\n            reg[a]=v->globals.data[b];\n\n            DISPATCH();\n\n        CASE_CODE(SGL):\n            a=DECODE_A(bc);\n            b=DECODE_Bx(bc);\n            v->globals.data[b]=reg[a];\n            DISPATCH();\n\n        CASE_CODE(CLOSEUP):\n            a=DECODE_A(bc);\n            vm_closeupvalues(v, &reg[a]);\n            DISPATCH();\n\n        CASE_CODE(LPR): /* Load property */\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[b];\n            right = reg[c];\n\n            if (MORPHO_ISINSTANCE(left)) {\n                objectinstance *instance = MORPHO_GETINSTANCE(left);\n                /* Is there a property with this id? */\n                if (dictionary_getintern(&instance->fields, right, &reg[a])) {\n                } else if (dictionary_getintern(&instance->klass->methods, right, &reg[a])) {\n                    /* ... or a method? */\n                    objectinvocation *bound=object_newinvocation(left, reg[a]);\n                    if (bound) {\n                        /* Bind into the VM */\n                        reg[a]=MORPHO_OBJECT(bound);\n                        vm_bindobject(v, reg[a]);\n                    }\n                } else if (dictionary_get(&instance->fields, right, &reg[a])) {\n                } else {\n                    /* Otherwise, raise an error */\n                    char *p = (MORPHO_ISSTRING(right) ? MORPHO_GETCSTRING(right) : \"\");\n                    VERROR(VM_OBJECTLACKSPROPERTY, p);\n                }\n            } else if (MORPHO_ISCLASS(left)) {\n                /* If it's a class, we lookup the method and create the invocation */\n                objectclass *klass = MORPHO_GETCLASS(left);\n                if (klass && dictionary_get(&klass->methods, right, &reg[a])) {\n                    objectinvocation *bound=object_newinvocation(left, reg[a]);\n                    if (bound) {\n                        /* Bind into the VM */\n                        reg[a]=MORPHO_OBJECT(bound);\n                        vm_bindobject(v, reg[a]);\n                    }\n                } else {\n                    /* Otherwise, raise an error */\n                    char *p = (MORPHO_ISSTRING(right) ? MORPHO_GETCSTRING(right) : \"\");\n                    VERROR(VM_CLASSLACKSPROPERTY, p);\n                }\n            } else {\n                /* Check for veneer class */\n                objectclass *klass;\n                if (MORPHO_ISOBJECT(left)) klass = object_getveneerclass(MORPHO_GETOBJECTTYPE(left));\n                else klass = value_getveneerclass(left);\n                \n                if (klass) {\n                    value ifunc;\n                    if (dictionary_get(&klass->methods, right, &ifunc)) {\n                        objectinvocation *bound=object_newinvocation(left, ifunc);\n                        if (bound) {\n                            /* Bind into the VM */\n                            reg[a]=MORPHO_OBJECT(bound);\n                            vm_bindobject(v, reg[a]);\n                        }\n                    } else {\n                        char *p = (MORPHO_ISSTRING(right) ? MORPHO_GETCSTRING(right) : \"\");\n                        VERROR(VM_CLASSLACKSPROPERTY, p);\n                    }\n                } else {\n                    ERROR(VM_NOTANOBJECT);\n                }\n            }\n            DISPATCH();\n\n        CASE_CODE(SPR):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[a];\n            right = reg[c];\n\n            if (MORPHO_ISINSTANCE(left)) {\n                objectinstance *instance = MORPHO_GETINSTANCE(left);\n                left = reg[b];\n                dictionary_insertintern(&instance->fields, left, right);\n            } else {\n                ERROR(VM_NOTANOBJECT);\n            }\n\n            DISPATCH();\n\n        CASE_CODE(LIX):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[a];\n\n            if (MORPHO_ISARRAY(left)) {\n                unsigned int ndim = c-b+1;\n                unsigned int indx[ndim];\n                if (array_valuelisttoindices(ndim, &reg[b], indx)){\n                    objectarrayerror err=array_getelement(MORPHO_GETARRAY(left), ndim, indx, &reg[b]);\n                    if (err!=ARRAY_OK) ERROR( array_error(err) );\n                } else {\n                    value newval = MORPHO_NIL;\n                    objectarrayerror err = getslice(&left,&array_slicedim,&array_sliceconstructor,\\\n                                                    &array_slicecopy,ndim,&reg[b],&newval);\n                    if (err!=ARRAY_OK) ERROR(array_error(err));\n\n                    if (!MORPHO_ISNIL(newval)) {\n                        reg[b] = newval;\n                        vm_bindobject(v, reg[b]);\n                    } else  ERROR(VM_NONNUMINDX);\n                }\n            } else {\n                if (!vm_invoke(v, left, indexselector, c-b+1, &reg[b], &reg[b])) {\n                    ERROR(VM_NOTINDEXABLE);\n                }\n                ERRORCHK();\n            }\n\n            DISPATCH();\n        \n        CASE_CODE(LIXL):\n        {\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            \n            /* Variant 1: */\n             value args[2] = { reg[b], reg[c] };\n             reg[a] = List_getindex(v, 1, args);\n             ERRORCHK();\n            \n            /* Variant 2: Potentially even faster \n            objectlist *list = MORPHO_GETLIST(reg[b]);\n            int i=MORPHO_GETINTEGERVALUE(reg[c]);\n            if (i>list->val.count) ERROR(VM_OUTOFBOUNDS);\n            reg[a]=list->val.data[i];*/\n            \n            DISPATCH();\n        }\n\n        CASE_CODE(SIX):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            left = reg[a];\n\n            if (MORPHO_ISARRAY(left)) {\n                unsigned int ndim = c-b;\n                unsigned int indx[ndim];\n                if (!array_valuelisttoindices(ndim, &reg[b], indx)) ERROR(VM_NONNUMINDX);\n                objectarrayerror err=array_setelement(MORPHO_GETARRAY(left), ndim, indx, reg[c]);\n                if (err!=ARRAY_OK) ERROR( array_error(err) );\n            } else {\n                if (!vm_invoke(v, left, setindexselector, c-b+1, &reg[b], &right)) {\n                    ERROR(VM_NOTINDEXABLE);\n                }\n                ERRORCHK();\n            }\n\n            DISPATCH();\n\n        CASE_CODE(PUSHERR):\n            b=DECODE_Bx(bc);\n            if (v->ehp && v->ehp>=v->errorhandlers+MORPHO_ERRORHANDLERSTACKSIZE-1) {\n                ERROR(VM_ERRSTCKOVFLW);\n            }\n            if (!v->ehp) v->ehp=v->errorhandlers; else v->ehp++; // Add new error handler to the error stack\n            v->ehp->fp=v->fp; // Store the current frame pointer\n            v->ehp->dict=v->konst[b]; // Store the error handler dictionary from the constant table\n            DISPATCH();\n\n        CASE_CODE(POPERR):\n            b=DECODE_sBx(bc); // Optional branch\n            pc+=b;            // Advance program counter\n            v->ehp--;         // Pull error handler off error stack\n            if (v->ehp<v->errorhandlers) v->ehp=NULL; // If the stack is empty rest to NULL\n            DISPATCH();\n\n        CASE_CODE(CAT):\n            a=DECODE_A(bc); b=DECODE_B(bc); c=DECODE_C(bc);\n            reg[a]=morpho_concatenate(v, c-b+1, reg+b);\n            vm_bindobject(v, reg[a]);\n            DISPATCH();\n\n        CASE_CODE(PRINT):\n            a=DECODE_A(bc);\n            left=reg[a];\n            if (!vm_invoke(v, left, printselector, 0, NULL, &right)) {\n                morpho_printvalue(v, left);\n            }\n            morpho_printf(v, \"\\n\");\n            DISPATCH();\n\n        CASE_CODE(BREAK):\n            if (v->debug) {\n                if (vm_shouldbreakatpc(v, pc) ||\n                    op==OP_BREAK) {\n                    ENTERDEBUGGER();\n                    ERRORCHK();\n                }\n                \n#ifdef MORPHO_COMPUTED_GOTO\n                // If using computed gotos, all instructions are routed through OP_BREAK\n                // when the debugger is active. When this is happening we must perform a regular\n                // dispatch after we've checked whether to enter the debugger\n                bool debugdispatchactive = (dispatchtable[0]==&&code_BREAK);\n                \n                if (debugger_isactive(v->debug)) { // Check if singlestep or breakpoints are active  \n                    if (!debugdispatchactive) DEBUG_ENABLE()\n                } else if (debugdispatchactive) DEBUG_DISABLE()\n                \n                if (op==OP_BREAK) DISPATCH(); // Perform a regular dispatch if we stopped at OP_BREAK\n                \n                // If the debug dispatch table was active, must dispatch to execute the instruction\n                if (debugdispatchactive) goto *debugdispatchtable[op];\n#endif\n            }\n            DISPATCH();\n\n        CASE_CODE(END):\n            #ifdef MORPHO_OPCODE_USAGE\n            {\n                char *opname[] = {\n                #define OPCODE(name) #name,\n                #include \"opcodes.h\"\n                    \"\"\n                };\n                #undef OPCODE\n                for (unsigned int i=0; i<OP_END; i++) {\n                    morpho_printf(v, \"%s:\\t\\t%lu\\n\", opname[i], opcount[i]);\n                }\n\n                morpho_printf(v, \",\");\n                for (unsigned int i=0; i<OP_END; i++) morpho_printf(v, \"%s, \", opname[i]);\n                morpho_printf(v, \"\\n\");\n\n                for (unsigned int i=0; i<OP_END; i++) {\n                    morpho_printf(v, \"%s, \", opname[i]);\n                    for (unsigned int j=0; j<OP_END; j++) {\n                        morpho_printf(v, \"%lu \", opopcount[i][j]);\n                        if (j<OP_END-1) morpho_printf(v, \",\");\n                    }\n                    morpho_printf(v, \"\\n\");\n                }\n            }\n            #endif\n            return true;\n    }\n\nvm_error:\n    {\n        objectstring erridstring=MORPHO_STATICSTRING(v->err.id);\n        value errid = MORPHO_OBJECT(&erridstring);\n\n        /* Find the most recent callframe that requires us to return */\n        callframe *retfp=NULL;\n        for (retfp=v->fp; retfp>v->frame && !retfp->ret; retfp--);\n\n        /* Search down the error stack for an error handler that can handle the error  */\n        for (errorhandler *eh=v->ehp; eh && eh>=v->errorhandlers; eh--) {\n            /* Abort if we pass an intermediate frame that requires us to return */\n            if (eh->fp<retfp) {\n                v->ehp=eh; // Pop off all earlier error handlers\n                break;\n            }\n\n            if (MORPHO_ISDICTIONARY(eh->dict)) {\n                value branchto = MORPHO_NIL;\n                objectdictionary *dict = MORPHO_GETDICTIONARY(eh->dict);\n                if (dictionary_get(&dict->dict, errid, &branchto)) {\n                    error_clear(&v->err);\n\n                    // Jump to the error handler\n                    v->fp=eh->fp;\n                    v->konst=v->fp->function->konst.data;\n                    pc=v->instructions+MORPHO_GETINTEGERVALUE(branchto);\n                    reg=v->stack.data+v->fp->roffset;\n\n                    if (v->openupvalues) { /* Close any upvalues */\n                        vm_closeupvalues(v, reg+v->fp->function->nregs);\n                    }\n\n                    v->ehp=eh-1; // Unwind the error handler stack\n                    if (v->ehp<v->errorhandlers) v->ehp=NULL;\n                    DISPATCH()\n                }\n            }\n        }\n\n        /* The error was not caught; unwind the stack to the point where we have to return  */\n        if (!v->errfp) {\n            v->errfp=v->fp; // Record frame pointer for stacktrace\n            v->errfp->pc=pc;\n        }\n\n        v->fp=retfp-1;\n\n    }\n\n#undef INTERPRET_LOOP\n#undef CASE_CODE\n#undef DISPATCH\n\n    //v->fp->pc=pc;\n\n    return false;\n}\n\n/* **********************************************************************\n* VM public interfaces\n* ********************************************************************** */\n\n/** Creates a new virtual machine */\nvm *morpho_newvm(void) {\n    vm *new = MORPHO_MALLOC(sizeof(vm));\n\n    if (new) vm_init(new);\n\n    return new;\n}\n\n/** Frees a virtual machine */\nvoid morpho_freevm(vm *v) {\n    vm_clear(v);\n    MORPHO_FREE(v);\n}\n\n/** Configure input callback function */\nvoid morpho_setinputfn(vm *v, morphoinputfn inputfn, void *ref) {\n    v->inputfn=inputfn;\n    v->inputref=ref;\n}\n\n/** Configure print callback function */\nvoid morpho_setprintfn(vm *v, morphoprintfn printfn, void *ref) {\n    v->printfn=printfn;\n    v->printref=ref;\n}\n\n/** Configure warning callback function */\nvoid morpho_setwarningfn(vm *v, morphowarningfn warningfn, void *ref) {\n    v->warningfn=warningfn;\n    v->warningref=ref;\n}\n\n/** Configure debugger callback function */\nvoid morpho_setdebuggerfn(vm *v, morphodebuggerfn debuggerfn, void *ref) {\n    v->debuggerfn=debuggerfn;\n    v->debuggerref=ref;\n}\n\n/** Returns a VM's error block */\nerror *morpho_geterror(vm *v) {\n    return &v->err;\n}\n\n/** @brief Raises an error described by an error structure\n * @param v        the virtual machine\n * @param err    error to raise */\nvoid morpho_error(vm *v, error *err) {\n    if (err->cat!=ERROR_WARNING) { // Errors of category ERROR_WARNING are always raised as warnings\n        v->err = *err;\n    } else morpho_warning(v, err);\n}\n\n/** @brief Raises an warning described by an error structure  */\nvoid morpho_warning(vm *v, error *err) {\n    if (v->warningfn) {\n        (v->warningfn) (v, v->warningref, err);\n    } else {\n        fprintf(stderr, \"Warning [%s]: %s\\n\", err->id, err->msg);\n    }\n}\n\n/** @brief Raise a runtime error given an id\n * @param v        the virtual machine\n * @param id       error id\n * @param ...      additional data for sprintf. */\nvoid morpho_runtimeerror(vm *v, errorid id, ...) {\n    va_list args;\n    error err;\n    error_init(&err);\n\n    va_start(args, id);\n    morpho_writeerrorwithidvalist(&err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args);\n    va_end(args);\n    \n    morpho_error(v, &err);\n    error_clear(&err);\n}\n\n/** @brief Raise a runtime warning given an id  */\nvoid morpho_runtimewarning(vm *v, errorid id, ...) {\n    va_list args;\n    error err;\n    error_init(&err);\n\n    va_start(args, id);\n    morpho_writeerrorwithidvalist(&err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args);\n    va_end(args);\n    \n    morpho_warning(v, &err);\n    error_clear(&err);\n}\n\n/** @brief Binds a set of objects to a Virtual Machine; public interface.\n *  @details Any object created during execution should be bound to a VM; this object is then managed by the garbage collector.\n *  @param v      the virtual machine\n *  @param obj    objects to bind */\nvoid morpho_bindobjects(vm *v, int nobj, value *obj) {\n    /* Now bind the new objects in. */\n    for (unsigned int i=0; i<nobj; i++) {\n        object *ob = MORPHO_GETOBJECT(obj[i]);\n        if (MORPHO_ISOBJECT(obj[i]) && ob->status<OBJECT_ISUNMARKED) {\n            ob->status=OBJECT_ISUNMARKED;\n            ob->next=v->objects;\n            v->objects=ob;\n            size_t size=object_size(ob);\n            v->bound+=size;\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n            dictionary_insert(&sizecheck, obj[i], MORPHO_INTEGER(size));\n#endif\n        }\n    }\n\n    /* Check if size triggers garbage collection */\n#ifndef MORPHO_DEBUG_STRESSGARBAGECOLLECTOR\n    if (v->bound>v->nextgc)\n#endif\n    {\n        int handle=morpho_retainobjects(v, nobj, obj);\n        \n        vm_collectgarbage(v);\n        \n        morpho_releaseobjects(v, handle);\n    }\n}\n\n/** @brief   Convenience function to wrap a single object into a value and bind to the VM\n *  @param   v VM to use\n *  @param   out Object to wrap\n *  @returns object wrapped in a value, or MORPHO_NIL if obj is NULL */\nvalue morpho_wrapandbind(vm *v, object *obj) {\n    value out = MORPHO_NIL;\n    if (obj) {\n        out=MORPHO_OBJECT(obj);\n        morpho_bindobjects(v, 1, &out);\n    }\n    return out;\n}\n\n/** @brief Temporarily retain objects across multiple reentrant calls to the VM.\n *  @param v      the virtual machine\n *  @param nobj  number of objects to retain\n *  @param obj    objects to retain\n *  @returns an integer handle to pass to releaseobjects */\nint morpho_retainobjects(vm *v, int nobj, value *obj) {\n    int gcount=v->retain.count;\n    varray_valueadd(&v->retain, obj, nobj);\n    return gcount;\n}\n\n/** @brief Release objects temporarily retained by the VM.\n *  @param v      the virtual machine\n *  @param handle a handle returned by morpho_retainobjects. */\nvoid morpho_releaseobjects(vm *v, int handle) {\n    if (handle>=0) v->retain.count=handle;\n}\n\n/** @brief Inform the VM that the size of an object has changed\n *  @param v      the virtual machine\n *  @param obj  the object to resize\n *  @param oldsize old size\n *  @param newsize new size\n */\nvoid morpho_resizeobject(vm *v, object *obj, size_t oldsize, size_t newsize) {\n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n    dictionary_insert(&sizecheck, MORPHO_OBJECT(obj), MORPHO_INTEGER(newsize));\n#endif\n    if (!MORPHO_ISGARBAGECOLLECTED(MORPHO_OBJECT(obj))) return;\n    v->bound-=oldsize;\n    v->bound+=newsize;\n}\n\n\n/** @brief Checks if an object is managed by the garbage collector\n *  @param obj  the object to check\n *  @returns true if it is managed, false otherwise \n */\nbool morpho_ismanagedobject(object *obj) {\n    return (obj->status==OBJECT_ISUNMARKED || obj->status==OBJECT_ISMARKED);\n}\n\n/** Runs a program\n * @param[in] v - the virtual machine to use\n * @param[in] p - program to run\n * @returns true on success, false if an error occurred */\nbool morpho_run(vm *v, program *p) {\n    if (!vm_start(v, p)) return false;\n    \n    /* Initialize global variables */\n    int oldsize = v->globals.count;\n    int nglobals = program_countglobals(p);\n    varray_valueresize(&v->globals, nglobals);\n    v->globals.count=nglobals;\n    for (int i=oldsize; i<nglobals; i++) v->globals.data[i]=MORPHO_NIL; /* Zero out globals */\n\n    /* and initially set the register pointer to the bottom of the stack */\n    value *reg = v->stack.data;\n    \n    /* Expand and clear the stack if necessary */\n    if (v->fp->function->nregs>v->stack.count) {\n        unsigned int oldcount=v->stack.count;\n        vm_expandstack(v, &reg, v->fp->function->nregs-v->stack.count);\n        for (unsigned int i=oldcount; i<v->stack.count; i++) v->stack.data[i]=MORPHO_NIL;\n    }\n\n    instructionindx start = program_getentry(p);\n\n    int success = morpho_interpret(v, reg, start);\n\n    if (!success &&\n        morpho_matcherror(morpho_geterror(v), VM_EXIT)) {\n        success=true;\n        error_clear(morpho_geterror(v));\n    }\n    \n    return success;\n}\n\n/* Call a morpho function from C code */\nbool morpho_call(vm *v, value f, int nargs, value *args, value *ret) {\n    bool success=false;\n    value fn=f;\n    value r0=f;\n\n    if (MORPHO_ISINVOCATION(fn)) {\n        /* An method invocation */\n        objectinvocation *inv = MORPHO_GETINVOCATION(f);\n        fn=inv->method;\n        r0=inv->receiver;\n    }\n    \n    if (MORPHO_ISMETAFUNCTION(fn) &&\n        !metafunction_resolve(MORPHO_GETMETAFUNCTION(fn), nargs, args, &v->err, &fn)) {\n        if (!morpho_checkerror(&v->err)) morpho_runtimeerror(v, VM_MLTPLDSPTCHFLD);\n        return false;\n    }\n    \n    if (MORPHO_ISBUILTINFUNCTION(fn)) {\n        objectbuiltinfunction *f = MORPHO_GETBUILTINFUNCTION(fn);\n\n        /* Copy arguments across to comply with call standard */\n        value xargs[nargs+1];\n        xargs[0]=r0;\n        for (unsigned int i=0; i<nargs; i++) xargs[i+1]=args[i];\n        v->fp->nopt=0; // TODO: Extend morpho call to support optional args.\n        \n#ifdef MORPHO_PROFILER\n        v->fp->inbuiltinfunction=f;\n#endif\n        *ret=(f->function) (v, nargs, xargs);\n#ifdef MORPHO_PROFILER\n        v->fp->inbuiltinfunction=NULL;\n#endif\n        success=true;\n    } else if (MORPHO_ISFUNCTION(fn) || MORPHO_ISCLOSURE(fn)) {\n        value *reg=v->stack.data+v->fp->roffset;\n        instruction *pc=v->fp->pc;\n\n        /* Set up the function call, advancing the frame pointer and expanding the stack if necessary */\n        if (vm_call(v, fn, v->fp->function->nregs, nargs, 0, args, &pc, &reg)) {\n            /* Now ensure the function (or self) is in r0 */\n            reg[0]=r0;\n\n            /* Set return to true in this callframe */\n            v->fp->ret=true;\n\n            /* Keep track of the stack in case it is reallocated */\n            value *stackbase=v->stack.data;\n            ptrdiff_t roffset=reg-v->stack.data;\n\n            success=morpho_interpret(v, reg, pc-v->instructions);\n\n            /* Restore reg if stack has expanded */\n            if (v->stack.data!=stackbase) reg=v->stack.data+roffset;\n\n            if (success) *ret=reg[0]; /* Return value */\n        }\n    } else if (MORPHO_ISCLASS(fn)) {\n        /* A function call on a class instantiates it */\n        objectclass *klass = MORPHO_GETCLASS(fn);\n        objectinstance *instance = object_newinstance(klass);\n        if (instance) {\n            value obj = MORPHO_OBJECT(instance);\n\n            /* Call the initializer if the class provides one */\n            value ifunc;\n            if (morpho_lookupmethod(obj, initselector, &ifunc)) {\n                success=morpho_invoke(v, obj, ifunc, nargs, args, ret);\n            } else if (nargs==0) {\n                success=true;\n            } else morpho_runtimeerror(v, VM_NOINITIALIZER, MORPHO_GETCSTRING(klass->name));\n            \n            if (success) {\n                vm_bindobjectwithoutcollect(v, obj); // 4/2/25 Changed to disable collection because we can't guarantee the external context of the caller (e.g. apply)\n                *ret = obj;\n            } else morpho_freeobject(obj);\n        } else morpho_runtimeerror(v, VM_INSTANTIATEFAILED);\n    }\n\n    return success;\n}\n\n/** Find the class associated with a value */\nobjectclass *morpho_lookupclass(value v) {\n    objectclass *klass=NULL;\n    if (MORPHO_ISINSTANCE(v)) klass=MORPHO_GETINSTANCE(v)->klass;\n    else if (MORPHO_ISCLASS(v)) klass=MORPHO_GETCLASS(v);\n    else if (MORPHO_ISOBJECT(v)) klass=object_getveneerclass(MORPHO_GETOBJECTTYPE(v));\n    else klass=value_getveneerclass(v);\n    return klass;\n}\n\n/** Finds a method */\nbool morpho_lookupmethod(value obj, value label, value *method) {\n    objectclass *klass = morpho_lookupclass(obj);\n    if (klass) return dictionary_get(&klass->methods, label, method);\n\n    return false;\n}\n\n/** Invoke a method on an object.\n @param[in] v - the virtual machine\n @param[in] obj - object to call on\n @param[in] method - method to invoke. NOTE lookup this first with morpho_lookupmethod if you just have a string\n @param[in] nargs - number of arguments\n @param[in] args - the arguments\n @param[out] ret - result of call\n @returns true on success, false otherwise */\nbool morpho_invoke(vm *v, value obj, value method, int nargs, value *args, value *ret) {\n    objectinvocation inv;\n    object_init((object *) &inv, OBJECT_INVOCATION);\n    inv.receiver=obj;\n    inv.method=method;\n\n    return morpho_call(v, MORPHO_OBJECT(&inv), nargs, args, ret);\n}\n\n/* **********************************************************************\n* I/O\n* ********************************************************************** */\n\n/** Prints a formatted string to the VM's output channel */\nint morpho_printf(vm *v, char *format, ...) {\n    int nchars=0;\n    \n    va_list args;\n    \n    if (v && v->printfn) {\n        for (;;) {\n            va_start(args, format);\n            nchars = vsnprintf(v->buffer.data, v->buffer.capacity, format, args);\n            va_end(args);\n            \n            if (nchars+1<=v->buffer.capacity) break;\n            \n            if (!varray_charresize(&v->buffer, nchars+1)) return 0;\n        }\n        \n        v->buffer.count=nchars;\n    \n        (v->printfn) (v, v->printref, v->buffer.data);\n    } else { // If no VM or no print function available, resort to regular printf\n        va_start(args, format);\n        nchars=vprintf(format, args);\n        va_end(args); \n    }\n    \n    return nchars;\n}\n\n/** Reads a line of text from the VM's input channel; returns the number of bytes read */\nint morpho_readline(vm *v, varray_char *buffer) {\n    int start=buffer->count;\n    if (v && v->inputfn) {\n        (v->inputfn) (v, v->inputref, MORPHO_INPUT_LINE, buffer);\n    } else { // If no VM or no input function available, resort to regular fgetc\n        do {\n            int c = fgetc(stdin);\n            if (c==EOF || c=='\\n') break;\n            varray_charwrite(buffer, (char) c);\n        } while (true);\n    }\n    return buffer->count-start;\n}\n\n/* **********************************************************************\n* Subkernels\n* ********************************************************************** */\n\nDEFINE_VARRAY(vm, struct svm *)\n\n/** Obtain subkernels from the VM for use in a thread */\nbool vm_subkernels(vm *v, int nkernels, vm **subkernels) {\n    int nk=0;\n    \n    /* Check for unused subkernels */\n    for (int i=0; i<v->subkernels.count; i++) {\n        vm *kernel=v->subkernels.data[i];\n        if (!kernel->parent) { // Check whether subkernel is unused\n            subkernels[nk]=kernel;\n            kernel->parent=v;\n            nk++;\n        }\n    }\n    \n    /* Create any additional kernels that need to be made */\n    for (int i=nk; i<nkernels; i++) {\n        vm *new = morpho_newvm();\n        if (!new) return false;\n        if (!varray_vmadd(&v->subkernels, &new, 1)) return false;\n        vm_start(new, v->current);\n        new->globals.count=v->globals.count;\n        new->globals.data=v->globals.data;\n        new->parent=v;\n        subkernels[i]=new;\n    }\n    \n    return true;\n}\n\n/** Release a subkernels from the VM for use in a thread */\nvoid vm_releasesubkernel(vm *subkernel) {\n    vm *v = subkernel->parent;\n    if (!v) return;\n    \n    /** Transfer objects from subkernel to kernel */\n    if (subkernel->objects) {\n        object *obj;\n    \n        for (obj=subkernel->objects; obj!=NULL; obj=obj->next) {\n            if (obj->next==NULL) break;\n        }\n        \n        /* Add the subkernel's objects to the parent */\n        obj->next=v->objects;\n        v->objects=subkernel->objects;\n        \n        /* Include this in the bound list */\n        v->bound+=subkernel->bound;\n        \n        /* Remove from the subkernel */\n        subkernel->objects=NULL;\n        subkernel->bound=0;\n    }\n    \n    /** Check if the subkernel is in an error state */\n    if (!ERROR_SUCCEEDED(subkernel->err) &&\n        ERROR_SUCCEEDED(v->err)) {\n        v->err=subkernel->err;\n    }\n    \n    subkernel->parent=NULL;\n}\n\n/** Clean out attached objects from a subkernel */\nvoid vm_cleansubkernel(vm *subkernel) {\n    object *next=NULL;\n    for (object *obj=subkernel->objects; obj!=NULL; obj=next) {\n        next=obj->next;\n        object_free(obj);\n    }\n    subkernel->objects=NULL;\n    subkernel->bound=0;\n}\n\n/* **********************************************************************\n* Thread local storage\n* ********************************************************************** */\n\nint ntlvars=0;\n\n/** Adds a thread local variable, returning the handle */\nint vm_addtlvar(void) {\n    int out = ntlvars;\n    ntlvars++;\n    return out;\n}\n\n/** Initialize threadlocal variables for a vm */\nbool vm_inittlvars(vm *v) {\n    if (v->tlvars.capacity<ntlvars) {\n        if (!varray_valueresize(&v->tlvars, ntlvars)) return false;\n        v->tlvars.count=ntlvars;\n        for (int i=0; i<ntlvars; i++) v->tlvars.data[i]=MORPHO_NIL;\n    }\n    return true;\n}\n\n/** Sets the value of a thread local variable */\nbool vm_settlvar(vm *v, int handle, value val) {\n    bool success=false;\n    if (handle<ntlvars &&\n        vm_inittlvars(v)) {\n        v->tlvars.data[handle]=val;\n        success=true;\n    }\n    return success;\n}\n\n/** Gets the value of a thread local variable */\nbool vm_gettlvar(vm *v, int handle, value *out) {\n    bool success=false;\n    if (handle<ntlvars &&\n        vm_inittlvars(v)) {\n        *out = v->tlvars.data[handle];\n        success=true; \n    }\n    return success;\n}\n\n/* **********************************************************************\n * Finalize functions\n * ********************************************************************** */\n\nvarray_value _finalizefns;\n\nvoid morpho_addfinalizefn(morpho_finalizefn finalizefn) {\n    varray_valuewrite(&_finalizefns, MORPHO_OBJECT(finalizefn));\n}\n\n/* **********************************************************************\n* Initialization\n* ********************************************************************** */\n\n/** Initializes morpho */\nvoid morpho_initialize(void) {\n    varray_valueinit(&_finalizefns);\n    \n    random_initialize();\n    error_initialize();\n    \n    value_initialize(); // } Must be first\n    object_initialize(); // }\n    \n    builtin_initialize(); // Must come before initialization of any classes or similar\n    resources_initialize(); // Must come before compiler and extensions\n    \n    lex_initialize();\n    parse_initialize();\n    compile_initialize();\n    debugger_initialize();\n    extensions_initialize();\n    \n#ifdef MORPHO_DEBUG_GCSIZETRACKING\n    dictionary_init(&sizecheck);\n#endif\n    \n    morpho_defineerror(ERROR_ALLOCATIONFAILED, ERROR_EXIT, ERROR_ALLOCATIONFAILED_MSG);\n    morpho_defineerror(ERROR_INTERNALERROR, ERROR_EXIT, ERROR_INTERNALERROR_MSG);\n    morpho_defineerror(ERROR_ERROR, ERROR_HALT, ERROR_ERROR_MSG);\n    \n    morpho_defineerror(VM_STCKOVFLW, ERROR_HALT, VM_STCKOVFLW_MSG);\n    morpho_defineerror(VM_ERRSTCKOVFLW, ERROR_HALT, VM_ERRSTCKOVFLW_MSG);\n    morpho_defineerror(VM_INVLDOP, ERROR_HALT, VM_INVLDOP_MSG);\n    morpho_defineerror(VM_CNCTFLD, ERROR_HALT, VM_CNCTFLD_MSG);\n    morpho_defineerror(VM_UNCALLABLE, ERROR_HALT, VM_UNCALLABLE_MSG);\n    morpho_defineerror(VM_GLBLRTRN, ERROR_HALT, VM_GLBLRTRN_MSG);\n    morpho_defineerror(VM_INSTANTIATEFAILED, ERROR_HALT, VM_INSTANTIATEFAILED_MSG);\n    morpho_defineerror(VM_NOTANOBJECT, ERROR_HALT, VM_NOTANOBJECT_MSG);\n    morpho_defineerror(VM_OBJECTLACKSPROPERTY, ERROR_HALT, VM_OBJECTLACKSPROPERTY_MSG);\n    morpho_defineerror(VM_NOINITIALIZER, ERROR_HALT, VM_NOINITIALIZER_MSG);\n    morpho_defineerror(VM_NOTANINSTANCE, ERROR_HALT, VM_NOTANINSTANCE_MSG);\n    morpho_defineerror(VM_CLASSLACKSPROPERTY, ERROR_HALT, VM_CLASSLACKSPROPERTY_MSG);\n    morpho_defineerror(VM_INVALIDARGS, ERROR_HALT, VM_INVALIDARGS_MSG);\n    morpho_defineerror(VM_NOOPTARG, ERROR_HALT, VM_NOOPTARG_MSG);\n    morpho_defineerror(VM_UNKNWNOPTARG, ERROR_HALT, VM_UNKNWNOPTARG_MSG);\n    morpho_defineerror(VM_INVALIDARGSDETAIL, ERROR_HALT, VM_INVALIDARGSDETAIL_MSG);\n    morpho_defineerror(VM_NOTINDEXABLE, ERROR_HALT, VM_NOTINDEXABLE_MSG);\n    morpho_defineerror(VM_OUTOFBOUNDS, ERROR_HALT, VM_OUTOFBOUNDS_MSG);\n    morpho_defineerror(VM_NONNUMINDX, ERROR_HALT, VM_NONNUMINDX_MSG);\n    morpho_defineerror(VM_ARRAYWRONGDIM, ERROR_HALT, VM_ARRAYWRONGDIM_MSG);\n    morpho_defineerror(VM_DVZR, ERROR_HALT, VM_DVZR_MSG);\n\tmorpho_defineerror(VM_GETINDEXARGS, ERROR_HALT, VM_GETINDEXARGS_MSG);\n    morpho_defineerror(VM_MLTPLDSPTCHFLD, ERROR_HALT, VM_MLTPLDSPTCHFLD_MSG);\n\n    morpho_defineerror(VM_DBGQUIT, ERROR_HALT, VM_DBGQUIT_MSG);\n\n    /* Selector for initializers */\n    initselector=builtin_internsymbolascstring(MORPHO_INITIALIZER_METHOD);\n\n    indexselector=builtin_internsymbolascstring(MORPHO_GETINDEX_METHOD);\n    setindexselector=builtin_internsymbolascstring(MORPHO_SETINDEX_METHOD);\n\n    addselector=builtin_internsymbolascstring(MORPHO_ADD_METHOD);\n    addrselector=builtin_internsymbolascstring(MORPHO_ADDR_METHOD);\n    subselector=builtin_internsymbolascstring(MORPHO_SUB_METHOD);\n    subrselector=builtin_internsymbolascstring(MORPHO_SUBR_METHOD);\n    mulselector=builtin_internsymbolascstring(MORPHO_MUL_METHOD);\n    mulrselector=builtin_internsymbolascstring(MORPHO_MULR_METHOD);\n    divselector=builtin_internsymbolascstring(MORPHO_DIV_METHOD);\n    divrselector=builtin_internsymbolascstring(MORPHO_DIVR_METHOD);\n    powselector=builtin_internsymbolascstring(MORPHO_POW_METHOD);\n    powrselector=builtin_internsymbolascstring(MORPHO_POWR_METHOD);\n\n    enumerateselector=builtin_internsymbolascstring(MORPHO_ENUMERATE_METHOD);\n    countselector=builtin_internsymbolascstring(MORPHO_COUNT_METHOD);\n    cloneselector=builtin_internsymbolascstring(MORPHO_CLONE_METHOD);\n\n    printselector=builtin_internsymbolascstring(MORPHO_PRINT_METHOD);\n}\n\n/** Finalizes morpho, calling all finalize functions */\nvoid morpho_finalize(void) {\n    \n    for (int i=_finalizefns.count-1; i>=0; i--) {\n        morpho_finalizefn fn=(morpho_finalizefn) MORPHO_GETOBJECT(_finalizefns.data[i]);\n        fn();\n    }\n  \n    varray_valueclear(&_finalizefns);\n}\n"
  },
  {
    "path": "src/core/vm.h",
    "content": "/** @file vm.h\n *  @author T J Atherton\n *\n *  @brief The Morpho virtual machine\n */\n\n#ifndef vm_h\n#define vm_h\n\n#define MORPHO_CORE\n#include \"core.h\"\n\n/* **********************************************************************\n* Interface\n* ********************************************************************** */\n\nvoid morpho_initialize(void);\nvoid morpho_finalize(void);\n\ninstructionindx vm_previnstruction(vm *v);\ninstructionindx vm_currentinstruction(vm *v);\nint vm_getoptionalargs(vm *v);\ndebugger *vm_getdebugger(vm *v);\n\n#endif /* vm_h */\n"
  },
  {
    "path": "src/datastructures/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        dictionary.c        dictionary.h\n        debugannotation.c   debugannotation.h\n        error.c             error.h\n        object.c            object.h\n        program.c           program.h\n        signature.c         signature.h\n        syntaxtree.c        syntaxtree.h\n        value.c             value.h\n        varray.c            varray.h\n        version.c           version.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        dictionary.h\n        debugannotation.h\n        error.h\n        object.h\n        program.h\n        signature.h\n        syntaxtree.h\n        value.h\n        varray.h\n        version.h\n)\n"
  },
  {
    "path": "src/datastructures/debugannotation.c",
    "content": "/** @file debugannotation.c\n*  @author T J Atherton\n*\n*  @brief Debugging annotations for programs\n*/\n\n#include \"debugannotation.h\"\n\n/* **********************************************************************\n * Debugging annotations\n * ********************************************************************** */\n\nDEFINE_VARRAY(debugannotation, debugannotation);\n\n/** Retrieve the last annotation */\ndebugannotation *debugannotation_last(varray_debugannotation *list) {\n    if (list->count>0) return &list->data[list->count-1];\n    return NULL;\n}\n\n/** Adds an annotation to a list */\nvoid debugannotation_add(varray_debugannotation *list, debugannotation *annotation) {\n    varray_debugannotationadd(list, annotation, 1);\n}\n\n/** Removes the last annotation */\nvoid debugannotation_stripend(varray_debugannotation *list) {\n    if (list->count>0) list->data[list->count-1].content.element.ninstr--;\n}\n\n/** Sets the current function */\nvoid debugannotation_setfunction(varray_debugannotation *list, objectfunction *func) {\n    debugannotation ann = { .type = DEBUG_FUNCTION, .content.function.function = func};\n    debugannotation_add(list, &ann);\n}\n\n/** Sets the current class */\nvoid debugannotation_setclass(varray_debugannotation *list, objectclass *klass) {\n    debugannotation ann = { .type = DEBUG_CLASS, .content.klass.klass = klass};\n    debugannotation_add(list, &ann);\n}\n\n/** Sets the current module */\nvoid debugannotation_setmodule(varray_debugannotation *list, value module) {\n    debugannotation ann = { .type = DEBUG_MODULE, .content.module.module = module };\n    debugannotation_add(list, &ann);\n}\n\n/** Associates a register with a symbol */\nvoid debugannotation_setreg(varray_debugannotation *list, indx reg, value symbol) {\n    if (!MORPHO_ISSTRING(symbol)) return;\n    value sym = object_clonestring(symbol);\n    debugannotation ann = { .type = DEBUG_REGISTER, .content.reg.reg = reg, .content.reg.symbol = sym };\n    debugannotation_add(list, &ann);\n}\n\n/** Associates a global with a symbol */\nvoid debugannotation_setglobal(varray_debugannotation *list, indx gindx, value symbol) {\n    if (!MORPHO_ISSTRING(symbol)) return;\n    value sym = object_clonestring(symbol);\n    debugannotation ann = { .type = DEBUG_GLOBAL, .content.global.gindx = gindx, .content.global.symbol = sym };\n    debugannotation_add(list, &ann);\n}\n\n/** Pushes an error handler onto the stack */\nvoid debugannotation_pusherr(varray_debugannotation *list, objectdictionary *dict) {\n    debugannotation ann = { .type = DEBUG_PUSHERR, .content.errorhandler.handler = dict};\n    debugannotation_add(list, &ann);\n}\n\n/** Pops an error handler from the stack */\nvoid debugannotation_poperr(varray_debugannotation *list) {\n    debugannotation ann = { .type = DEBUG_POPERR };\n    debugannotation_add(list, &ann);\n}\n\n/** Uses information from a syntaxtreenode to associate a sequence of instructions with source */\nvoid debugannotation_addnode(varray_debugannotation *list, syntaxtreenode *node) {\n    if (!node) return;\n    debugannotation *last = debugannotation_last(list);\n    if (last && last->type==DEBUG_ELEMENT &&\n        node->line==last->content.element.line &&\n        node->posn==last->content.element.posn) {\n        last->content.element.ninstr++;\n    } else {\n        debugannotation ann = { .type = DEBUG_ELEMENT, .content.element.line = node->line, .content.element.posn = node->posn, .content.element.ninstr=1 };\n        debugannotation_add(list, &ann);\n    }\n}\n\n/** Clear debugging list, freeing attached info */\nvoid debugannotation_clear(varray_debugannotation *list) {\n    for (unsigned int j=0; j<list->count; j++) {\n        value sym=MORPHO_NIL;\n        switch (list->data[j].type) {\n            case DEBUG_REGISTER:\n                sym = list->data[j].content.reg.symbol;\n                break;\n            case DEBUG_GLOBAL:\n                sym=list->data[j].content.global.symbol;\n                break;\n            default: break;\n        }\n        if (MORPHO_ISOBJECT(sym)) object_free(MORPHO_GETOBJECT(sym));\n    }\n    varray_debugannotationclear(list);\n}\n\n/* **********************************************************************\n * Display annotations\n * ********************************************************************** */\n\n/** Prints all the annotations for a program */\nvoid debugannotation_showannotations(varray_debugannotation *list) {\n    indx ix = 0;\n    printf(\"Showing %u annotations.\\n\", list->count);\n    for (unsigned int j=0; j<list->count; j++) {\n        printf(\"%u: \", j);\n        debugannotation *ann = &list->data[j];\n        switch (ann->type) {\n            case DEBUG_CLASS:\n                printf(\"Class: \");\n                if (!ann->content.klass.klass) {\n                    printf(\"(none)\");\n                } else {\n                    morpho_printvalue(NULL, MORPHO_OBJECT(ann->content.klass.klass));\n                }\n                break;\n            case DEBUG_ELEMENT:\n                printf(\"Element: [%ti] instructions: %i line: %i posn: %i\",\n                       ix, ann->content.element.ninstr, ann->content.element.line, ann->content.element.posn);\n                ix+=ann->content.element.ninstr;\n                break;\n            case DEBUG_FUNCTION:\n                printf(\"Function: \");\n                morpho_printvalue(NULL, MORPHO_OBJECT(ann->content.function.function));\n                break;\n            case DEBUG_MODULE:\n                printf(\"Module: \");\n                morpho_printvalue(NULL, ann->content.module.module);\n                break;\n            case DEBUG_PUSHERR:\n                printf(\"Pusherr: \");\n                morpho_printvalue(NULL, MORPHO_OBJECT(ann->content.errorhandler.handler));\n                break;\n            case DEBUG_POPERR:\n                printf(\"Poperr: \");\n                break;\n            case DEBUG_REGISTER:\n                printf(\"Register: %ti \", ann->content.reg.reg);\n                morpho_printvalue(NULL, ann->content.reg.symbol);\n                break;\n            case DEBUG_GLOBAL:\n                printf(\"Global: %ti \", ann->content.global.gindx);\n                morpho_printvalue(NULL, ann->content.reg.symbol);\n                break;\n        }\n        printf(\"\\n\");\n    }\n}\n"
  },
  {
    "path": "src/datastructures/debugannotation.h",
    "content": "/** @file debugannotation.h\n *  @author T J Atherton\n *\n *  @brief Debugging annotations for programs\n*/\n\n#ifndef debugannotation_h\n#define debugannotation_h\n\n#include \"syntaxtree.h\"\n#include \"object.h\"\n#include \"program.h\"\n\n/* -------------------------------------------------------\n * Debug annotations contain debugging information\n * ------------------------------------------------------- */\n\n/** Annotations for the compiled code to link back to the source */\ntypedef struct {\n    enum {\n        DEBUG_FUNCTION, // Set the current function\n        DEBUG_CLASS, // Set the current class\n        DEBUG_MODULE, // Set the current module\n        DEBUG_REGISTER, // Associates a symbol with a register\n        DEBUG_GLOBAL, // Associates a symbol with a global\n        DEBUG_ELEMENT, // Associates a sequence of instructions with a code element\n        DEBUG_PUSHERR, // Push an error handler\n        DEBUG_POPERR // Pop an error handler\n    } type;\n    union {\n        struct {\n            objectdictionary *handler;\n        } errorhandler;\n        struct {\n            objectfunction *function;\n        } function;\n        struct {\n            objectclass *klass;\n        } klass;\n        struct {\n            value module;\n        } module;\n        struct {\n            indx reg;\n            value symbol;\n        } reg;\n        struct {\n            indx gindx;\n            value symbol;\n        } global;\n        struct {\n            int ninstr;\n            int line;\n            int posn;\n        } element;\n    } content;\n} debugannotation;\n\nDECLARE_VARRAY(debugannotation, debugannotation)\n\n/* -------------------------------------------------------\n * Work with debug annotations\n * ------------------------------------------------------- */\n\ndebugannotation *debugannotation_last(varray_debugannotation *list);\nvoid debugannotation_add(varray_debugannotation *list, debugannotation *annotation);\nvoid debugannotation_stripend(varray_debugannotation *list);\nvoid debugannotation_setfunction(varray_debugannotation *list, objectfunction *func);\nvoid debugannotation_setclass(varray_debugannotation *list, objectclass *klass);\nvoid debugannotation_setmodule(varray_debugannotation *list, value module);\nvoid debugannotation_setreg(varray_debugannotation *list, indx reg, value symbol);\nvoid debugannotation_setglobal(varray_debugannotation *list, indx gindx, value symbol);\nvoid debugannotation_pusherr(varray_debugannotation *list, objectdictionary *dict);\nvoid debugannotation_poperr(varray_debugannotation *list);\nvoid debugannotation_addnode(varray_debugannotation *list, syntaxtreenode *node);\nvoid debugannotation_clear(varray_debugannotation *list);\n\nvoid debugannotation_showannotations(varray_debugannotation *list);\n\n#endif /* debugannotation_h */\n"
  },
  {
    "path": "src/datastructures/dictionary.c",
    "content": "/** @file dictionary.h\n *  @author T J Atherton\n *\n *  @brief Dictionary (hashtable) data structure\n */\n\n#include <stdio.h>\n#include <inttypes.h>\n#include \"dictionary.h\"\n#include \"common.h\"\n#include \"object.h\"\n\n/* **********************************************************************\n * Macros that control the behavior of the dictionary.\n * ********************************************************************** */\n\n/** The initial size of non-empty dictionary */\n#define DICTIONARY_DEFAULTSIZE 16\n\n/** An empty value */\n#define DICTIONARY_EMPTYVALUE MORPHO_NIL\n\n/** Value used to indicate a tombstone */\n#define DICTIONARY_TOMBSTONEVALUE MORPHO_TRUE\n\n/** Literal for an empty entry */\n#define DICTIONARY_EMPTYENTRY ((dictionaryentry) { DICTIONARY_EMPTYVALUE, DICTIONARY_EMPTYVALUE})\n\n/** Literal for a tombstone entry */\n#define DICTIONARY_TOMBSTONEENTRY ((dictionaryentry) { DICTIONARY_EMPTYVALUE, DICTIONARY_TOMBSTONEVALUE})\n\n/*\n * These macros can be changed to tune the algorithm\n */\n\n/** Define if we need to enforce power of two size in our implementation */\n#define DICTIONARY_ENFORCEPOWEROFTWO\n\n/** Test if we should resize - the below triggers a resize at 3/4 capacity */\n#define DICTIONARY_SIZEINCREASETHRESHOLD(x) (((x)>>1) + ((x)>>2))\n\n/** Test if we should resize - the below triggers a resize at 1/4 capacity */\n#define DICTIONARY_SIZEDECREASETHRESHOLD(x) ((x)>>2)\n\n/** Generate a new larger size given the current size - currently multiplies by 2  */\n#define DICTIONARY_INCREASESIZE(x) (x<<1)\n\n/** Generate a new smaller size given the current size - currently divides by 2  */\n#define DICTIONARY_DECREASESIZE(x) (x>>1)\n\n/** If defined, use Jenkins integer hash function */\n//#define DICTIONARY_INTEGERHASH_JENKINS\n\n/** If defined, use Fibonacci integer hash function */\n#define DICTIONARY_INTEGERHASH_FIBONACCI\n\n/** Reduce functions */\n\n/** Integer modulo */\n//#define DICTIONARY_REDUCE(x, size) (x % size)\n\n/** Faster version for power of two sizes */\n#define DICTIONARY_REDUCE(x, size) (x & (size-1))\n\n/** Faster version for arbitrary sizes - https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ */\n/*static inline uint32_t dictionary_reduce64(uint32_t x, uint32_t N) {\n  return ((uint64_t) x * (uint64_t) N) >> 32 ;\n}*/\n//#define DICTIONARY_REDUCE(x, size) (dictionary_reduce64(x,size))\n\n/* **********************************************************************\n * Hash functions\n * ********************************************************************** */\n\n/** Integer hash function from 32 ubit int to 32 bit uint due to Robert Jenkins */\n#ifdef DICTIONARY_INTEGERHASH_JENKINS\nhash dictionary_hashint( uint32_t a) {\n   a = (a+0x7ed55d16) + (a<<12);\n   a = (a^0xc761c23c) ^ (a>>19);\n   a = (a+0x165667b1) + (a<<5);\n   a = (a+0xd3a2646c) ^ (a<<9);\n   a = (a+0xfd7046c5) + (a<<3);\n   a = (a^0xb55a4f09) ^ (a>>16);\n   return a;\n}\n#endif\n\n#ifdef DICTIONARY_INTEGERHASH_FIBONACCI\nhash dictionary_hashint(uint32_t hash) {\n    return (hash * 2654435769u);\n}\n#endif\n\nhash dictionary_hashpointer(void *hash) {\n    uintptr_t ptr = (uintptr_t) hash;\n#if UINTPTR_MAX == UINT64_MAX\n    return (ptr * 11400714819323198485llu) >> 32;\n#elif UINTPTR_MAX == UINT32_MAX\n    return (ptr * 2654435769u);\n#else\n    UNREACHABLE(\"dictionary pointer hash function undefined. [Check dictionary_hashpointer]\");\n#endif\n}\n\n/** String hashing function FNV-1a combined with Fibonacci hash */\nhash dictionary_hashcstring(const char* key, size_t length) {\n  uint32_t hash = 2166136261u;\n\n  for (unsigned int i=0; i < length; i++) {\n    hash ^= key[i];\n    hash *= 16777619u; // FNV prime number for 32 bits\n  }\n\n  return dictionary_hashint(hash);\n}\n\n/** Hash multiple values with FNV-1a */\nhash dictionary_hashvaluelist(size_t length, value *key) {\n    uint32_t hash = 2166136261u;\n\n    for (unsigned int i=0; i < length; i++) {\n      hash ^= dictionary_hashvalue(key[i]);\n      hash *= 16777619u;\n    }\n\n    return dictionary_hashint(hash);\n}\n\n/* **********************************************************************\n * Dictionary implementation\n * ********************************************************************** */\n\n/** @brief Hashes a value\n * @param key the key to hash\n * @param intern set to true to use a precomputed hash (i.e. if it has been interned).\n * @returns the corresponding hash */\nhash dictionary_hash(value key, bool intern) {\n    if (MORPHO_ISINTEGER(key)) {\n        return dictionary_hashint((uint32_t) MORPHO_GETINTEGERVALUE(key));\n    } else if (MORPHO_ISOBJECT(key)){\n        if (intern) {\n            return MORPHO_GETOBJECTHASH(key);\n        } else return object_hash(MORPHO_GETOBJECT(key));\n    }\n    return 0;\n}\n\n/** Veneer onto interned hash */\nhash dictionary_hashvalue(value key) {\n    return dictionary_hash(key, false);\n}\n\n/** @brief Initializes a dictionary\n * @param dict the dictionary to initialize */\nvoid dictionary_init(dictionary *dict) {\n    dict->capacity=0;\n    dict->count=0;\n    dict->contents=NULL;\n}\n\n/** @brief Clears a dictionary structure, freeing attached memory\n *  @param dict the dictionary to clear\n *  @warning This doens't free keys or values in the dictionary. */\nvoid dictionary_clear(dictionary *dict) {\n    if (dict->contents) MORPHO_FREE(dict->contents);\n    dictionary_init(dict);\n}\n\n/** @brief Frees a dictionary's contents\n *  @param dict     the dictionary to clear\n *  @param freekeys whether to free the keys\n *  @param freevals whether to free the vals */\nvoid dictionary_freecontents(dictionary *dict, bool freekeys, bool freevals) {\n    if (dict->contents) {\n        for (unsigned int i=0; i<dict->capacity; i++) {\n            dictionaryentry *e = &dict->contents[i];\n            if (!MORPHO_ISNIL(e->key)) {\n                if (freekeys) morpho_freeobject(e->key);\n                if (freevals) morpho_freeobject(e->val);\n            }\n        }\n    }\n}\n\n/** @brief Resizes a dictionary.\n *  @param dict the dictionary to resize\n *  @param size a new size for the dictionary\n *  @returns true on success\n */\nbool dictionary_resize(dictionary *dict, unsigned int size) {\n    dictionaryentry *new=NULL, *old = dict->contents;\n    unsigned int oldsize = dict->capacity;\n#ifdef DICTIONARY_ENFORCEPOWEROFTWO\n    unsigned int newsize = morpho_powerof2ceiling(size);\n#else\n    unsigned int newsize = size;\n#endif\n    \n    /* Don't resize below the minimum */\n    if (dict->contents && newsize<DICTIONARY_DEFAULTSIZE) return false;\n    \n    new=MORPHO_MALLOC(newsize * sizeof(dictionaryentry));\n\n    /* Clear the newly allocated structure */\n    if (new) {\n        for (unsigned int i=0; i<newsize; i++) new[i] = DICTIONARY_EMPTYENTRY;\n    } else return false;\n    \n    /* Update the dictionary */\n    dict->capacity=newsize;\n    dict->contents=new;\n    dict->count=0; /* Restart from no entries */\n    \n    if (old) {\n        /* Copy the contents over */\n        for (unsigned int i=0; i<oldsize; i++) {\n            dictionaryentry *e = &old[i];\n            \n            if (MORPHO_ISNIL(e->key)) continue;\n            \n            dictionary_insert(dict, e->key, e->val);\n        }\n        MORPHO_FREE(old); \n    }\n    \n    return (bool) dict->contents;\n}\n\n/** @brief Searches for an entry in a dictionary\n *  @param[in]  dict   the dictionary to search\n *  @param[in]  key    the key to search for\n *  @param[in]  intern whether to use a strict equality search for objects or a fast search\n *  @param[out] entry  the dictionary entry corresponding to the key or a blank entry.\n *  @returns true if the entry was found, false otherwise */\nstatic bool dictionary_find(dictionary *dict, value key, bool intern, dictionaryentry **entry) {\n    /* If there's nothing in the hashtable, return immediately */\n    if (!dict->contents) return false;\n    \n    /* Find the starting point by hashing the key */\n    unsigned int start_index = DICTIONARY_REDUCE(dictionary_hash(key, intern), dict->capacity);\n    unsigned int indx = start_index;\n    \n    /* Store a pointer to any tombstones we encounter along the way. */\n    dictionaryentry *tombstone = NULL;\n    dictionaryentry *e = NULL;\n    \n    /* Loop over entries */\n    do {\n        e = &dict->contents[indx];\n        if (intern) {\n            /* If intern is set, we use MORPHO_SAME (tests for equality depending on whether\n            objects are the same not just equivalent. */\n            if (MORPHO_ISSAME(e->key, key)) {\n                /* We found the key! */\n                *entry = e;\n                return true;\n            }\n        } else if (MORPHO_ISEQUAL(e->key, key)) {\n            /* If intern is false, we can use the slower equivalence test */\n            /* We found the key! */\n            *entry = e;\n            return true;\n        }\n        \n        /* If the key for this entry is blank, it's empty or could be a tombstone */\n        if (MORPHO_ISNIL(e->key)) {\n            if (MORPHO_ISEQUAL(e->val, DICTIONARY_TOMBSTONEVALUE)) {\n                if (!tombstone) tombstone = e; /* We found a tombstone */\n            } else {\n                /* Otherwise, this is an empty slot. We can now break\n                the loop and eventually return this, or the tombstone */\n                break;\n            }\n        }\n        \n        indx = DICTIONARY_REDUCE((indx + 1), dict->capacity);\n    } while (indx!=start_index); /* Loop will always terminate if we do a full cycle of the entries */\n    // If we did not find an existing entry, return the blank, or\n    // preferably a tombstone\n    *entry = (tombstone ? tombstone : e);\n    return false;\n}\n\n/** @brief Internal function that inserts a value in a hashtable given a key\n * @param[in]  dict the dictionary\n * @param[in]  key  key to insert\n * @param[in]  val  value to insert\n * @param[in]  intern use an interned key\n * @returns true if successful, false otherwise\n * @warning If an entry already exists, it is overwritten. Caller should check for existing keys\n *          if this is necessary. */\nstatic inline bool _dictionary_insert(dictionary *dict, value key, value val, bool intern) {\n    dictionaryentry *entry=NULL;\n    \n    if (!dict->contents) {\n        dictionary_resize(dict, DICTIONARY_DEFAULTSIZE);\n    } else if (dict->count+1 > DICTIONARY_SIZEINCREASETHRESHOLD(dict->capacity)) {\n        /* Trigger a resize */\n        dictionary_resize(dict, DICTIONARY_INCREASESIZE(dict->capacity));\n    }\n    \n    if (dict->contents) {\n        if (dictionary_find(dict, key, intern, &entry)) {\n            /* Entry already exists */\n            entry->val=val;\n            return true;\n        } else {\n            /* Entry doesn't exist, */\n            if (entry) {\n                entry->key=key;\n                entry->val=val;\n                dict->count++;\n                return true;\n            } else {\n                UNREACHABLE(\"Dictionary failed to insert an entry\");\n            }\n        }\n    }\n    \n    return false;\n}\n\n/** @brief Inserts a value in a hashtable given a key\n * @param[in]  dict the dictionary\n * @param[in]  key  key to insert\n * @param[in]  val  value to insert\n * @returns true if successful, false otherwise\n * @warning If an entry already exists, it is overwritten. Caller should check for existing keys\n *          if this is necessary. */\nbool dictionary_insert(dictionary *dict, value key, value val) {\n    return _dictionary_insert(dict, key, val, false);\n}\n\n/** @brief Inserts a value in a hashtable given a key, assuming the key has been interned\n * @param[in]  dict the dictionary\n * @param[in]  key  key to insert\n * @param[in]  val  value to insert\n * @returns true if successful, false otherwise\n * @warning If an entry already exists, it is overwritten. Caller should check for existing keys\n *          if this is necessary. */\nbool dictionary_insertintern(dictionary *dict, value key, value val) {\n    return _dictionary_insert(dict, key, val, true);\n}\n    \n/** @brief Interns a new key\n *  @detail Looks to see if a similar key [one that passes MORPHO_ISEQUAL] is already\n *          in the dictionary, and if so returns it. Otherwise, inserts this key into\n *          the dictionary.\n *  @param[in]  dict the dictionary\n *  @param[in]  key  a new key to intern\n *  @returns the internalized key, or MORPHO_NIL on failure. */\nvalue dictionary_intern(dictionary *dict, value key) {\n    dictionaryentry *entry=NULL;\n    \n    /* Is the key already in the dictionary? */\n    if (dictionary_find(dict, key, false, &entry)) {\n        return entry->key;\n    } else {\n        /* If not, insert it with a blank value */\n        if (dictionary_insert(dict, key, MORPHO_NIL)) {\n            MORPHO_SETOBJECTHASH( key, dictionary_hash(key, false));\n            return key;\n        }\n    }\n    \n    return MORPHO_NIL;\n}\n\n/** @brief Internal function that retrieves a value from a dictionary given a key\n * @param[in]  dict   the dictionary to get\n * @param[in]  key    key to locate\n * @param[in]  intern should we assume the key has been interned?\n *                    i.e. that object equivalence is tested with MORPHO_ISSAME rather than MORPHO_ISEQUAL\n * @param[out] val  Stores the result in this value if found.\n * @returns true if found, false otherwise\n*/\nstatic inline bool _dictionary_get(dictionary *dict, value key, bool intern, value *val) {\n    dictionaryentry *entry=NULL;\n    \n    if (dictionary_find(dict, key, intern, &entry)) {\n        if (val) *val = entry->val;\n        return true;\n    }\n    \n    return false;\n}\n\n/** @brief Retrieves a value from a dictionary given a key\n * @param[in]  dict the dictionary to get\n * @param[in]  key  key to locate\n * @param[out] val  Stores the result in this value if found.\n * @returns true if found, false otherwise\n */\nbool dictionary_get(dictionary *dict, value key, value *val) {\n    return _dictionary_get(dict, key, false, val);\n}\n\n/** @brief Retrieves a value from a dictionary given a key assuming\n *         that key has been interned.\n * @param[in]  dict the dictionary to get\n * @param[in]  key  key to locate\n * @param[out] val  Stores the result in this value if found.\n * @returns true if found, false otherwise\n */\nbool dictionary_getintern(dictionary *dict, value key, value *val) {\n    return _dictionary_get(dict, key, true, val);\n}\n\n/** @brief Removes a key from a dictionary given a key\n * @param[in]  dict the dictionary to initialize\n * @param[in]  key  key to remove\n * @returns true if the key was found, false otherwise\n */\nbool dictionary_remove(dictionary *dict, value key) {\n    dictionaryentry *entry=NULL;\n    \n    if (dictionary_find(dict, key, false, &entry)) {\n        *entry = DICTIONARY_TOMBSTONEENTRY;\n        dict->count--;\n        \n        /* If we have lost our last entry, clear the dictionary */\n        if (dict->count==0) {\n            dictionary_clear(dict);\n        } else if (dict->count < DICTIONARY_SIZEDECREASETHRESHOLD(dict->capacity)) {\n            /* Trigger a resize if we are below some threshhold */\n            dictionary_resize(dict, DICTIONARY_DECREASESIZE(dict->capacity));\n        }\n        \n        return true;\n    }\n    \n    return false;\n}\n\n/** @brief Copies the entries of one dictionary to another */\nbool dictionary_copy(dictionary *src, dictionary *dest) {\n    if (src->contents) {\n        for (unsigned int i=0; i<src->capacity; i++) {\n            dictionaryentry *e = &src->contents[i];\n            if (!MORPHO_ISNIL(e->key)) {\n                if (!dictionary_insert(dest, e->key, e->val)) return false;\n            }\n        }\n    }\n    return true;\n}\n\n/* **********************************************************************\n * Set functions, i.e. union, intersection, etc.\n * ********************************************************************** */\n\n/** @brief Computes the union of two dictionaries, i.e. the output dictionary contains all keys that occur in either a or b\n * @param[in]  a - input dictionary (values from this dictionary take priority)\n * @param[in]  b - input dictionary\n * @param[out] out - output dictionary\n * @returns true on success. */\nbool dictionary_union(dictionary *a, dictionary *b, dictionary *out) {\n    dictionary_clear(out);\n    if (!dictionary_copy(b, out)) return false;\n    if (!dictionary_copy(a, out)) return false;\n    return true;\n}\n\n/** @brief Computes the intersection of two dictionaries, i.e. the output dictionary contains only keys that occur in both a and b\n * @param[in]  a - input dictionary (values are copied from this dictionary)\n * @param[in]  b - input dictionary\n * @param[out] out - output dictionary\n * @returns true on success. */\nbool dictionary_intersection(dictionary *a, dictionary *b, dictionary *out) {\n    bool success=true;\n    dictionary_clear(out);\n    if (a->contents) {\n        for (unsigned int i=0; i<a->capacity; i++) {\n            dictionaryentry *e = &a->contents[i];\n            if (!MORPHO_ISNIL(e->key)) {\n                if (dictionary_get(b, e->key, NULL)) {\n                    if (!dictionary_insert(out, e->key, e->val)) return false;\n                }\n            }\n        }\n    }\n    return success;\n}\n\n/** @brief Computes the difference of two dictionaries, i.e. the output dictionary contains only keys from a that do NOT occur in B\n * @param[in]  a - input dictionary (keys are copied from this)\n * @param[in]  b - input dictionary\n * @param[out] out - output dictionary\n * @returns true on success. */\nbool dictionary_difference(dictionary *a, dictionary *b, dictionary *out) {\n    bool success=true;\n    dictionary_clear(out);\n    if (a->contents) {\n        for (unsigned int i=0; i<a->capacity; i++) {\n            dictionaryentry *e = &a->contents[i];\n            if (!MORPHO_ISNIL(e->key)) {\n                if (!dictionary_get(b, e->key, NULL)) {\n                    if (!dictionary_insert(out, e->key, e->val)) return false;\n                }\n            }\n        }\n    }\n    return success;\n}\n"
  },
  {
    "path": "src/datastructures/dictionary.h",
    "content": "/** @file dictionary.h\n *  @author T J Atherton\n *\n *  @brief Dictionary (hashtable) data structure\n */\n\n#ifndef dictionary_h\n#define dictionary_h\n\n#include \"value.h\"\n\n/* -------------------------------------------------------\n * Hash type definition\n * ------------------------------------------------------- */\n\ntypedef uint32_t hash;\n#define HASH_EMPTY 0\n\n/* -------------------------------------------------------\n * Dictionary entry type definition\n * ------------------------------------------------------- */\n\n/** @brief A single dictionary entry */\ntypedef struct {\n    value key;\n    value val;\n} dictionaryentry;\n\n/* -------------------------------------------------------\n * Dictionary type definition\n * ------------------------------------------------------- */\n\n/** @brief dictionary data structure that maps keys to values */\ntypedef struct {\n    unsigned int capacity; /** capacity of the dictionary */\n    unsigned int count; /** number of items in the dictionary */\n    \n    dictionaryentry *contents; /** contents of the dictionary */\n} dictionary;\n\n/* -------------------------------------------------------\n * Hash functions that can be used in object definitions\n * ------------------------------------------------------- */\n\nhash dictionary_hashvalue(value key);\nhash dictionary_hashint(uint32_t a);\nhash dictionary_hashpointer(void *hash);\nhash dictionary_hashcstring(const char *key, size_t length);\nhash dictionary_hashvaluelist(size_t nel, value *key);\n\n/* -------------------------------------------------------\n * Dictionary interface\n * ------------------------------------------------------- */\n\nvoid dictionary_init(dictionary *dict);\nvoid dictionary_clear(dictionary *dict);\nvoid dictionary_freecontents(dictionary *dict, bool freekeys, bool freevals);\nbool dictionary_insert(dictionary *dict, value key, value val);\nbool dictionary_insertintern(dictionary *dict, value key, value val);\nvalue dictionary_intern(dictionary *dict, value key);\nbool dictionary_get(dictionary *dict, value key, value *val);\nbool dictionary_getintern(dictionary *dict, value key, value *val);\nbool dictionary_remove(dictionary *dict, value key);\nbool dictionary_copy(dictionary *src, dictionary *dest);\n\nbool dictionary_union(dictionary *a, dictionary *b, dictionary *out);\nbool dictionary_intersection(dictionary *a, dictionary *b, dictionary *out);\nbool dictionary_difference(dictionary *a, dictionary *b, dictionary *out);\n\n#endif /* dictionary_h */\n"
  },
  {
    "path": "src/datastructures/error.c",
    "content": "/** @file error.c\n*  @author T J Atherton\n*\n*  @brief Morpho error data structure and handling\n*/\n\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n\n#include \"error.h\"\n#include \"strng.h\"\n#include \"common.h\"\n#include \"dictionary.h\"\n\n/* **********************************************************************\n * Global data\n * ********************************************************************** */\n\n/** A table of errors */\nstatic dictionary error_table;\n\n/** A table of error strings. */\nstatic varray_errordefinition error_messages;\n\n/* **********************************************************************\n * Utility functions\n * ********************************************************************** */\n\nDEFINE_VARRAY(errordefinition, errordefinition)\n\n/** Prints to an error block\n *  @param err      Error struct to fill out\n *  @param cat      The category of error */\nstatic void error_printf(error *err, errorcategory cat, char *file, int line, int posn, char *message, va_list args) {\n    \n    err->cat=cat;\n    err->file=file;\n    err->line=line;\n    err->posn=posn; \n    \n    /* Print the message with requested args */\n    vsnprintf(err->msg, MORPHO_ERRORSTRINGSIZE, message, args);\n}\n\n/** Clears an error structure\n *  @param err      Error struct to fill out */\nvoid error_clear(error *err) {\n    err->cat=ERROR_NONE;\n    err->id=NULL;\n    err->file=NULL;\n    err->line=ERROR_POSNUNIDENTIFIABLE; err->posn=ERROR_POSNUNIDENTIFIABLE;\n}\n\n/** Clears an error structure\n *  @param err      Error struct to fill out */\nvoid error_init(error *err) {\n    error_clear(err);\n}\n\n/** Gets an error definition given an errorid\n *  @param[in]  id     Error to retrieve\n *  @param[out] def   The error definition\n *  @returns true on success */\nbool morpho_getdefinitionfromid(errorid id, errordefinition **def) {\n    value result;\n    int indx;\n    bool success=false;\n    value key = object_stringfromcstring(id, strlen(id));\n    \n    if (dictionary_get(&error_table, key, &result)) {\n        indx=MORPHO_GETINTEGERVALUE(result);\n        *def=&error_messages.data[indx];\n        success=true;\n    }\n    \n    object_free(MORPHO_GETOBJECT(key));\n    \n    return success;\n}\n\n/** @brief Writes an error message to an error structure\n *  @param err  The error structure\n *  @param id   The error id.\n *  @param line The line at which the error occurred, if identifiable.\n *  @param posn The position in the line at which the error occurred, if identifiable.\n *  @param args Additional parameters (the data for the printf commands in the message) */\nvoid morpho_writeerrorwithidvalist(error *err, errorid id, char *file, int line, int posn, va_list args) {\n    error_init(err);\n    errordefinition *def;\n    \n    err->id=id;\n    if (morpho_getdefinitionfromid(id, &def)) {\n        /* Print the message with requested args */\n        error_printf(err, def->cat, file, line, posn, def->msg, args);\n    } else {\n        UNREACHABLE(\"Undefined error generated.\");\n    }\n}\n\n/** @brief Writes an error message to an error structure\n *  @param err  The error structure\n *  @param id   The error id.\n *  @param file The file in which the error ocdured, if relevant.\n *  @param line The line at which the error occurred, if identifiable.\n *  @param posn The position in the line at which the error occurred, if identifiable. \n *  @param ...  Additional parameters (the data for the printf commands in the message) */\nvoid morpho_writeerrorwithid(error *err, errorid id, char *file, int line, int posn, ...) {\n    va_list args;\n    va_start(args, posn);\n    morpho_writeerrorwithidvalist(err, id, file, line, posn, args);\n    va_end(args);\n}\n\n/** @brief Writes an error message to an error structure without position information\n *  @param err  The error structure\n *  @param id   The error id.\n *  @param ...  Additional parameters (the data for the printf commands in the message) */\nvoid error_writewithid(error *err, errorid id, ... ) {\n    va_list args;\n    va_start(args, id);\n    morpho_writeerrorwithidvalist(err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args);\n    va_end(args);\n}\n\n/** @brief Writes a user error to an error structure\n *  @param err  The error structure\n *  @param id   The error id.\n *  @param message Additional parameters (the data for the printf commands in the message) */\nvoid morpho_writeusererror(error *err, errorid id, char *message) {\n    err->line=ERROR_POSNUNIDENTIFIABLE;\n    err->posn=ERROR_POSNUNIDENTIFIABLE;\n    err->cat=ERROR_USER;\n    err->id=id;\n    size_t length = strlen(message);\n    if (length>MORPHO_ERRORSTRINGSIZE-1) length = MORPHO_ERRORSTRINGSIZE-1;\n    memcpy(err->msg, message, length);\n    err->msg[length]='\\0'; // Ensure null termination\n}\n\n/** Defines an error\n * @param id       Error struct to fill out\n * @param cat      The category of error\n * @param message  The message string*/\nvoid morpho_defineerror(errorid id, errorcategory cat, char *message) {\n    errordefinition new = {.cat = cat, .msg=morpho_strdup(message)};\n    \n    if (new.msg) {\n        value key = object_stringfromcstring(id, strlen(id));\n        int indx = varray_errordefinitionwrite(&error_messages, new);\n        \n        if (dictionary_get(&error_table, key, NULL)) {\n            UNREACHABLE(\"Duplicate error.\\n\");\n        }\n        \n        dictionary_insert(&error_table, key, MORPHO_INTEGER(indx));\n    }\n}\n\n/** Gets the id of an error */\nerrorid morpho_geterrorid(error *err) {\n    return err->id;\n}\n\n/** Tests if an error struct is showing error id\n * @returns true if the match succeeds and false otherwise */\nbool morpho_matcherror(error *err, errorid id) {\n    if (err->cat==ERROR_NONE) return false; \n    return (strcmp(err->id, id)==0);\n}\n\n/** Tests if an error block is showing an error */\nbool morpho_checkerror(error *err) {\n    return (err->cat!=ERROR_NONE);\n}\n\n/* **********************************************************************\n* Unreachable code\n* ********************************************************************** */\n\n#ifdef MORPHO_DEBUG\nvoid morpho_unreachable(const char *explanation) {\n    fprintf(stderr, \"Internal consistency error: Please contact developer. [Explanation: %s].\\n\", explanation);\n    exit(BSD_EX_SOFTWARE);\n}\n#endif\n\n/* **********************************************************************\n* Initialization/Finalization\n* ********************************************************************** */\n\n/** Initializes the error handling system */\nvoid error_initialize(void) {\n    dictionary_init(&error_table);\n    varray_errordefinitioninit(&error_messages);\n    \n    morpho_addfinalizefn(error_finalize);\n}\n\n/** Finalizes the error handling system */\nvoid error_finalize(void) {\n    dictionary_freecontents(&error_table, true, false);\n    dictionary_clear(&error_table);\n    for (unsigned int i=0; i<error_messages.count; i++) {\n        MORPHO_FREE(error_messages.data[i].msg);\n    }\n    varray_errordefinitionclear(&error_messages);\n}\n"
  },
  {
    "path": "src/datastructures/error.h",
    "content": "/** @file error.h\n *  @author T J Atherton\n *\n *  @brief Morpho error data structure and handling\n*/\n\n#ifndef error_h\n#define error_h\n\n#include <stdarg.h>\n#include \"build.h\"\n#include \"varray.h\"\n\n/* -------------------------------------------------------\n * Error type definitions\n * ------------------------------------------------------- */\n\n/** @brief Identifier for errors. */\ntypedef char * errorid;\n\n/* --------------------------------\n * Categories of error\n * -------------------------------- */\n\n/** Identifies the category of error that has occurred */\ntypedef enum {\n    ERROR_NONE, /** No error. */\n    \n/* Execution should continue */\n    ERROR_WARNING, /** Warnings generated. */\n    \n/* Execution should not continue */\n    ERROR_HALT, /** or has occured. Should return to the user as fast as possible. */\n    ERROR_USER, /** Generated by the user */\n    ERROR_EXIT, /** Morpho should exit. */\n    \n/* Other kinds of error */\n    ERROR_LEX, /** or generated by the lexer */\n    ERROR_PARSE, /** or generated by the parser */\n    ERROR_COMPILE, /** or generated by the compiler */\n    ERROR_DEBUGGER /** or generated by the debugger */\n} errorcategory;\n\n/* --------------------------------\n * Macros to scrutinize these types\n * -------------------------------- */\n\n/** Did an operation succeed without errors? */\n#define ERROR_SUCCEEDED(err) ((err).cat == ERROR_NONE)\n\n/** Did an operation fail? */\n#define ERROR_FAILED(err) ((err).cat != ERROR_NONE)\n\n/** Is this a runtime error? */\n#define ERROR_ISRUNTIMEERROR(err) ((err).cat <= ERROR_EXIT)\n\n/* ----------------------------------------------------\n * Error struct, containing information about the error\n * ---------------------------------------------------- */\n\n/** @brief A type used by public-facing morpho functions */\ntypedef errorcategory morphoerror;\n\n/** @brief A static container for error messages. */\ntypedef struct {\n    errorcategory cat;\n    errorid id;\n    char *file; \n    int line, posn;\n    char msg[MORPHO_ERRORSTRINGSIZE];\n} error;\n\n/** Set line and posn to this value if they can't be determined */\n#define ERROR_POSNUNIDENTIFIABLE -1\n\n/* --------------------------------\n * Error definitions\n * -------------------------------- */\n\n/** @brief Definition of an error message. */\ntypedef struct {\n    errorcategory cat;\n    char *msg;\n} errordefinition;\n\n/** A varray of errordefinitions */\nDECLARE_VARRAY(errordefinition, errordefinition)\n\n/* -------------------------------------------------------\n * Error related macros\n * ------------------------------------------------------- */\n\n/** Macro to place in code that should be unreachable */\n#ifdef MORPHO_DEBUG\nvoid morpho_unreachable(const char *explanation);\n#define UNREACHABLE(x) morpho_unreachable(x)\n#else\n#define UNREACHABLE(x)\n#endif\n\n/* --------------------------------\n * Exit codes\n * -------------------------------- */\n\n/** Exit codes from BSD standard */\n#define BSD_EX_SOFTWARE 70\n\n/* -------------------------------------------------------\n * General error codes\n * ------------------------------------------------------- */\n\n#define ERROR_ALLOCATIONFAILED            \"Alloc\"\n#define ERROR_ALLOCATIONFAILED_MSG        \"Memory allocation failed.\"\n\n#define ERROR_INTERNALERROR               \"Intrnl\"\n#define ERROR_INTERNALERROR_MSG           \"Internal error (contact developer).\"\n\n#define ERROR_ERROR                       \"Err\"\n#define ERROR_ERROR_MSG                   \"Error.\"\n\n/* -------------------------------------------------------\n * VM error messages\n * ------------------------------------------------------- */\n\n#define VM_EXIT                           \"Exit\"\n#define VM_EXIT_MSG                       \"VM halted.\"\n\n#define VM_STCKOVFLW                      \"StckOvflw\"\n#define VM_STCKOVFLW_MSG                  \"Stack overflow.\"\n\n#define VM_ERRSTCKOVFLW                   \"ErrStckOvflw\"\n#define VM_ERRSTCKOVFLW_MSG               \"Error handler stack overflow.\"\n\n#define VM_INVLDOP                        \"InvldOp\"\n#define VM_INVLDOP_MSG                    \"Invalid operands. Failed to %s %s and %s\"\n\n#define VM_CNCTFLD                        \"CnctFld\"\n#define VM_CNCTFLD_MSG                    \"Concatenation failed.\"\n\n#define VM_UNCALLABLE                     \"Uncallable\"\n#define VM_UNCALLABLE_MSG                 \"Can only call a function or method.\"\n\n#define VM_GLBLRTRN                       \"GlblRtrn\"\n#define VM_GLBLRTRN_MSG                   \"Return encountered outside a function or method.\"\n\n#define VM_INSTANTIATEFAILED              \"InstFail\"\n#define VM_INSTANTIATEFAILED_MSG          \"Could not instantiate object.\"\n\n#define VM_NOTANOBJECT                    \"NotAnObj\"\n#define VM_NOTANOBJECT_MSG                \"Not an object.\"\n\n#define VM_OBJECTLACKSPROPERTY            \"ObjLcksPrp\"\n#define VM_OBJECTLACKSPROPERTY_MSG        \"Object lacks property or method '%s'.\"\n\n#define VM_NOINITIALIZER                  \"NoInit\"\n#define VM_NOINITIALIZER_MSG              \"Cannot instantiate with arguments because class '%s' does not provide an initializer.\"\n\n#define VM_NOTANINSTANCE                  \"NotAnInst\"\n#define VM_NOTANINSTANCE_MSG              \"Can only invoke methods on objects.\"\n\n#define VM_CLASSLACKSPROPERTY             \"ClssLcksMthd\"\n#define VM_CLASSLACKSPROPERTY_MSG         \"Class lacks method '%s'.\"\n\n#define VM_INVALIDARGS                    \"InvldArgs\"\n#define VM_INVALIDARGS_MSG                \"Expected %u arguments but got %u.\"\n\n#define VM_NOOPTARG                       \"NoOptArg\"\n#define VM_NOOPTARG_MSG                   \"Function doens't expect optional arguments.\"\n\n#define VM_UNKNWNOPTARG                   \"UnkwnOptArg\"\n#define VM_UNKNWNOPTARG_MSG               \"Unknown optional argument '%s'.\"\n\n#define VM_INVALIDARGSDETAIL              \"InvldArgsBltn\"\n#define VM_INVALIDARGSDETAIL_MSG          \"Function %s expects %u arguments of type %s.\"\n\n#define VM_NOTINDEXABLE                   \"NotIndxbl\"\n#define VM_NOTINDEXABLE_MSG               \"Value or object not indexable.\"\n\n#define VM_OUTOFBOUNDS                    \"IndxBnds\"\n#define VM_OUTOFBOUNDS_MSG                \"Index out of bounds.\"\n\n#define VM_NONNUMINDX                     \"NonNmIndx\"\n#define VM_NONNUMINDX_MSG                 \"Non-numerical array index.\"\n\n#define VM_ARRAYWRONGDIM                  \"ArrayDim\"\n#define VM_ARRAYWRONGDIM_MSG              \"Incorrect number of dimensions for array.\"\n\n#define VM_DBGQUIT                        \"DbgQuit\"\n#define VM_DBGQUIT_MSG                    \"Program terminated by user in debugger.\"\n\n#define VM_DVZR                           \"DvZr\"\n#define VM_DVZR_MSG                       \"Division by zero.\"\n\n#define VM_GETINDEXARGS                   \"NonintIndex\"\n#define VM_GETINDEXARGS_MSG               \"Noninteger array index.\"\n\n#define VM_MLTPLDSPTCHFLD                 \"MltplDsptchFld\"\n#define VM_MLTPLDSPTCHFLD_MSG             \"Multiple dispatch could not find an implementation that matches these arguments.\"\n\n/* -------------------------------------------------------\n * Error interface\n * ------------------------------------------------------- */\n\nvoid error_init(error *err);\nvoid error_clear(error *err);\n\nvoid morpho_writeerrorwithidvalist(error *err, errorid id, char *file, int line, int posn, va_list args);\nvoid morpho_writeerrorwithid(error *err, errorid id, char *file, int line, int posn, ...);\nvoid error_writewithid(error *err, errorid id, ... );\nvoid morpho_writeusererror(error *err, errorid id, char *message);\nvoid morpho_defineerror(errorid id, errorcategory cat, char *message);\n\nerrorid morpho_geterrorid(error *err);\nbool morpho_matcherror(error *err, errorid id);\nbool morpho_checkerror(error *err);\n\nvoid error_initialize(void);\nvoid error_finalize(void);\n\n#endif /* error_h */\n"
  },
  {
    "path": "src/datastructures/object.c",
    "content": "/** @file object.c\n *  @author T J Atherton\n *\n *  @brief Provide functionality for extended and mutable data types.\n*/\n\n#include <string.h>\n#include <stdio.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n/* **********************************************************************\n * Object definitions\n * ********************************************************************** */\n\n/** Hold the object type definitions as they're created */\nobjecttypedefn _objectdefns[MORPHO_MAXIMUMOBJECTDEFNS];\nobjecttype objectdefnnext; /** Type of the next object definition */\n\n/** Adds a new object type with a given definition.\n @returns: the objecttype identifier to be used henceforth */\nobjecttype object_addtype(objecttypedefn *def) {\n    if (!def->printfn || !def->sizefn) {\n        UNREACHABLE(\"Object definition must provide a print and size function.\");\n    }\n\n    if (objectdefnnext>=MORPHO_MAXIMUMOBJECTDEFNS) {\n        UNREACHABLE(\"Too many object definitions (increase MORPHO_MAXIMUMOBJECTDEFNS).\");\n    }\n\n    _objectdefns[objectdefnnext]=*def;\n    _objectdefns[objectdefnnext].veneer = NULL;\n    objectdefnnext+=1;\n\n    return objectdefnnext-1;\n}\n\n/** Gets the appropriate definition given an object */\nobjecttypedefn *object_getdefn(object *obj) {\n    return &_objectdefns[obj->type];\n}\n\n/* **********************************************************************\n * Objects\n * ********************************************************************** */\n\n/** @brief Initializes an object to be a certain type\n *  @param obj    object to initialize\n *  @param type   type to initialize with */\nvoid object_init(object *obj, objecttype type) {\n    obj->next=NULL;\n    obj->hsh=HASH_EMPTY;\n    obj->status=OBJECT_ISUNMANAGED;\n    obj->type=type;\n}\n\n/** Frees an object */\nvoid object_free(object *obj) {\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    if (obj) {\n        fprintf(stderr, \"Free object %p of type %d \", (void *) obj, obj->type);\n        morpho_printvalue(NULL, MORPHO_OBJECT(obj));\n        fprintf(stderr, \"\\n\");\n    }\n#endif\n    if (object_getdefn(obj)->freefn) object_getdefn(obj)->freefn(obj);\n    MORPHO_FREE(obj);\n}\n\n/** Free an object if it is unmanaged */\nvoid object_freeifunmanaged(object *obj) {\n    if (obj->status==OBJECT_ISUNMANAGED) object_free(obj);\n}\n\n/** Calls an object's print function */\nvoid object_print(void *v, value val) {\n    object *obj = MORPHO_GETOBJECT(val);\n    object_getdefn(obj)->printfn(obj, v);\n}\n\n/** Gets the total size of an object */\nsize_t object_size(object *obj) {\n    return object_getdefn(obj)->sizefn(obj);\n}\n\n/** Hash an object, either by calling its hash function or by hashing its pointer */\nhash object_hash(object *obj) {\n    objecttypedefn *defn = object_getdefn(obj);\n    if (defn->hashfn) return (defn->hashfn) (obj);\n    \n    return dictionary_hashpointer(obj);\n}\n\n/** Compare two objects */\nint object_cmp(object *a, object *b) {\n    objecttypedefn *defn = object_getdefn(a);\n    \n    if (defn->cmpfn) return (defn->cmpfn) (a, b);\n    \n    return (a == b? MORPHO_EQUAL: MORPHO_NOTEQUAL);\n}\n\n/** @brief Allocates an object\n *  @param size   size of memory to reserve\n *  @param type   type to initialize with */\nobject *object_new(size_t size, objecttype type) {\n    object *new = MORPHO_MALLOC(size);\n\n    if (new) object_init(new, type);\n\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    fprintf(stderr, \"Create object %p of size %ld with type %d.\\n\", (void *) new, size, type);\n#endif\n\n    return new;\n}\n\n/** Checks if an object is of a particular type */\nbool object_istype(value val, objecttype type) {\n    return (MORPHO_ISOBJECT(val) && MORPHO_GETOBJECTTYPE(val)==type);\n}\n\n/** Free any object that may be contained in a value. */\nvoid morpho_freeobject(value val) {\n    if (MORPHO_ISOBJECT(val)) object_free(MORPHO_GETOBJECT(val));\n}\n\n/* **********************************************************************\n * Veneer classes\n * ********************************************************************** */\n\n/** @brief Sets the veneer class for a particular object type */\nvoid object_setveneerclass(objecttype type, value class) {\n    if (_objectdefns[type].veneer!=NULL) {\n        UNREACHABLE(\"Veneer class redefined.\\n\");\n    }\n    _objectdefns[type].veneer=(object *) MORPHO_GETCLASS(class);\n}\n\n/** @brief Gets the veneer for a particular object type */\nobjectclass *object_getveneerclass(objecttype type) {\n    return (objectclass *) _objectdefns[type].veneer;\n}\n\n/** @brief Finds the object type associated with a veneer class; returns false if it is not a veneer class */\nbool object_veneerclasstotype(objectclass *clss, objecttype *type) {\n    for (int i=0; i<objectdefnnext; i++) {\n        if (_objectdefns[i].veneer==(object *) clss) {\n            if (type) *type = i;\n            return true;\n        }\n    }\n    return false;\n}\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************* */\n\nvoid object_initialize(void) {\n    objectdefnnext=0;\n    \n    morpho_addfinalizefn(object_finalize);\n}\n\nvoid object_finalize(void) {\n    objectdefnnext=0;\n}\n"
  },
  {
    "path": "src/datastructures/object.h",
    "content": "/** @file object.h\n *  @author T J Atherton\n *\n *  @brief Implement objects, heap-allocated data structures with type information\n*/\n\n#ifndef object_h\n#define object_h\n\n#include <stddef.h>\n#include \"value.h\"\n#include \"dictionary.h\"\n\n/* -------------------------------------------------------\n * Fundamental object type\n * ------------------------------------------------------- */\n\n/** Objects are heap-allocated data structures that have a common header allowing type identification.\n    The header should never be accessed directly, but through macros as provided below */\n\n/** The objecttype identifies the type of an object. */\ntypedef int objecttype;\n\n/** Fundamental object structure */\nstruct sobject {\n    objecttype type;            // Type\n    enum {                      // Memory management status for the object:\n        OBJECT_ISUNMANAGED,     // - UNMANAGED means the object is manually alloc'd/dealloc'd\n        OBJECT_ISBUILTIN,       // - BUILTIN means the object was created by the builtin environment\n        OBJECT_ISPROGRAM,       // - PROGRAM means the object is bound to the program\n        OBJECT_ISUNMARKED,      // - UNMARKED means the object is managed by the GC\n        OBJECT_ISMARKED         // - MARKED is used internally by the GC\n    } status;\n    hash hsh;                   // hash value\n    struct sobject *next;       // All objects can be chained together (e.g. to attach to the VM that created them)\n};\n\n/** These macros access the object structure's fields. */\n\n/** Checks if an object is garbage collected */\n#define MORPHO_ISGARBAGECOLLECTED(val)      (MORPHO_GETOBJECT(val)->status>=OBJECT_ISUNMARKED)\n\n/** Gets the type of the object associated with a value\n    @warning: Do not use this to compare types, use an appropriate macro like MORPHO_ISXXX  */\n#define MORPHO_GETOBJECTTYPE(val)           (MORPHO_GETOBJECT(val)->type)\n\n/** Gets an object's key */\n#define MORPHO_GETOBJECTHASH(val)           (MORPHO_GETOBJECT(val)->hsh)\n\n/** Sets an objects key */\n#define MORPHO_SETOBJECTHASH(val, newhash)  (MORPHO_GETOBJECT(val)->hsh = newhash)\n\n/* -------------------------------------------------------\n * object definitions\n * ------------------------------------------------------- */\n\n/** To define a new object type, you must provide several functions that enable the morpho runtime to interact with it.\n    These object definition functions are collected together in an objecttypedefn.\n    The object type is assigned at initialization by calling object_addtype */\n\n/** Called to print a short identifier for the object */\ntypedef void (*objectprintfn) (object *obj, void *v);\n\n/** Called to mark the contents of an object; called by the garbage collector to identify subsidiary objects */\ntypedef void (*objectmarkfn) (object *obj, void *v);\n\n/** Called to free any unmanaged subsidiary data structures for an object */\ntypedef void (*objectfreefn) (object *obj);\n\n/** Called to return the size of an object and attached data (anything NOT stored in a value) */\ntypedef size_t (*objectsizefn) (object *obj);\n\n/** Called to hash an object */\ntypedef hash (*objecthashfn) (object *obj);\n\n/** Called to compare two objects */\ntypedef int (*objectcmpfn) (object *a, object *b);\n/** This function should return one of: */\n#define MORPHO_EQUAL 0\n#define MORPHO_NOTEQUAL 1\n#define MORPHO_BIGGER 1\n#define MORPHO_SMALLER -1\n\n/** Defines a custom object type. */\ntypedef struct {\n    object *veneer; // Veneer class\n    objectfreefn freefn;\n    objectmarkfn markfn;\n    objectsizefn sizefn;\n    objectprintfn printfn;\n    objecthashfn hashfn;\n    objectcmpfn cmpfn;\n} objecttypedefn;\n\n/* -------------------------------------------------------\n * Object creation and management\n * ------------------------------------------------------- */\n\n// Define new object types\nobjecttype object_addtype(objecttypedefn *def);\nobjecttypedefn *object_getdefn(object *obj);\n\n// Management of object structures\nvoid object_init(object *obj, objecttype type);\nvoid object_free(object *obj);\nvoid object_freeifunmanaged(object *obj);\nvoid object_print(void *v, value val);\nvoid object_printtobuffer(value v, varray_char *buffer);\nsize_t object_size(object *obj);\nhash object_hash(object *obj);\nint object_cmp(object *a, object *b);\n\nbool object_istype(value val, objecttype type);\n\n// Create a new object with a specified allocation size and type\nobject *object_new(size_t size, objecttype type);\n\n// Recommended interface to free an object from a value\nvoid morpho_freeobject(value val);\n\n// Index type\ntypedef ptrdiff_t indx;\n\n// Object initialization and finalization\nvoid object_initialize(void);\nvoid object_finalize(void);\n\n#endif /* object_h */\n"
  },
  {
    "path": "src/datastructures/program.c",
    "content": "/** @file program.c\n*  @author T J Atherton\n*\n*  @brief Data structure for a morpho program\n*/\n\n#define MORPHO_CORE\n\n#include \"core.h\"\n#include \"debug.h\"\n#include \"compile.h\"\n#include \"morpho.h\"\n\n/* **********************************************************************\n* Programs\n* ********************************************************************** */\n\nDEFINE_VARRAY(instruction, instruction);\nDEFINE_VARRAY(globalinfo, globalinfo);\n\n/** @brief Initializes a program */\nvoid program_init(program *p) {\n    varray_instructioninit(&p->code);\n    varray_debugannotationinit(&p->annotations);\n    p->global=object_newfunction(MORPHO_PROGRAMSTART, MORPHO_NIL, NULL, 0);\n    p->boundlist=NULL;\n    dictionary_init(&p->symboltable);\n    varray_globalinfoinit(&p->globals);\n    varray_valueinit(&p->classes);\n}\n\n/** @brief Clears a program, freeing associated data structures */\nvoid program_clear(program *p) {\n    if (p->global) object_free((object *) p->global);\n    varray_instructionclear(&p->code);\n    debugannotation_clear(&p->annotations);\n    p->global=NULL;\n    /* Free any objects bound to the program */\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    fprintf(stderr, \"--Freeing objects bound to program.\\n\");\n#endif\n    while (p->boundlist!=NULL) {\n        object *next = p->boundlist->next;\n        object_free(p->boundlist);\n        p->boundlist=next;\n    }\n    #ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n        fprintf(stderr, \"------\\n\");\n    #endif\n    /* Note we don't free the contents as they are already interned */\n    varray_globalinfoclear(&p->globals);\n    dictionary_clear(&p->symboltable);\n    varray_valueclear(&p->classes);\n}\n\n/** @brief Creates and initializes a new program */\nprogram *morpho_newprogram(void) {\n    program *new = MORPHO_MALLOC(sizeof(program));\n\n    if (new) program_init(new);\n\n    return new;\n}\n\n/** @brief Frees a program */\nvoid morpho_freeprogram(program *p) {\n    program_clear(p);\n    MORPHO_FREE(p);\n}\n\n/** Sets the entry point of a program */\nvoid program_setentry(program *p, instructionindx entry) {\n    if (p->global) p->global->entry=entry;\n}\n\n/** Gets the entry point of a program  */\ninstructionindx program_getentry(program *p) {\n    instructionindx out = MORPHO_PROGRAMSTART;\n    if (p->global) out=p->global->entry;\n    return out;\n}\n\n/** @brief Binds an object to a program\n *  @details Objects bound to the program are freed with the program; use for static data (e.g. held in constant tables) */\nvoid program_bindobject(program *p, object *obj) {\n    if (!obj->next && /* Object is not already bound to the program (or something else) */\n        p->boundlist!=obj &&\n        obj->status==OBJECT_ISUNMANAGED) {\n        obj->status=OBJECT_ISPROGRAM;\n        obj->next=p->boundlist;\n        p->boundlist=obj;\n    }\n}\n\n/** @brief Interns a symbol into the programs symbol table.\n *  @details Note that the string is cloned if it does not exist already.\n *           Interning is used to accelerate dynamic lookups as the same string for a symbol will be used universally */\nvalue program_internsymbol(program *p, value symbol) {\n    value new = symbol, out;\n#ifdef MORPHO_DEBUG_SYMBOLTABLE\n    fprintf(stderr, \"Interning symbol '\");\n    morpho_printvalue(NULL, symbol);\n#endif\n    \n    if (builtin_checksymbol(symbol)) { // Check if this is part of the built in symbol table already\n        return builtin_internsymbol(symbol);\n    }\n    \n    if (!dictionary_get(&p->symboltable, symbol, NULL)) {\n       new = object_clonestring(symbol);\n    }\n    \n    out = dictionary_intern(&p->symboltable, new);\n#ifdef MORPHO_DEBUG_SYMBOLTABLE\n    fprintf(stderr, \"' at %p\\n\", (void *) MORPHO_GETOBJECT(out));\n#endif\n    program_bindobject(p, MORPHO_GETOBJECT(out));\n    return out;\n}\n\n/** @brief Adds a global to the program */\nglobalindx program_addglobal(program *p, value symbol) {\n    globalinfo info = { .symbol = program_internsymbol(p, symbol), .type=MORPHO_NIL };\n    \n    return (globalindx) varray_globalinfowrite(&p->globals, info);\n}\n\n/** @brief Sets the type associated with a global variable */\nvoid program_globalsettype(program *p, globalindx indx, value type) {\n    if (indx<0 || indx>p->globals.count) return;\n    \n    p->globals.data[indx].type=type;\n}\n\n/** @brief Gets the type associated with a global variable */\nbool program_globaltype(program *p, globalindx indx, value *type) {\n    if (indx<0 || indx>p->globals.count) return false;\n    *type = p->globals.data[indx].type;\n    return true; \n}\n\n/** @brief Gets the symbol associated with a global variable */\nbool program_globalsymbol(program *p, globalindx indx, value *symbol) {\n    if (indx<0 || indx>p->globals.count) return false;\n    *symbol = p->globals.data[indx].symbol;\n    return true; \n}\n\n/** @brief Returns the number of globals allocated in the program */\nint program_countglobals(program *p) {\n    return p->globals.count;\n}\n\n/** @brief Adds a class to the program's class list */\nint program_addclass(program *p, value klass) {\n    return varray_valuewrite(&p->classes, klass);\n}\n\n/** @brief Returns the number of classes allocated in the program */\nint program_countclasses(program *p, value klass) {\n    return p->classes.count;\n}\n"
  },
  {
    "path": "src/datastructures/program.h",
    "content": "/** @file program.h\n *  @author T J Atherton\n *\n *  @brief Morpho program data structure\n*/\n\n#ifndef program_h\n#define program_h\n\n#include \"varray.h\"\n#include \"object.h\"\n#include \"classes.h\"\n#include \"debugannotation.h\"\n\n#ifdef MORPHO_CORE\n\n/* -------------------------------------------------------\n * Instructions are the basic unit of execution\n * ------------------------------------------------------- */\n\ntypedef unsigned int instruction;\nDECLARE_VARRAY(instruction, instruction);\n\n/** @brief Index into instructions */\ntypedef indx instructionindx;\n\n/* -------------------------------------------------------\n * Global variables\n * ------------------------------------------------------- */\n\n/** @brief Index of a global */\ntypedef int globalindx;\n\n/** @brief Record information about each global variable */\ntypedef struct {\n    value symbol;\n    value type;\n} globalinfo;\n\nDECLARE_VARRAY(globalinfo, globalinfo)\n\n/* -------------------------------------------------------\n * Programs comprise instructions and debugging information\n * ------------------------------------------------------- */\n\n/** @brief Morpho code program and associated data */\ntypedef struct {\n    varray_instruction code; /** Compiled instructions */\n    varray_debugannotation annotations; /** Information about how the code connects to the source */\n    objectfunction *global;  /** Pseudofunction containing global data */\n    varray_globalinfo globals; /** Global variables */\n    varray_value classes; /** Classes defined by this program */\n    object *boundlist; /** Linked list of static objects bound to this program */\n    dictionary symboltable; /** The symbol table */\n} program;\n\n#define MORPHO_PROGRAMSTART 0\nvoid program_setentry(program *p, instructionindx entry);\ninstructionindx program_getentry(program *p);\nvarray_value *program_getconstanttable(program *p);\nvoid program_bindobject(program *p, object *obj);\n\nvalue program_internsymbol(program *p, value symbol);\n\nglobalindx program_addglobal(program *p, value symbol);\nvoid program_globalsettype(program *p, globalindx indx, value type);\nbool program_globaltype(program *p, globalindx indx, value *type);\nbool program_globalsymbol(program *p, globalindx indx, value *symbol);\nint program_countglobals(program *p);\n\nint program_addclass(program *p, value klass);\nint program_countclasses(program *p, value klass);\n\n#endif /* MORPHO_CORE */\n\n#endif /* error_h */\n"
  },
  {
    "path": "src/datastructures/signature.c",
    "content": "/** @file signature.c\n *  @author T J Atherton\n *\n *  @brief Function signatures and their declarations\n*/\n\n#include \"classes.h\"\n\n#include \"signature.h\"\n#include \"parse.h\"\n\n/* **********************************************************************\n * Manage signature structures\n * ********************************************************************** */\n\nvoid signature_init(signature *s) {\n    varray_valueinit(&s->types);\n    s->ret=MORPHO_NIL;\n    s->varg=false;\n}\n\nvoid signature_clear(signature *s) {\n    varray_valueclear(&s->types);\n}\n\n/** @brief Sets the contents of a signature\n *  @param[in] s - the signature structure\n *  @param[in] nparam - number of fixed parameters\n *  @param[in] types - list of types of each parameter */\nvoid signature_set(signature *s, int nparam, value *types) {\n    s->types.count=0; // Reset\n    varray_valueadd(&s->types, types, nparam);\n}\n\n/** @brief Sets whether a signature contains variadic arguments */\nvoid signature_setvarg(signature *s, bool varg) {\n    s->varg=varg;\n}\n\n/** @brief Sets whether a signature contains variadic arguments */\nbool signature_isvarg(signature *s) {\n    return s->varg;\n}\n\n/** @brief Returns true if any entries in the signature are typed*/\nbool signature_istyped(signature *s) {\n    for (int i=0; i<s->types.count; i++) if (!MORPHO_ISNIL(s->types.data[i])) return true;\n    return false;\n}\n\n/** @brief Check if two signatures are equal */\nbool signature_isequal(signature *a, signature *b) {\n    if (a->types.count!=b->types.count) return false;\n    for (int i=0; i<a->types.count; i++) if (!MORPHO_ISEQUAL(a->types.data[i], b->types.data[i])) return false;\n    return true; \n}\n\n/** @brief Return list of types */\nbool signature_paramlist(signature *s, int *nparams, value **ptypes) {\n    if (nparams) *nparams = s->types.count;\n    if (ptypes) *ptypes = s->types.data;\n    return s->types.data;\n}\n\n/** @brief Returns the type of the i'th parameter, if it exists */\nbool signature_getparamtype(signature *s, int i, value *type) {\n    if (i>=s->types.count) return false;\n    if (type) *type = s->types.data[i];\n    return true; \n}\n\n/** @brief Returns the return type from the signature if defined */\nvalue signature_getreturntype(signature *s) {\n    return s->ret;\n}\n\n/** @brief Count the number of parameters in a signature */\nint signature_countparams(signature *s) {\n    return s->types.count;\n}\n\n/* **********************************************************************\n * Parse signatures\n * ********************************************************************** */\n\nenum {\n    SIGNATURE_LEFTBRACE,\n    SIGNATURE_RIGHTBRACE,\n    SIGNATURE_COMMA,\n    SIGNATURE_DOTDOTDOT,\n    SIGNATURE_SYMBOL,\n    SIGNATURE_EOF\n};\n\ntokendefn sigtokens[] = {\n    { \"(\",          SIGNATURE_LEFTBRACE         , NULL },\n    { \")\",          SIGNATURE_RIGHTBRACE        , NULL },\n    { \",\",          SIGNATURE_COMMA             , NULL },\n    { \"...\",        SIGNATURE_DOTDOTDOT         , NULL },\n    { \"\",           SIGNATURE_EOF               , NULL }\n};\n\n/** @brief Initializes a lexer for parsing signatures */\nvoid signature_initializelexer(lexer *l, char *signature) {\n    lex_init(l, signature, 0);\n    lex_settokendefns(l, sigtokens);\n    lex_seteof(l, SIGNATURE_EOF);\n    lex_setsymboltype(l, SIGNATURE_SYMBOL);\n}\n\n/** @brief Parses a type name held in the parser's previous type */\nbool signature_parsetype(parser *p, value *type) {\n    value symbol;\n    if (!parse_stringfromtoken(p, 0, p->previous.length, &symbol)) return false;\n    value klass = builtin_findclass(symbol);\n    morpho_freeobject(symbol);\n    \n    if (MORPHO_ISCLASS(klass)) *type=klass;\n    return MORPHO_ISCLASS(klass);\n}\n\n/** @brief Parser function to process a symbol held in p->previous */\nbool signature_parsesymbol(parser *p, void *out) {\n    signature *sig = (signature *) out;\n    bool success=false;\n    \n    if (p->previous.length==1 && *p->previous.start=='_') {\n        value blank = MORPHO_NIL;\n        success=varray_valueadd(&sig->types, &blank, 1);\n    } else {\n        value type;\n        if (signature_parsetype(p, &type)) success=varray_valueadd(&sig->types, &type, 1);\n    }\n    return success;\n}\n\n/** @brief Parser function to process varg */\nbool signature_parsevarg(parser *p, void *out) {\n    signature *sig = (signature *) out;\n    \n    value blank = MORPHO_NIL;\n    bool success=varray_valueadd(&sig->types, &blank, 1);\n    sig->varg=true;\n    \n    return success;\n}\n\n/** @brief Main parser function for signatures */\nbool signature_parsesignature(parser *p, void *out) {\n    signature *sig = (signature *) out;\n    \n    if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) {\n        value type;\n        if (signature_parsetype(p, &type)) sig->ret=type;\n    }\n    \n    if (!parse_checktokenadvance(p, SIGNATURE_LEFTBRACE)) return false;\n    \n    while (!parse_checktoken(p, SIGNATURE_RIGHTBRACE) &&\n           !parse_checktoken(p, SIGNATURE_EOF)) {\n        if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) {\n            if (!signature_parsesymbol(p, out)) return false;\n        } else if (parse_checktokenadvance(p, SIGNATURE_DOTDOTDOT)) {\n            if (!signature_parsevarg(p, out)) return false; \n        } else return false;\n        \n        parse_checktokenadvance(p, SIGNATURE_COMMA);\n    }\n    \n    if (!parse_checktokenadvance(p, SIGNATURE_RIGHTBRACE)) return false;\n    \n    return true;\n}\n\n/** Parses a signature */\nbool signature_parse(char *sig, signature *out) {\n    error err;\n    error_init(&err);\n    \n    lexer l;\n    signature_initializelexer(&l, sig);\n    \n    parser p;\n    parse_init(&p, &l, &err, out);\n    parse_setbaseparsefn(&p, signature_parsesignature);\n    parse_setskipnewline(&p, false, TOKEN_NONE);\n    \n    bool success=parse(&p);\n    \n    parse_clear(&p);\n    lex_clear(&l);\n    \n    return success;\n}\n\n/** Print a signature for debugging purposes */\nvoid signature_print(signature *s) {\n    printf(\"(\");\n    for (int i=0; i<s->types.count; i++) {\n        value type=s->types.data[i];\n        \n        if (s->varg && i==s->types.count-1) printf(\"...\");\n        else if (MORPHO_ISNIL(type)) printf(\"_\");\n        else if (MORPHO_ISCLASS(type)) morpho_printvalue(NULL, MORPHO_GETCLASS(type)->name);\n        \n        if (i<s->types.count-1) printf(\",\");\n    }\n    printf(\")\");\n}\n"
  },
  {
    "path": "src/datastructures/signature.h",
    "content": "/** @file signature.h\n *  @author T J Atherton\n *\n *  @brief Function signatures and their declarations\n*/\n\n#ifndef signature_h\n#define signature_h\n\n#include <stdbool.h>\n#include \"varray.h\"\n\ntypedef struct {\n    varray_value types; /** Signature of parameters */\n    value ret; /** Return type */\n    bool varg; /** Is the function variadic? */\n} signature;\n\nvoid signature_init(signature *s);\nvoid signature_clear(signature *s);\n\nvoid signature_setvarg(signature *s, bool varg);\nbool signature_isvarg(signature *s);\n\nbool signature_istyped(signature *s);\nbool signature_isequal(signature *a, signature *b);\nbool signature_paramlist(signature *s, int *nparams, value **ptypes);\nbool signature_getparamtype(signature *s, int i, value *type);\nvalue signature_getreturntype(signature *s);\nint signature_countparams(signature *s);\n\nvoid signature_set(signature *s, int nparam, value *types);\nbool signature_parse(char *sig, signature *out);\n\nvoid signature_print(signature *s);\n\n#endif\n"
  },
  {
    "path": "src/datastructures/syntaxtree.c",
    "content": "/** @file syntaxtree.c\n *  @author T J Atherton\n *\n *  @brief Syntax tree data structure for morpho\n*/\n\n#include <stdio.h>\n#include \"syntaxtree.h\"\n#include \"common.h\"\n\nDEFINE_VARRAY(syntaxtreenode, syntaxtreenode);\nDEFINE_VARRAY(syntaxtreeindx, syntaxtreeindx);\n\n/** @brief Initialize a syntax tree */\nvoid syntaxtree_init(syntaxtree *tree) {\n    varray_syntaxtreenodeinit(&tree->tree);\n    tree->entry=0;\n}\n\n/** @brief Wipe a syntax tree, not freeing attached objects */\nvoid syntaxtree_wipe(syntaxtree *tree) {\n    tree->tree.count=0;\n}\n\n/** @brief Finalize a syntax tree */\nvoid syntaxtree_clear(syntaxtree *tree) {\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    fprintf(stderr, \"--Freeing syntax tree %p.\\n\",(void *) tree);\n#endif\n    /** Free attached objects */\n    for (unsigned int i=0; i<tree->tree.count; i++) {\n        syntaxtreenode *node = &tree->tree.data[i];\n        if (MORPHO_ISOBJECT(node->content)) {\n            object_free(MORPHO_GETOBJECT(node->content));\n        }\n    }\n    varray_syntaxtreenodeclear(&tree->tree);\n#ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR\n    fprintf(stderr, \"------\\n\");\n#endif\n}\n\n#ifdef MORPHO_DEBUG\nstatic char * nodedisplay[] = {\n    \"\",        // NODE_BASE\n    \n    \"\",        // NODE_NIL\n    \"\",        // NODE_BOOL\n    \"\",        // NODE_FLOAT,\n    \"\",        // NODE_INTEGER,\n    \"\",        // NODE_STRING,\n    \"\",        // NODE_SYMBOL,\n    \"self\",    // NODE_SELF,\n    \"super\",   // NODE_SUPER,\n    \"im\",      // NODE_IMAG,\n    \n    \"\",        // NODE_LEAF, /* ^ All leafs should be above this enum value */\n    \n    \"-\",       // NODE_NEGATE,\n    \"!\",       // NODE_NOT,\n    \n    \"\",        // NODE_UNARY, /* ^ All unary operators should be above this enum value */\n    \n    \"+\",       // NODE_ADD,\n    \"-\",       // NODE_SUBTRACT,\n    \"*\",       // NODE_MULTIPLY,\n    \"/\",       // NODE_DIVIDE,\n    \"^\",       // NODE_POW,\n    \n    \"=\",       // NODE_ASSIGN,\n    \n    \"==\",      // NODE_EQ,\n    \"!=\",      // NODE_NEQ,\n    \"<\",       // NODE_LT,\n    \">\",       // NODE_GT,\n    \"<=\",      // NODE_LTEQ,\n    \">=\",      // NODE_GTEQ,\n    \n    \"and\",     // NODE_AND\n    \"or\",      // NODE_OR\n    \n    \"?\",       // NODE_TERNARY\n    \n    \".\",       // NODE_DOT\n    \n    \"..\",      // NODE_INCLUSIVERANGE\n    \"...\",     // NODE_RANGE\n    \n    \"\",        // NODE_OPERATOR, /* ^ All operators should be above this enum value */\n    \n    \"print\",   // NODE_PRINT\n    \":=\",      // NODE_DECLARATION\n    \"type\",    // NODE_TYPE\n    \"fn\",      // NODE_FUNCTION\n    \"\",        // NODE_METHOD\n    \"class\",   // NODE_CLASS\n    \"return\",  // NODE_RETURN\n    \"if\",      // NODE_IF\n    \"else\",    // NODE_THEN\n    \"while\",   // NODE_WHILE\n    \"for\",     // NODE_FOR\n    \"do\",      // NODE_DO\n    \"in\",      // NODE_IN\n    \"break\",   // NODE_BREAK\n    \"continue\",// NODE_CONTINUE\n    \"try\",     // NODE_TRY\n    \n    \"\",        // NODE_STATEMENT\n    \n    \"()\",      // NODE_GROUPING\n    \";\",       // NODE_SEQUENCE\n    \"dict\",    // NODE_DICTIONARY\n    \"keyval\",  // NODE_DICTENTRY\n    \"\\\"\",      // NODE_INTERPOLATION\n    \"arglist\", // NODE_ARGLIST\n    \"{}\",      // NODE_SCOPE\n    \"call\",    // NODE_CALL\n    \"index\",   // NODE_INDEX\n    \"list\",    // NODE_LIST\n    \"tuple\",   // NODE_TUPLE\n    \"import\",  // NODE_IMPORT\n    \"as\",      // NODE_AS\n    \"@\",       // NODE_BREAKPOINT\n    \n    \"\",        // NODE_STRUCTURAL\n};\n\n/** @brief Prints a single node of a syntax tree with indentation\n * @param base     pointer to head node\n * @param i        index of this node\n * @param indent   indentation level */\nvoid syntaxtree_printnode(syntaxtreenode *base, syntaxtreeindx i, unsigned int indent) {\n    syntaxtreenode *node = base + i;\n    for (unsigned int i=0; i<indent; i++) printf(\"  \");\n    if (!SYNTAXTREE_ISLEAF(node->type)) {\n        char *display = nodedisplay[node->type];\n        \n        if (display) printf(\"%s\",display);\n        else UNREACHABLE(\"print syntax tree node type [Operator print rule not implemented]\");\n    } \n    \n    if (SYNTAXTREE_ISLEAF(node->type)) {\n        if (node->type==NODE_SELF || node->type==NODE_SUPER) {\n            char *display = nodedisplay[node->type];\n            if (display) printf(\"%s\\n\",display);\n        } else {\n            morpho_printvalue(NULL, node->content);\n            printf(\"\\n\");\n        }\n    } else {\n        if (node->type==NODE_FUNCTION || node->type==NODE_CLASS) {\n            printf(\" \");\n            morpho_printvalue(NULL, node->content);\n        } else if (node->type==NODE_INTERPOLATION) {\n            printf(\"\\\" '\");\n            morpho_printvalue(NULL, node->content);\n            printf(\"'\");\n        }\n        printf(\"\\n\");\n        if (node->left!=SYNTAXTREE_UNCONNECTED) syntaxtree_printnode(base, node->left,indent+1);\n        if (!SYNTAXTREE_ISUNARY(node->type)) {\n            if (node->right!=SYNTAXTREE_UNCONNECTED) {\n                syntaxtree_printnode(base, node->right,indent+1);\n            } else {\n                for (unsigned int i=0; i<indent+1; i++) printf(\"  \");\n                printf(\"-\\n\");\n            }\n        }\n    }\n}\n\n/** @brief Prints a syntax tree */\nvoid syntaxtree_print(syntaxtree *tree) {\n    if (tree->tree.count && tree->entry!=SYNTAXTREE_UNCONNECTED) syntaxtree_printnode(tree->tree.data, tree->entry, 0);\n}\n#endif\n\n/** @brief Adds a node to the syntax tree\n *  @param tree    tree to add to.\n *  @param type    type of node to add\n *  @param content a value to add\n *  @param left    } left ...\n *  @param right   } ...and right branches of the node.\n */\nbool syntaxtree_addnode(syntaxtree *tree, syntaxtreenodetype type, value content, int line, int posn, syntaxtreeindx left, syntaxtreeindx right, syntaxtreeindx *out) {\n    syntaxtreenode new = {.content=content, .left = left, .right = right, .type = type, .line=line, .posn=posn};\n    \n    if (!varray_syntaxtreenodeadd(&tree->tree, &new, 1)) return false;\n    \n    *out = tree->tree.count-1;\n    return true;\n}\n\n/** Gets a syntaxtree node from its index */\nsyntaxtreenode *syntaxtree_nodefromindx(syntaxtree *tree, syntaxtreeindx indx) {\n    return tree->tree.data+indx;\n}\n\n/* @brief Flattens a tree into a list of node indices\n * @param[in] tree - the syntaxtree to traverse\n * @param[in] node - the starting node\n * @param[in] ntypes - number of node types to match (these will be flattened)\n * @param[in] types - list of node types to flatten\n * @param[in/out] list - list of nodes flattened from this node */\nvoid syntaxtree_flatten(syntaxtree *tree, syntaxtreeindx indx, unsigned int ntypes, syntaxtreenodetype *types, varray_syntaxtreeindx *list) {\n    if (indx==SYNTAXTREE_UNCONNECTED) return; \n    syntaxtreenode *node = syntaxtree_nodefromindx(tree, indx);\n    if (!node) return;\n    \n    bool isdesiredtype=false;\n    for (unsigned int i=0; i<ntypes; i++) {\n        if (node->type==types[i]) isdesiredtype=true;\n    }\n    \n    if (isdesiredtype) {\n        if (node->left!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(tree, node->left, ntypes, types, list);\n        if (node->right!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(tree, node->right, ntypes, types, list);\n    } else {\n        varray_syntaxtreeindxadd(list, &indx, 1);\n    }\n}\n"
  },
  {
    "path": "src/datastructures/syntaxtree.h",
    "content": "/** @file syntaxtree.h\n *  @author T J Atherton\n *\n *  @brief Syntax tree data structure for morpho\n*/\n\n#ifndef syntaxtree_h\n#define syntaxtree_h\n\n#include <stddef.h>\n#include \"value.h\"\n#include \"varray.h\"\n\n\n/** Syntax trees in morpho are binary trees: they consist of nodes, which may contain a value and reference two child elements */\n\n/* -------------------------------------------------------\n * Nodes\n * ------------------------------------------------------- */\n\n/** Syntax tree node types are left as a generic int to facilitate reprogrammability */\ntypedef int syntaxtreenodetype;\n\n/** @brief A reference to an element of the tree is by an index */\ntypedef ptrdiff_t syntaxtreeindx;\n\n/** @brief A node on the syntax tree */\ntypedef struct _syntaxtreenode {\n    syntaxtreenodetype type; /** Type of node */\n    \n    value content; /** A value that represents the node contents */\n    syntaxtreeindx left; /** left child element */\n    syntaxtreeindx right; /** right child element */\n    \n    int line; /** line in the source that the element was created from. */\n    int posn; /** column in the source that the element was created from. */\n} syntaxtreenode;\n\n/** Macro used to represent unconnected nodes  */\n#define SYNTAXTREE_UNCONNECTED -1\n\n/* -------------------------------------------------------\n * Syntax tree\n * ------------------------------------------------------- */\n\nDECLARE_VARRAY(syntaxtreenode, syntaxtreenode);\nDECLARE_VARRAY(syntaxtreeindx, syntaxtreeindx);\n\n/** @brief A syntax tree data structure */\ntypedef struct {\n    varray_syntaxtreenode tree; /** List of nodes */\n    syntaxtreeindx entry; /** Entry point into the syntax tree */\n} syntaxtree;\n\n/* -------------------------------------------------------\n * Node types\n * ------------------------------------------------------- */\n\n/** @brief Type of node */\nenum {\n    NODE_BASE,\n    \n    NODE_NIL,\n    NODE_BOOL,\n    NODE_FLOAT,\n    NODE_INTEGER,\n    NODE_STRING,\n    NODE_SYMBOL,\n    NODE_SELF,\n    NODE_SUPER, \n    NODE_IMAG,\n    \n    NODE_LEAF, /* ^ All leafs should be above this enum value */\n    \n    NODE_NEGATE,\n    NODE_NOT,\n    \n    NODE_UNARY, /* ^ All unary operators should be above this enum value */\n    \n    NODE_ADD,\n    NODE_SUBTRACT,\n    NODE_MULTIPLY,\n    NODE_DIVIDE,\n    NODE_POW,\n    \n    NODE_ASSIGN,\n    \n    NODE_EQ,\n    NODE_NEQ,\n    NODE_LT,\n    NODE_GT,\n    NODE_LTEQ,\n    NODE_GTEQ,\n    \n    NODE_AND,\n    NODE_OR,\n    \n    NODE_TERNARY,\n\n    NODE_DOT,\n    \n    NODE_RANGE,\n    NODE_INCLUSIVERANGE,\n    \n    NODE_OPERATOR, /* ^ All operators should be above this enum value */\n    \n    NODE_PRINT,\n    NODE_DECLARATION,\n    NODE_TYPE,\n    NODE_FUNCTION,\n    NODE_METHOD,\n    NODE_CLASS,\n    NODE_RETURN,\n    NODE_IF,\n    NODE_THEN,\n    NODE_WHILE,\n    NODE_FOR,\n    NODE_DO,\n    NODE_IN,\n    NODE_BREAK,\n    NODE_CONTINUE,\n    NODE_TRY,\n    \n    NODE_STATEMENT, /* ^ All statements should be above this enum value */\n    \n    NODE_GROUPING,\n    NODE_SEQUENCE,\n    NODE_DICTIONARY,\n    NODE_DICTENTRY,\n    NODE_INTERPOLATION,\n    NODE_ARGLIST,\n    NODE_SCOPE,\n    NODE_CALL,\n    NODE_INDEX,\n    NODE_LIST,\n    NODE_TUPLE,\n    NODE_IMPORT,\n    NODE_AS,\n    NODE_BREAKPOINT\n};\n\n/* -------------------------------------------------------\n * Macros to check node types\n * ------------------------------------------------------- */\n\n/** @brief Check if a node type lies between two values */\nstatic inline bool syntaxtree_istype(syntaxtreenodetype type, syntaxtreenodetype lower, syntaxtreenodetype upper) {\n    return ((type > lower) && (type < upper));\n}\n\n/** Check if a node is a leaf, unary operator, binary operator or a statement */\n#define SYNTAXTREE_ISLEAF(x) syntaxtree_istype(x, NODE_BASE, NODE_LEAF)\n#define SYNTAXTREE_ISUNARY(x) syntaxtree_istype(x, NODE_LEAF, NODE_UNARY)\n#define SYNTAXTREE_ISOPERATOR(x) syntaxtree_istype(x, NODE_UNARY, NODE_OPERATOR)\n#define SYNTAXTREE_ISSTATEMENT(x) syntaxtree_istype(x, NODE_OPERATOR, NODE_STATEMENT)\n\n/* -------------------------------------------------------\n * Interface\n * ------------------------------------------------------- */\n\nvoid syntaxtree_init(syntaxtree *tree);\nvoid syntaxtree_wipe(syntaxtree *tree);\nvoid syntaxtree_clear(syntaxtree *tree);\nvoid syntaxtree_print(syntaxtree *tree);\n\nbool syntaxtree_addnode(syntaxtree *tree, syntaxtreenodetype type, value content, int line, int posn, syntaxtreeindx left, syntaxtreeindx right, syntaxtreeindx *out);\n\nsyntaxtreenode *syntaxtree_nodefromindx(syntaxtree *tree, syntaxtreeindx indx);\n\nvoid syntaxtree_flatten(syntaxtree *tree, syntaxtreeindx indx, unsigned int ntypes, syntaxtreenodetype *types, varray_syntaxtreeindx *list);\n\n#endif /* syntaxtree_h */\n"
  },
  {
    "path": "src/datastructures/value.c",
    "content": "/** @file value.c\n *  @author T J Atherton\n *\n *  @brief Fundamental data type for morpho\n*/\n\n#include <float.h>\n\n#include \"value.h\"\n#include \"common.h\"\n\n/* **********************************************************************\n* Comparison of values\n* ********************************************************************** */\n\n/** @brief Compares where two values are the same, i.e. are identical or refer to the same object. \n * @details Faster than morpho_comparevalue\n * @param a value to compare\n * @param b value to compare\n * @returns true if a and b are identical, false otherwise */\nbool morpho_issame(value a, value b) {\n#ifdef MORPHO_NAN_BOXING\n    return (a==b);\n#else\n    if (a.type!=b.type) return false;\n\n    switch (a.type) {\n        case VALUE_NIL:\n            return true; /** Nils are always the same */\n        case VALUE_INTEGER:\n            return (b.as.integer == a.as.integer);\n        case VALUE_DOUBLE:\n            /* The sign bit comparison is required to distinguish between -0 and 0. */\n            return ((b.as.real == a.as.real) && (signbit(b.as.real)==signbit(a.as.real)));\n        case VALUE_BOOL:\n            return (b.as.boolean == a.as.boolean);\n        case VALUE_OBJECT:\n            return MORPHO_GETOBJECT(a) == MORPHO_GETOBJECT(b);\n        default:\n            UNREACHABLE(\"unhandled value type for comparison [Check morpho_issame]\");\n    }\n\n    return false;\n#endif\n}\n\n/**  @brief Compare two doubles for equality using both absolute and relative tolerances */\nbool morpho_doubleeqtest(double a, double b) {\n    if (a==b) return true; \n    double diff = fabs(a-b);\n    double absa = fabs(a), absb=fabs(b);\n    double absmax = (absa>absb ? absa : absb);\n    return (diff == 0.0) || (absmax > DBL_MIN && diff/absmax <= MORPHO_RELATIVE_EPS);\n}\n\n/** @brief Compares two values\n * @param a value to compare\n * @param b value to compare\n * @returns 0 if a and b are equal, a positive number if b\\>a and a negative number if a\\<b\n * @warning Requires that both values have the same type */\nint morpho_comparevalue(value a, value b) {\n    if (!morpho_ofsametype(a, b)) return MORPHO_NOTEQUAL;\n    \n    if (MORPHO_ISFLOAT(a)) {\n        double aa = MORPHO_GETFLOATVALUE(a);\n        double bb = MORPHO_GETFLOATVALUE(b);\n        if (morpho_doubleeqtest(aa,bb)) return MORPHO_EQUAL;\n        return (bb>aa ? MORPHO_BIGGER : MORPHO_SMALLER);\n    } else {\n        switch (MORPHO_GETTYPE(a)) {\n            case VALUE_NIL:\n                return MORPHO_EQUAL; /** Nones are always the same */\n            case VALUE_INTEGER: {\n                int aa = MORPHO_GETINTEGERVALUE(a);\n                int bb = MORPHO_GETINTEGERVALUE(b);\n                if (aa==bb) return MORPHO_EQUAL;\n                else return (bb>aa ? MORPHO_BIGGER : MORPHO_SMALLER);\n            }\n            case VALUE_BOOL:\n                return (MORPHO_GETBOOLVALUE(b) != MORPHO_GETBOOLVALUE(a));\n            case VALUE_OBJECT:\n                if (MORPHO_GETOBJECTTYPE(a)!=MORPHO_GETOBJECTTYPE(b)) {\n                    return 1; /* Objects of different type are always different */\n                } else return object_cmp(MORPHO_GETOBJECT(a), MORPHO_GETOBJECT(b));\n            default:\n                UNREACHABLE(\"unhandled value type for comparison [Check morpho_comparevalue]\");\n        }\n    }\n    \n    return MORPHO_NOTEQUAL;\n}\n\n/** @brief Compares two values, even for inequivalent values e.g. int to float\n * @param a value to compare\n * @param b value to compare\n * @returns 0 if a and b are equal, a positive number if b\\>a and a negative number if a\\<b*/\nint morpho_extendedcomparevalue(value a, value b) {\n    if (morpho_ofsametype(a, b)) return morpho_comparevalue(a, b);\n    \n    value aa=a, bb=b;\n    \n    if (MORPHO_ISINTEGER(a) && MORPHO_ISFLOAT(b)) {\n        aa = MORPHO_INTEGERTOFLOAT(aa);\n        return morpho_comparevalue(aa, bb);\n    } else if (MORPHO_ISFLOAT(a) && MORPHO_ISINTEGER(b)) {\n        bb = MORPHO_INTEGERTOFLOAT(bb);\n        return morpho_comparevalue(aa, bb);\n    } else if (MORPHO_ISCOMPLEX(bb) && MORPHO_ISNUMBER(aa)) {\n        aa=b; bb=a;\n    }\n    \n    if (MORPHO_ISCOMPLEX(aa) && MORPHO_ISNUMBER(bb)) {\n        MorphoComplex z = MORPHO_GETDOUBLECOMPLEX(aa);\n        if (fabs(cimag(z)) < cabs(z)*MORPHO_RELATIVE_EPS) { // Ensure imaginary part is zero\n            aa=MORPHO_FLOAT(creal(z));\n            double real;\n            morpho_valuetofloat(bb, &real);\n            bb=MORPHO_FLOAT(real);\n        } else return MORPHO_NOTEQUAL;\n        return morpho_comparevalue(aa, bb);\n    }\n    \n    return MORPHO_NOTEQUAL;\n}\n\n/* **********************************************************************\n* Type check and conversion\n* ********************************************************************** */\n\n/** Detect if a value is a number */\nbool morpho_isnumber(value a) {\n    return (MORPHO_ISINTEGER(a) || MORPHO_ISFLOAT(a));\n}\n\n/** Define notion of falsity/truthyness */\nbool morpho_isfalse(value a) {\n    return (MORPHO_ISNIL(a) || (MORPHO_ISBOOL(a) && (MORPHO_GETBOOLVALUE(a)==false)));\n}\n\n/** Convert a value to an integer */\nbool morpho_valuetoint(value v, int *out) {\n    if (MORPHO_ISINTEGER(v)) { *out = MORPHO_GETINTEGERVALUE(v); return true; }\n    if (MORPHO_ISFLOAT(v)) { *out = (int) MORPHO_GETFLOATVALUE(v); return true; }\n    return false;\n}\n\n/** Convert a value to a float */\nbool morpho_valuetofloat(value v, double *out) {\n    if (MORPHO_ISINTEGER(v)) { *out = (double) MORPHO_GETINTEGERVALUE(v); return true; }\n    if (MORPHO_ISFLOAT(v)) { *out = MORPHO_GETFLOATVALUE(v); return true; }\n    return false;\n}\n\n/* **********************************************************************\n* Utility functions\n* ********************************************************************** */\n\n/** Promotes a list of numbers to floats if any are floating point.\n * @param[in] nv - number of values\n * @param[in] v  - list of values\n * @returns true if successful, false if any values are not numbers */\nbool value_promotenumberlist(unsigned int nv, value *v) {\n    bool fl=false;\n    for (unsigned int i=0; i<nv; i++) {\n        if (!MORPHO_ISNUMBER(v[i])) return false;\n        if (MORPHO_ISFLOAT(v[i])) fl=true;\n    }\n    \n    if (fl) {\n        for (unsigned int i=0; i<nv; i++) {\n            if (MORPHO_ISINTEGER(v[i])) v[i]=MORPHO_FLOAT((double) MORPHO_GETINTEGERVALUE(v[i]));\n        }\n    }\n    return true;\n}\n\n/** Finds the maximum and minimum of a list of values */\nbool value_minmax(unsigned int nval, value *list, value *min, value *max) {\n    if (nval==0) return false;\n    \n    if (min) *min=list[0];\n    if (max) *max=list[0];\n    \n    for (unsigned int i=1; i<nval; i++) {\n        if (min) {\n            value l=*min, r=list[i];\n            if (morpho_extendedcomparevalue(l, r)<0) *min = list[i];\n        }\n        \n        if (max) {\n            value l=*max, r=list[i];\n            if (morpho_extendedcomparevalue(l, r)>0) *max = list[i];\n        }\n    }\n    \n    return true;\n}\n\n/* **********************************************************************\n* Varray_values and utility functions\n* ********************************************************************** */\n\nDEFINE_VARRAY(value, value);\n\n/** @brief Finds a value in an varray using a loose equality test (MORPHO_ISEQUAL)\n *  @param[in]  varray     the array to search\n *  @param[in]  v          value to find\n *  @param[out] out        index of the match\n *  @returns whether the value was found or not. */\nbool varray_valuefind(varray_value *varray, value v, unsigned int *out) {\n    for (unsigned int i=0; i<varray->count; i++) {\n        if (MORPHO_ISEQUAL(varray->data[i], v)) {\n            if (out) *out=i;\n            return true;\n        }\n    }\n    return false;\n}\n\n/** @brief Finds a value in an varray using strict equality test (MORPHO_ISSAME)\n *  @param[in]  varray     the array to search\n *  @param[in]  v          value to find\n *  @param[out] out        index of the match\n *  @returns whether the value was found or not. */\nbool varray_valuefindsame(varray_value *varray, value v, unsigned int *out) {\n    for (unsigned int i=0; i<varray->count; i++) {\n        if (MORPHO_ISSAME(varray->data[i], v)) {\n            if (out) *out=i;\n            return true;\n        }\n    }\n    return false;\n}\n\n/* **********************************************************************\n * Veneer classes\n * ********************************************************************** */\n\nobjectclass *_valueveneers[MORPHO_MAXIMUMVALUETYPES];\n\n/** @brief Sets the veneer class for a particular value type */\nvoid value_setveneerclass(value type, value clss) {\n    if (!MORPHO_ISCLASS(clss)) {\n        UNREACHABLE(\"Veneer class must be a class.\");\n    }\n    \n    if (MORPHO_ISOBJECT(type)) {\n        UNREACHABLE(\"Cannot define a veneer class for generic objects.\");\n    } else if (MORPHO_ISFLOAT(type)) {\n        _valueveneers[0]=MORPHO_GETCLASS(clss);\n    } else {\n        int k = MORPHO_GETORDEREDTYPE(type);\n        _valueveneers[k]=MORPHO_GETCLASS(clss);\n    }\n}\n\n/** @brief Gets the veneer class for a particular value type */\nobjectclass *value_getveneerclass(value type) {\n    if (MORPHO_ISFLOAT(type)) {\n        return _valueveneers[0];\n    } else {\n        int k = MORPHO_GETORDEREDTYPE(type);\n        return _valueveneers[k];\n    }\n}\n\n/** @brief Returns the veneer class given the type index */\nobjectclass *value_veneerclassfromtype(int type) {\n    if (type<MORPHO_MAXIMUMVALUETYPES) {\n        return _valueveneers[type];\n    } else return NULL;\n}\n\n/** @brief Returns an type index for the class */\nbool value_veneerclasstotype(objectclass *clss, int *type) {\n    for (int i=0; i<MORPHO_MAXIMUMVALUETYPES; i++) {\n        if (_valueveneers[i]==clss) {\n            if (type) *type = i;\n            return true;\n        }\n    }\n    return false;\n}\n\n/* **********************************************************************\n * Initialization/Finalization\n * ********************************************************************** */\n\nvoid value_initialize(void) {\n    for (int i=0; i<MORPHO_MAXIMUMVALUETYPES; i++) _valueveneers[i]=NULL;\n}\n"
  },
  {
    "path": "src/datastructures/value.h",
    "content": "/** @file value.h\n *  @author T J Atherton\n *\n *  @brief Fundamental data type for morpho\n*/\n\n#ifndef value_h\n#define value_h\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <math.h>\n\n#include \"build.h\"\n#include \"varray.h\"\n\n/* Forward declarations of object structures */\ntypedef struct sobject object;\n\n/* -------------------------------------------------------\n * Fundamental value type\n * ------------------------------------------------------- */\n\n/** Values are the basic data type in morpho: each variable declared with 'var' corresponds to one value.\n    Values can contain the following types:\n        VALUE_NIL           - nil\n        VALUE_INTEGER - 32 bit integer\n        VALUE_DOUBLE  -\n        VALUE_BOOL       - boolean type\n        VALUE_OBJECT   - pointer to an object\n    The implementation of a value is intentionally opaque and can be NAN boxed into a 64-bit double or left as a struct.\n    This file therefore defines several kinds of macro to:\n        * create values of a given type, e.g. MORPHO_INTEGER.\n        * Test the type of a value, e.g. MORPHO_ISINTEGER\n        * Extract a given type from a value and cast to the relevant C type, e.g. MORPHO_GETINTEGERVALUE */\n\n/** NAN Boxing represents a value as a double, using the values that correspond to NAN to contain the remaining types. */\n#ifdef MORPHO_NAN_BOXING\n\n/** In this representation, we can extract non-double types from a 64 bit integer */\ntypedef uint64_t value;\n\n/** Define macros that enable us to refer to various bits */\n#define SIGN_BIT    ((uint64_t) 0x8000000000000000)\n#define QNAN        ((uint64_t) 0x7ffc000000000000)\n#define LOWER_WORD  ((uint64_t) 0x00000000ffffffff)\n\n/** Store the type in bits 47-49 */\n#define TAG_NIL     (1ull<<47) // 001\n#define TAG_BOOL    (2ull<<47) // 010\n#define TAG_INT     (3ull<<47) // 011\n#define TAG_OBJ     SIGN_BIT\n\n/** Bool values are stored in the lowest bit */\n#define TAG_TRUE    1\n#define TAG_FALSE   0\n\n/** Bit mask used to select type bits */\n#define TYPE_BITS (TAG_OBJ | TAG_NIL | TAG_BOOL | TAG_INT)\n\n/** Map VALUE_XXX macros to type bits  */\n#define VALUE_NIL       (TAG_NIL)\n#define VALUE_INTEGER   (TAG_INT)\n#define VALUE_DOUBLE    ()\n#define VALUE_BOOL      (TAG_BOOL)\n#define VALUE_OBJECT    (TAG_OBJ)\n\n/** Get the type from a value */\n#define MORPHO_GETTYPE(x)  ((x) & TYPE_BITS)\n\n/** Union to enable conversion of a double to a 64 bit integer */\ntypedef union {\n    uint64_t bits;\n    double num;\n} doubleunion;\n\n/** Converts a double to a value by type punning */\nstatic inline value doubletovalue(double num) {\n  doubleunion data;\n  data.num = num;\n  return data.bits;\n}\n\n/** Converts a value to a double by type punning */\nstatic inline double valuetodouble(value v) {\n  doubleunion data;\n  data.bits = v;\n  return data.num;\n}\n\n/** Create a literal */\n#define MORPHO_NIL                  ((value) (uint64_t) (QNAN | TAG_NIL))\n#define MORPHO_TRUE                 ((value) (uint64_t) (QNAN | TAG_BOOL | TAG_TRUE))\n#define MORPHO_FALSE                ((value) (uint64_t) (QNAN | TAG_BOOL | TAG_FALSE))\n\n#define MORPHO_INTEGER(x)           ((((uint64_t) (x)) & LOWER_WORD) | QNAN | TAG_INT)\n#define MORPHO_FLOAT(x)             doubletovalue(x)\n#define MORPHO_BOOL(x)              ((x) ? MORPHO_TRUE : MORPHO_FALSE)\n#define MORPHO_OBJECT(x)            ((value) (TAG_OBJ | QNAN | (uint64_t)(uintptr_t)(x)))\n\n/** Test for the type of a value */\n#define MORPHO_ISNIL(v)             ((v) == MORPHO_NIL)\n#define MORPHO_ISINTEGER(v)         (((v) & (QNAN | TYPE_BITS)) == (QNAN | TAG_INT))\n#define MORPHO_ISFLOAT(v)           (((v) & QNAN) != QNAN)\n#define MORPHO_ISBOOL(v)            (((v) & (QNAN | TYPE_BITS)) == (QNAN | TAG_BOOL))\n#define MORPHO_ISOBJECT(v) \\\n        (((v) & (QNAN | TYPE_BITS))== (QNAN | TAG_OBJ))\n\n/** Get a value */\n#define MORPHO_GETINTEGERVALUE(v)   ((int) ((uint32_t) (v & LOWER_WORD)))\n#define MORPHO_GETFLOATVALUE(v)     valuetodouble(v)\n#define MORPHO_GETBOOLVALUE(v)      ((v) == MORPHO_TRUE)\n#define MORPHO_GETOBJECT(v)         ((object *) (uintptr_t) ((v) & ~(TAG_OBJ | QNAN)))\n\nstatic inline bool morpho_ofsametype(value a, value b) {\n    if (MORPHO_ISFLOAT(a) || MORPHO_ISFLOAT(b)) {\n        return MORPHO_ISFLOAT(a) && MORPHO_ISFLOAT(b);\n    } else {\n        if ((a & TYPE_BITS)==(b & TYPE_BITS)) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\n/** Get a non-object's type field as an integer */\nstatic inline int _getorderedtype(value x) {\n    return (MORPHO_ISFLOAT(x) ? 0 : (((x) & TYPE_BITS)>>47) & 0x7);\n}\n#define MORPHO_GETORDEREDTYPE(x) _getorderedtype(x)\n\n/** Alternatively, we represent a value through a struct. */\n#else\n\n/** @brief A enumerated type defining the different types available in Morpho. */\nenum {\n    VALUE_DOUBLE, // Note that the order of these must match the boxed version above\n    VALUE_NIL,\n    VALUE_BOOL,\n    VALUE_INTEGER,\n    VALUE_OBJECT\n};\n\ntypedef int valuetype;\n\n/** @brief The unboxed value type. */\ntypedef struct {\n    valuetype type;\n    union {\n        int integer;\n        double real;\n        bool boolean;\n        struct sobject *obj;\n    } as;\n} value;\n\n/** This macro gets the type of the value.\n    @warning Not intended for broad use. */\n#define MORPHO_GETTYPE(v) ((v).type)\n\n/** Gets the ordered type of the value\n    @warning Not intended for broad use. */\n#define MORPHO_GETORDEREDTYPE(v) ((v).type)\n\n/** Test for the type of a value */\n#define MORPHO_ISNIL(v) ((v).type==VALUE_NIL)\n#define MORPHO_ISINTEGER(v) ((v).type==VALUE_INTEGER)\n#define MORPHO_ISFLOAT(v) ((v).type==VALUE_DOUBLE)\n#define MORPHO_ISBOOL(v) ((v).type==VALUE_BOOL)\n#define MORPHO_ISOBJECT(v) ((v).type==VALUE_OBJECT)\n\n/** Create a literal */\n#define MORPHO_NIL ((value) { VALUE_NIL, .as.integer = (int) 0 })\n#define MORPHO_INTEGER(x) ((value) { VALUE_INTEGER, .as.integer = (int) (x) })\n#define MORPHO_FLOAT(x) ((value) { VALUE_DOUBLE, .as.real = (double) x })\n#define MORPHO_BOOL(x) ((value) { VALUE_BOOL, .as.boolean = (bool) x })\n#define MORPHO_OBJECT(x) ((value) { VALUE_OBJECT, .as.obj = (object *) x })\n\n#define MORPHO_TRUE MORPHO_BOOL(true)\n#define MORPHO_FALSE MORPHO_BOOL(false)\n\n/** Get a value */\n#define MORPHO_GETINTEGERVALUE(v) ((v).as.integer)\n#define MORPHO_GETFLOATVALUE(v) ((v).as.real)\n#define MORPHO_GETBOOLVALUE(v) ((v).as.boolean)\n#define MORPHO_GETOBJECT(v) ((v).as.obj)\n\nstatic inline bool morpho_ofsametype(value a, value b) {\n    return (a.type == b.type);\n}\n\n#endif\n\n/* -------------------------------------------------------\n * Comparing values\n * ------------------------------------------------------- */\n\n/** Check if two values are the same, i.e. identical or refer to the same object */\nbool morpho_issame(value a, value b);\n\n/** Test if two values are identical, i.e. identical or refer to the same object */\n#define MORPHO_ISSAME(a,b) (morpho_issame(a,b))\n\n/** Compare two values, checking contents of objects where supported */\nint morpho_comparevalue(value a, value b);\n\n/** Compare two values, even if they have inequivalent types e.g. int and float */\nint morpho_extendedcomparevalue(value a, value b);\n\n/** Macro to test if two values are equal, checking contents of objects where supported */\n#define MORPHO_ISEQUAL(a,b) (!morpho_comparevalue(a,b))\n\n/* -------------------------------------------------------\n * Type checking and conversion\n * ------------------------------------------------------- */\n\n/** Detect if a value is a number */\nbool morpho_isnumber(value a);\n\n/** Define a unified notion of falsity/truthyness */\nbool morpho_isfalse(value a);\n\n/** Convert a value to an integer */\nbool morpho_valuetoint(value v, int *out);\n\n/** Convert a value to a float */\nbool morpho_valuetofloat(value v, double *out);\n\n/** Macro to detect if a value is a number */\n#define MORPHO_ISNUMBER(v) (morpho_isnumber(v))\n\n/** Conversion of integer to a float */\n#define MORPHO_INTEGERTOFLOAT(x) (MORPHO_FLOAT((double) MORPHO_GETINTEGERVALUE((x))))\n\n/** Conversion of a float to an integer with rounding */\n#define MORPHO_FLOATTOINTEGER(x) (MORPHO_INTEGER((int) round(MORPHO_GETFLOATVALUE((x)))))\n\n/** Macros to determine if a value is true or false */\n#define MORPHO_ISFALSE(x) (morpho_isfalse(x))\n#define MORPHO_ISTRUE(x) (!morpho_isfalse(x))\n\n/* -------------------------------------------------------\n * Varrays of values\n * ------------------------------------------------------- */\n\nDECLARE_VARRAY(value, value);\n\nbool varray_valuefind(varray_value *varray, value v, unsigned int *out);\nbool varray_valuefindsame(varray_value *varray, value v, unsigned int *out);\n\n/* -------------------------------------------------------\n * Other utility functions\n * ------------------------------------------------------- */\n\nbool value_promotenumberlist(unsigned int nv, value *v);\nbool value_minmax(unsigned int nval, value *list, value *min, value *max);\n\nvoid value_initialize(void);\n\n#endif /* value_h */\n"
  },
  {
    "path": "src/datastructures/varray.c",
    "content": "/** @file varray.c\n *  @author T J Atherton\n *\n *  @brief Dynamically resizing array (varray) data structure\n*/\n\n#include \"varray.h\"\n\n/* **********************************************************************\n* Common varray types\n* ********************************************************************** */\n\nDEFINE_VARRAY(char, char);\nDEFINE_VARRAY(int, int);\nDEFINE_VARRAY(double, double);\nDEFINE_VARRAY(ptrdiff, ptrdiff_t);\n\n/** @brief Computes the nearest power of 2 above an integer\n * @param   n An integer\n * @returns Nearest power of 2 above n\n See: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float */\nunsigned int varray_powerof2ceiling(unsigned int n) {\n    n--;\n    n |= n >> 1;\n    n |= n >> 2;\n    n |= n >> 4;\n    n |= n >> 8;\n    n |= n >> 16;\n    n++;\n    \n    return n;\n}\n"
  },
  {
    "path": "src/datastructures/varray.h",
    "content": "/** @file varray.h\n *  @author T J Atherton\n *\n *  @brief Dynamically resizing array (varray) data structure\n*/\n\n#ifndef varray_h\n#define varray_h\n\n#include <stdlib.h>\n#include <stddef.h>\n#include <stdbool.h>\n\n#include \"memory.h\"\n\n/* -------------------------------------------------------\n * Variable array macros\n * ------------------------------------------------------- */\n\n/** @brief Creates a generic varray containing a specified type.\n *\n * @details Varrays only differ by their contents, and so we use macros to\n *  conveniently define types and functions.\n * To use these:\n *  First, call DECLARE_VARRAY(NAME,TYPE) in your .h file with a selected\n *   name for your varray and the type of thing you want to store in it.\n *  This will define:\n *  1. A type called varray_NAME (where NAME is the name you gave).\n *  2. Functions\n *     varray_NAMEinit(v)               - Initializes the varray\n *     varray_NAMEadd(v, data[], count) - Adds elements to the varray\n *     varray_NAMEwrite(v, data)        - Writes a single element to the varray, returning the index\n *     varray_NAMEclear(v)              - Clears the varray, freeing memory\n *  Then, call DEFINE_VARRAY(NAME,TYPE) in your .c file to define the appropriate functions\n */\n#define DECLARE_VARRAY(name, type) \\\n    typedef struct { \\\n        unsigned int count; \\\n        unsigned int capacity; \\\n        type *data; \\\n    } varray_##name; \\\n    \\\n    void varray_##name##init(varray_##name *v); \\\n    bool varray_##name##add(varray_##name *v, type *data, int count); \\\n    bool varray_##name##resize(varray_##name *v, int count); \\\n    int varray_##name##write(varray_##name *v, type data); \\\n    void varray_##name##clear(varray_##name *v);\n\n#define DEFINE_VARRAY(name, type) \\\nvoid varray_##name##init(varray_##name *v) { \\\n    v->count = 0; \\\n    v->capacity=0; \\\n    v->data=NULL; \\\n} \\\n\\\nbool varray_##name##add(varray_##name *v, type *data, int count) { \\\n    if (v->capacity<v->count + count) { \\\n        unsigned int capacity = varray_powerof2ceiling(v->count + count); \\\n        v->data = (type *) morpho_allocate(v->data, v->capacity * sizeof(type), \\\n                            capacity * sizeof(type)); \\\n        v->capacity = capacity; \\\n    }; \\\n    \\\n    if (v->data && data) for (unsigned int i = 0; i < count; i++) { \\\n        v->data[v->count++] = data[i]; \\\n    } \\\n    return (v->data!=NULL); \\\n} \\\n\\\nbool varray_##name##resize(varray_##name *v, int count) { \\\n    if (v->capacity<v->count + count) { \\\n        unsigned int capacity = varray_powerof2ceiling(v->count + count); \\\n        v->data = (type *) morpho_allocate(v->data, v->capacity * sizeof(type), \\\n                            capacity * sizeof(type)); \\\n        v->capacity = capacity; \\\n    }; \\\n    return (v->data!=NULL); \\\n} \\\n\\\nint varray_##name##write(varray_##name *v, type data) { \\\n    varray_##name##add(v, &data, 1); \\\n    return v->count-1; \\\n} \\\n\\\nvoid varray_##name##clear(varray_##name *v) { \\\n    morpho_allocate(v->data, 0, 0); \\\n    varray_##name##init(v); \\\n} \\\nbool varray_##name##pop(varray_##name *v, type *dest) { \\\n    if (v->count>0) { \\\n        v->count-=1; \\\n        *dest = v->data[v->count]; \\\n        return true; \\\n    } \\\n    return false; \\\n} \\\n\n/* -------------------------------------------------------\n * Common varray types\n * ------------------------------------------------------- */\n\nDECLARE_VARRAY(char, char);\nDECLARE_VARRAY(int, int);\nDECLARE_VARRAY(double, double);\nDECLARE_VARRAY(ptrdiff, ptrdiff_t);\n\nunsigned int varray_powerof2ceiling(unsigned int n);\n\n#endif /* varray_h */\n"
  },
  {
    "path": "src/datastructures/version.c",
    "content": "/** @file version.c\n *  @author T J Atherton\n *\n *  @brief Version comparison\n*/\n\n#include <stdio.h>\n\n#include \"build.h\"\n#include \"version.h\"\n\n/** @brief Initialize a version structure\n * @param[in] v - Version structure to intialize\n * @param[in] major }\n * @param[in] minor } version\n * @param[in] patch } */\nvoid version_init(version *v, int major, int minor, int patch) {\n    v->major=major;\n    v->minor=minor;\n    v->patch=patch;\n}\n\n/** @brief Compare two versions;  returns < 0 if a<b; 0 if a==b and >0 if a>b */\nint version_cmp(version *a, version *b) {\n    int d[3] = { a->major-b->major, a->minor-b->minor, a->patch-b->patch };\n    for (int i=0; i<3; i++) if (d[i]!=0) return d[i];\n    \n    return 0;\n}\n\n/** @brief Test whether a version is compatible with minimum and maximum version constraints\n * @param[in] v - Version structure to test\n * @param[in] min - (optional) minimum version number\n * @param[in] max - (optional) maximum version number\n * @returns true if min <= v <= max */\nbool version_check(version *v, version *min, version *max) {\n    int l = (min ? version_cmp(min, v) : 0);\n    int r = (max ? version_cmp(v, max) : 0);\n        \n    return (l>=0 && r<=0);\n}\n\n/** @brief Convert a version to a string\n * @param[in] v - Version structure to intialize\n * @param[in] n - size of output buffer\n * @param[out] str - output string - recommend creating with VERSION_MAXSTRINGLENGTH */\nvoid version_tostring(version *v, size_t size, char *str) {\n    snprintf(str, size, \"%i.%i.%i\", v->major, v->minor, v->patch);\n}\n\n/** @brief Sets a version to the current morpho version */\nvoid morpho_version(version *v) {\n    version_init(v, MORPHO_VERSION_MAJOR, MORPHO_VERSION_MINOR, MORPHO_VERSION_PATCH);\n}\n"
  },
  {
    "path": "src/datastructures/version.h",
    "content": "/** @file version.h\n *  @author T J Atherton\n *\n *  @brief Semantic version comparison\n*/\n\n#ifndef version_h\n#define version_h\n\n#include <stdbool.h>\n\n#define VERSION_MAXSTRINGLENGTH 64\n\ntypedef struct {\n    int major; // when you make incompatible API changes\n    int minor; // when you add functionality in a backward compatible manner\n    int patch; // when you make backward compatible bug fixes\n} version;\n\n#define VERSION_STATIC(maj, min, ptch) { .major = maj, .minor = min, .patch = ptch }\n\n/** @brief Initialize a version structure\n * @param[in] v - Version structure to intialize\n * @param[in] major }\n * @param[in] minor } version\n * @param[in] patch } */\nvoid version_init(version *v, int major, int minor, int patch);\n\n/** @brief Compare two versions;  returns < 0 if a<b; 0 if a==b and >0 if a>b */\nint version_cmp(version *a, version *b);\n\n/** @brief Test whether a version is compatible with minimum and maximum version constraints\n * @param[in] v - Version structure to test\n * @param[in] min - (optional) minimum version number\n * @param[in] max - (optional) maximum version number\n * @returns true if min <= v <= max */\nbool version_check(version *v, version *min, version *max);\n\n/** @brief Convert a version to a string\n * @param[in] v - Version structure to intialize\n * @param[in] n - size of output buffer\n * @param[out] str - output string - recommend creating with VERSION_MAXSTRINGLENGTH */\nvoid version_tostring(version *v, size_t n, char *str);\n\n/** @brief Sets a version to the current morpho version */\nvoid morpho_version(version *v);\n\n#endif\n"
  },
  {
    "path": "src/debug/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        debug.c      debug.h\n        profile.c    profile.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        debug.h\n        profile.h\n)\n"
  },
  {
    "path": "src/debug/debug.c",
    "content": "/** @file debug.c\n *  @author T J Atherton\n *\n *  @brief Debugging, dissassembly and other tools\n */\n\n#include <stdarg.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"compile.h\"\n#include \"vm.h\"\n#include \"gc.h\"\n#include \"debug.h\"\n#include \"morpho.h\"\n#include \"strng.h\"\n\n#include \"debugannotation.h\"\n\nvoid morpho_runtimeerror(vm *v, errorid id, ...);\n\n/* **********************************************************************\n * Extract debugging information using annotations\n * ********************************************************************** */\n\n/** Finds debugging info asssociated with instruction at indx */\nbool debug_infofromindx(program *code, instructionindx indx, value *module, int *line, int *posn, objectfunction **func, objectclass **klass) {\n    if (module) *module=MORPHO_NIL;\n    if (func) *func=code->global;\n    if (klass) *klass=NULL;\n    instructionindx i=0;\n    \n    for (unsigned int j=0; j<code->annotations.count; j++) {\n        debugannotation *ann = &code->annotations.data[j];\n        switch (ann->type) {\n            case DEBUG_ELEMENT: {\n                if (i+ann->content.element.ninstr>indx) {\n                    if (line) *line = ann->content.element.line;\n                    if (posn) *posn = ann->content.element.posn;\n                    return true;\n                }\n                i+=ann->content.element.ninstr;\n            }\n                break;\n            case DEBUG_FUNCTION: if (func) *func=ann->content.function.function; break;\n            case DEBUG_CLASS: if (klass) *klass=ann->content.klass.klass; break;\n            case DEBUG_MODULE: if (module) *module=ann->content.module.module; break;\n            default: break;\n        }\n    }\n    \n    return false;\n}\n\n/** Finds the instruction indx corresponding to a particular line of code */\nbool debug_indxfromline(program *code, value file, int line, instructionindx *out) {\n    instructionindx i=0;\n    value module=MORPHO_NIL;\n    \n    for (unsigned int j=0; j<code->annotations.count; j++) {\n        debugannotation *ann = &code->annotations.data[j];\n        switch (ann->type) {\n            case DEBUG_ELEMENT:\n                if (MORPHO_ISEQUAL(file, module) &&\n                    ann->content.element.line==line) {\n                    *out=i;\n                    return true;\n                }\n                i+=ann->content.element.ninstr;\n                break;\n            case DEBUG_MODULE:\n                module=ann->content.module.module;\n                break;\n            default: break;\n        }\n    }\n    return false;\n}\n\n/** Finds the instruction index corresponding to the entry point of a function or method */\nbool debug_indxfromfunction(program *code, value klassname, value fname, instructionindx *indx) {\n    objectclass *cklass=NULL;\n    objectfunction *cfunc=NULL;\n    \n    for (unsigned int j=0; j<code->annotations.count; j++) {\n        debugannotation *ann = &code->annotations.data[j];\n        switch (ann->type) {\n            case DEBUG_FUNCTION:\n                cfunc=ann->content.function.function;\n                if (MORPHO_ISEQUAL(cfunc->name, fname) &&\n                    (MORPHO_ISNIL(klassname) || MORPHO_ISEQUAL(cklass->name, klassname))) {\n                    *indx=cfunc->entry;\n                    return true;\n                }\n                break;\n            case DEBUG_CLASS:\n                cklass=ann->content.klass.klass;\n                break;\n            default: break;\n        }\n    }\n    \n    return false;\n}\n\n/** Identifies symbols associated with registers\n * @param[in] code - a program\n * @param[in] func - the function of interest\n * @param[in] indx - (optional) instruction to stop at\n * @param[out] symbols - array of size func->negs; entries will contain associated register names on exit */\nbool debug_symbolsforfunction(program *code, objectfunction *func, instructionindx *indx, value *symbols) {\n    objectfunction *cfunc=code->global;\n    instructionindx i=0;\n    \n    for (unsigned int j=0; j<func->nregs; j++) symbols[j]=MORPHO_NIL;\n    \n    for (unsigned int j=0; j<code->annotations.count; j++) {\n        debugannotation *ann = &code->annotations.data[j];\n        switch (ann->type) {\n            case DEBUG_ELEMENT: {\n                if (indx && i+ann->content.element.ninstr>*indx) return true;\n                i+=ann->content.element.ninstr;\n            }\n                break;\n            case DEBUG_FUNCTION: cfunc=ann->content.function.function; break;\n            case DEBUG_REGISTER: {\n                if (cfunc==func) {\n                    symbols[ann->content.reg.reg]=ann->content.reg.symbol;\n                }\n            }\n                break;\n            default: break;\n        }\n    }\n    \n    return true;\n}\n\n/** Attempts to find a symbol.\n * @param[in] v - virtual machine to search\n * @param[in] matchstr - string to match\n * @param[out] frame - callframe matched\n * @param[out] symbol - actual symbol found\n * @param[out] val - value of the found symbol\n * @returns true on success */\nbool debug_findsymbol(vm *v, value matchstr, callframe **frame, value *symbol, value **val) {\n    /* Check back through callframes */\n    for (callframe *f=v->fp; f>=v->frame; f--) {\n        value symbols[f->function->nregs];\n        instructionindx indx = f->pc-v->current->code.data;\n        \n        debug_symbolsforfunction(v->current, f->function, &indx, symbols);\n        \n        for (int i=0; i<f->function->nregs; i++) {\n            if (!MORPHO_ISNIL(symbols[i]) && MORPHO_ISEQUAL(symbols[i], matchstr)) {\n                if (frame) *frame = f;\n                if (symbol) *symbol = symbols[i];\n                if (val) *val = &v->stack.data[f->roffset+i];\n                return true;\n            }\n        }\n    }\n    \n    /* Otherwise is it a global? */\n    for (unsigned int j=0; j<v->current->annotations.count; j++) {\n        debugannotation *ann = &v->current->annotations.data[j];\n        if (ann->type==DEBUG_GLOBAL &&\n            MORPHO_ISEQUAL(ann->content.global.symbol, matchstr)) {\n            if (frame) *frame = v->frame;\n            if (symbol) *symbol = ann->content.global.symbol;\n            if (val) *val = &v->globals.data[ann->content.global.gindx];\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Identifies a symbol for a given global number\n * @param[in] p - program to search\n * @param[in] id - global number to find\n * @param[out] frame - callframe matched*/\nbool debug_symbolforglobal(program *p, indx id, value *symbol) {\n    for (unsigned int j=0; j<p->annotations.count; j++) {\n        debugannotation *ann = &p->annotations.data[j];\n        if (ann->type==DEBUG_GLOBAL &&\n            ann->content.global.gindx==id) {\n            *symbol = ann->content.global.symbol;\n            return true;\n        }\n    }\n    return false;\n}\n\n/* **********************************************************************\n * Stack traces\n * ********************************************************************** */\n\n/** Prints a stacktrace */\nvoid morpho_stacktrace(vm *v) {\n    for (callframe *f = (v->errfp ? v->errfp : v->fp); f!=NULL && f>=v->frame; f--) {\n        instructionindx indx = f->pc-v->current->code.data;\n        if (indx>0) indx--; /* Because the pc always points to the NEXT instr. */\n        \n        morpho_printf(v, \"  \");\n        morpho_printf(v, \"%s\", (f==v->fp ? \"  in \" : \"from \"));\n        \n        if (!MORPHO_ISNIL(f->function->name)) morpho_printvalue(v, f->function->name);\n        else morpho_printf(v, \"global\");\n        \n        int line=0;\n        \n        value module = MORPHO_NIL;\n        if (debug_infofromindx(v->current, indx, &module, &line, NULL, NULL, NULL)) {\n            morpho_printf(v, \" at line %u\", line);\n            \n            if (!MORPHO_ISNIL(module)) {\n                morpho_printf(v, \" in module '\");\n                morpho_printvalue(v, module);\n                morpho_printf(v, \"'\");\n            }\n        }\n        \n        morpho_printf(v, \"\\n\");\n    }\n}\n\n/* **********************************************************************\n * Debugger data structure\n * ********************************************************************** */\n\n/** Initializes a debugger structure with a specified program */\nvoid debugger_init(debugger *d, program *p) {\n    d->singlestep=false;\n    \n    d->nbreakpoints=0;\n    \n    d->err=NULL;\n    d->currentfunc=NULL;\n    d->currentline=0;\n    d->currentmodule=MORPHO_NIL;\n    \n    varray_charinit(&d->breakpoints);\n    \n    int ninstructions = p->code.count;\n    if (!varray_charresize(&d->breakpoints, ninstructions)) return;\n    memset(d->breakpoints.data, '\\0', sizeof(char)*ninstructions);\n    d->breakpoints.count=ninstructions;\n}\n\n/** Clears a debugger structure */\nvoid debugger_clear(debugger *d) {\n    varray_charclear(&d->breakpoints);\n}\n\n/** Returns the current VM */\nvm *debugger_currentvm(debugger *d) {\n    return d->currentvm;\n}\n\n/** Returns the current program */\nprogram *debugger_currentprogram(debugger *d) {\n    return d->currentvm->current;\n}\n\n/** Sets the error structure that the debugger will report errors to */\nvoid debugger_seterror(debugger *d, error *err) {\n    d->err=err;\n}\n\n/** @brief Raises a debugger error\n * @param debug        the debugger\n * @param id       error id\n * @param ...      additional data for sprintf. */\nvoid debugger_error(debugger *debug, errorid id, ... ) {\n    if (!debug->err || debug->err->id!=ERROR_NONE) return; // Ensure errors are not overwritten.\n    va_list args;\n    va_start(args, id);\n    morpho_writeerrorwithidvalist(debug->err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args);\n    va_end(args);\n}\n\n/** Sets whether single step mode is in operation */\nvoid debugger_setsinglestep(debugger *d, bool singlestep) {\n    d->singlestep=singlestep;\n}\n\n/** Are we in singlestep mode? */\nbool debugger_insinglestep(debugger *d) {\n    return d->singlestep;\n}\n\n/** Sets a breakpoint */\nbool debugger_setbreakpoint(debugger *d, instructionindx indx) {\n    if (indx>d->breakpoints.count) return false;\n    d->breakpoints.data[indx]='b';\n    d->nbreakpoints++;\n    return true;\n}\n\n/** Clears a breakpoint */\nbool debugger_clearbreakpoint(debugger *d, instructionindx indx) {\n    if (indx>d->breakpoints.count) return false;\n    d->breakpoints.data[indx]='\\0';\n    d->nbreakpoints--;\n    return true;\n}\n\n/** Tests if we should break at a given point */\nbool debugger_shouldbreakat(debugger *d, instructionindx indx) {\n    if (indx>d->breakpoints.count) return false;\n    return (d->breakpoints.data[indx]!='\\0');\n}\n\n/** Tests if the debugger is in a mode that could cause breaks at arbitrary instructions */\nbool debugger_isactive(debugger *d) {\n    return (d->singlestep || (d->nbreakpoints>0));\n}\n\n/* **********************************************************************\n * Disassembler\n * ********************************************************************** */\n\n/** Formatting rules for disassembler */\ntypedef struct {\n    instruction op; /** Opcode */\n    char *label; /** Label to display in disasembler */\n    char *display; /** Display code - rX is register, cX is constant X, gX is global X, uX is upvalue, + refers to signed B */\n} assemblyrule;\n\n/** Define disassembler by how to display each opcode */\nassemblyrule assemblyrules[] ={\n    { OP_NOP, \"nop\", \"\" },\n    { OP_MOV, \"mov\", \"rA, rB\" },\n    { OP_LCT, \"lct\", \"rA, cX\" },\n    { OP_ADD, \"add\", \"rA, rB, rC\" },\n    { OP_SUB, \"sub\", \"rA, rB, rC\" },\n    { OP_MUL, \"mul\", \"rA, rB, rC\" },\n    { OP_DIV, \"div\", \"rA, rB, rC\" },\n    { OP_POW, \"pow\", \"rA, rB, rC\" },\n    { OP_NOT, \"not\", \"rA, rB\" },\n    \n    { OP_EQ, \"eq \", \"rA, rB, rC\" },\n    { OP_NEQ, \"neq\", \"rA, rB, rC\" },\n    { OP_LT, \"lt \", \"rA, rB, rC\" },\n    { OP_LE, \"le \", \"rA, rB, rC\" },\n    \n    { OP_PRINT, \"print\", \"rA\" },\n    \n    { OP_B, \"b\", \"+\" },\n    { OP_BIF, \"bif\", \"rA +\" },\n    { OP_BIFF, \"biff\", \"rA +\" },\n    \n    { OP_CALL, \"call\", \"rA, B, C\" }, // b, c literal\n    { OP_INVOKE, \"invoke\", \"rA, B, C\" }, // b, c literal\n    { OP_METHOD, \"method\", \"rA, B, C\" }, // b, c literal\n    \n    { OP_RETURN, \"return\", \"?rB\" }, // Return register B is A nonzero\n\n    { OP_CLOSURE, \"closure\", \"rA, pB\" }, // b prototype\n    \n    { OP_LUP, \"lup\", \"rA, uB\" }, // b 'u'\n    { OP_SUP, \"sup\", \"uA, rB\" }, // a 'u', b c|r\n    \n    { OP_CLOSEUP, \"closeup\", \"rA\" },\n    { OP_LPR, \"lpr\", \"rA, rB, rC\" },\n    { OP_SPR, \"spr\", \"rA, rB, rC\" },\n    \n    { OP_LIX,  \"lix\", \"rA, rB, rC\" },\n    { OP_LIXL, \"lixl\", \"rA, rB, rC\" },\n    \n    { OP_SIX,  \"six\", \"rA, rB, rC\" },\n    \n    { OP_LGL, \"lgl\", \"rA, gX\" }, //\n    { OP_SGL, \"sgl\", \"rA, gX\" }, // label b with 'g'\n    \n    { OP_PUSHERR, \"pusherr\", \"cX\" },\n    { OP_POPERR, \"poperr\", \"+\" },\n    \n    { OP_CAT, \"cat\", \"rA, rB, rC\" },\n    { OP_BREAK, \"break\", \"\" },\n    { OP_END, \"end\", \"\" },\n    { 0, NULL, \"\" } // Null terminate the list\n};\n\nassemblyrule *debugger_getassemblyrule(unsigned int op) {\n    for (unsigned int i=0; assemblyrules[i].label!=NULL; i++) if (assemblyrules[i].op==op) return &assemblyrules[i];\n    return NULL;\n}\n\ntypedef enum { DBG_CONTENTS_NONE, DBG_CONTENTS_REG, DBG_CONTENTS_CONST} debugcontents;\n\n/** Shows the contents of a register or constant */\nbool debugger_showcontents(vm *v, debugcontents b, int i, value *konst, value *reg) {\n    value *table = NULL;\n    switch (b) {\n        case DBG_CONTENTS_CONST: table=konst; break;\n        case DBG_CONTENTS_REG: table = reg; break;\n        default: break;\n    }\n    if (!table) return false;\n    morpho_printf(v, \"%s%i=\", (b==DBG_CONTENTS_CONST ? \"c\" : \"r\"), i);\n    morpho_printvalue(v, table[i]);\n    return true;\n}\n\n/** @brief Disassembles a single instruction, writing the output to the console.\n *  @param v VM to use for output\n *  @param instruction The instruction to disassemble\n *  @param indx        Instruction index to display\n *  @param konst current constant table\n *  @param reg   current registers */\nvoid debugger_disassembleinstruction(vm *v, instruction instruction, instructionindx indx, value *konst, value *reg) {\n    unsigned int op = DECODE_OP(instruction);\n    debugcontents mode=DBG_CONTENTS_NONE, bm=DBG_CONTENTS_NONE, cm=DBG_CONTENTS_NONE;\n    int nb=0, nc=0;\n    morpho_printf(v, \"%4lu : \", indx);\n    int n=0; // Number of characters displayed\n    int width=25; // Width of display\n    \n    assemblyrule *show=debugger_getassemblyrule(op);\n    if (show) {\n        n+=morpho_printf(v, \"%s \", show->label);\n        for (char *c=show->display; *c!='\\0'; c++) {\n            switch (*c) {\n                case 'A': n+=morpho_printf(v, \"%u\", DECODE_A(instruction)); break;\n                case 'B': {\n                    bm=mode; nb=DECODE_B(instruction); mode=DBG_CONTENTS_NONE;\n                    n+=morpho_printf(v, \"%u\", nb);\n                }\n                    break;\n                case 'X': {\n                    bm=mode; nb=DECODE_Bx(instruction); mode=DBG_CONTENTS_NONE;\n                    n+=morpho_printf(v, \"%u\", nb);\n                }\n                    break;\n                case '+': n+=morpho_printf(v, \"%i\", DECODE_sBx(instruction)); break;\n                case 'C': {\n                    cm=mode; nc=DECODE_C(instruction);\n                    n+=morpho_printf(v, \"%u\", DECODE_C(instruction));\n                }\n                    break;\n                case 'c': mode=DBG_CONTENTS_CONST; n+=morpho_printf(v, \"%c\", *c); break;\n                case 'r': mode=DBG_CONTENTS_REG; n+=morpho_printf(v, \"%c\", *c); break;\n                case '?':\n                    if (DECODE_A(instruction)==0) return;\n                    break;\n                default: n+=morpho_printf(v, \"%c\", *c); break;\n            }\n        }\n        \n        /* Show contents if any were produced by this instruction */\n        if ((!konst && !reg) || (bm==DBG_CONTENTS_NONE && cm==DBG_CONTENTS_NONE)) return;\n        for (int k=width-n; k>0; k--) morpho_printf(v, \" \");\n        morpho_printf(v, \"; \");\n        if (debugger_showcontents(v, bm, nb, konst, reg)) morpho_printf(v, \" \");\n        debugger_showcontents(v, cm, nc, konst, reg);\n    }\n}\n\n/** Checks if an instruction matches a label in the current error dictionary, and if so print it. */\nvoid debugger_errorlabel(vm *v, varray_value *errorstack, instructionindx i) {\n    objectdictionary *dict = MORPHO_GETDICTIONARY(errorstack->data[errorstack->count-1]);\n    \n    /* Search the current error handler to see if this line corresponds to a label */\n    for (unsigned int k=0; k<dict->dict.capacity; k++) {\n        value label = dict->dict.contents[k].key;\n        if (!MORPHO_ISNIL(label)) {\n            if (MORPHO_GETINTEGERVALUE(dict->dict.contents[k].val)==i) {\n                morpho_printvalue(v, label);\n                morpho_printf(v, \":\\n\");\n            }\n        }\n    }\n}\n\n/** Disassembles a program\n *  @param v - vm to use for output\n *  @param code - program to disassemble\n *  @param matchline - optional line number to match */\nvoid debugger_disassemble(vm *v, program *code, int *matchline) {\n    instructionindx entry = program_getentry(code); // The entry point of the function\n    instructionindx i=0;\n    value *konst=(code->global ? code->global->konst.data : NULL);\n    bool silent = matchline;\n    \n    varray_value errorstack;\n    varray_valueinit(&errorstack);\n    \n    /* Loop over debugging information */\n    for (unsigned int j=0; j<code->annotations.count; j++) {\n        debugannotation *ann = &code->annotations.data[j];\n        \n        switch(ann->type) {\n            case DEBUG_ELEMENT:\n                {\n                    if (matchline) {\n                        if (ann->content.element.line<(*matchline)) {\n                            i+=ann->content.element.ninstr;\n                            break;\n                        }\n                        if (ann->content.element.line>(*matchline)) return;\n                    } else if (errorstack.count>0) {\n                        debugger_errorlabel(v, &errorstack, i);\n                    }\n                    \n                    for (unsigned int k=0; k<ann->content.element.ninstr; k++, i++) {\n                        morpho_printf(v, \"%s\", (i==entry ? \"->\" : \"  \"));\n                        debugger_disassembleinstruction(v, code->code.data[i], i, konst, NULL);\n                        morpho_printf(v, \"\\n\");\n                    }\n                }\n                break;\n            case DEBUG_FUNCTION:\n                {\n                    objectfunction *func=ann->content.function.function;\n                    konst=func->konst.data;\n                    if (silent) break;\n                    if (!MORPHO_ISNIL(func->name)) {\n                        morpho_printf(v, \"fn \");\n                        morpho_printvalue(v, func->name);\n                        morpho_printf(v, \":\\n\");\n                    } else morpho_printf(v, \"\\n\");\n                }\n                break;\n            case DEBUG_CLASS:\n                {\n                    objectclass *klass=ann->content.klass.klass;\n                    if (silent) break;\n                    if (klass && !MORPHO_ISNIL(klass->name)) {\n                        morpho_printf(v, \"class \");\n                        morpho_printvalue(v, klass->name);\n                        morpho_printf(v, \":\\n\");\n                    }\n                }\n                break;\n            case DEBUG_PUSHERR:\n                {\n                    objectdictionary *errdict = ann->content.errorhandler.handler;\n                    varray_valuewrite(&errorstack, MORPHO_OBJECT(errdict));\n                }\n                break;\n            case DEBUG_POPERR:\n                {\n                    if (errorstack.count>0) errorstack.count--;\n                }\n                break;\n            default:\n                break;\n        }\n    }\n    \n    varray_valueclear(&errorstack);\n}\n\n/** Public interface to disassembler */\nvoid morpho_disassemble(vm *v, program *code, int *matchline) {\n    debugger_disassemble(NULL, code, matchline);\n}\n\n/* **********************************************************************\n * General debugger commands\n * ********************************************************************** */\n\nvoid debugger_garbagecollect(debugger *debug) {\n    size_t init = debug->currentvm->bound;\n    vm_collectgarbage(debug->currentvm);\n    morpho_printf(NULL, \"Collected %ld bytes (from %zu to %zu). Next collection at %zu bytes.\\n\", init-debug->currentvm->bound, init, debug->currentvm->bound, debug->currentvm->nextgc);\n}\n\nvoid debugger_quit(debugger *debug) {\n    morpho_runtimeerror(debug->currentvm, VM_DBGQUIT);\n}\n\n/* **********************************************************************\n * Breakpoints\n * ********************************************************************** */\n\nbool debugger_breakatinstruction(debugger *debug, bool set, instructionindx indx) {\n    bool success=false;\n    if (set) success=debugger_setbreakpoint(debug, indx);\n    else success=debugger_clearbreakpoint(debug, indx);\n    \n    if (!success) debugger_error(debug, DEBUGGER_INVLDINSTR);\n    \n    return success;\n}\n\n/** Break at a particular line */\nbool debugger_breakatline(debugger *debug, bool set, value file, int line) {\n    instructionindx indx;\n    return (debug_indxfromline(debugger_currentprogram(debug), file, line, &indx) &&\n                  debugger_breakatinstruction(debug, set, indx));\n}\n\n/** Break at a function or method */\nbool debugger_breakatfunction(debugger *debug, bool set, value klass, value function) {\n    instructionindx indx;\n    return (debug_indxfromfunction(debugger_currentprogram(debug), klass, function, &indx) &&\n                  debugger_breakatinstruction(debug, set, indx));\n}\n\n/* **********************************************************************\n * Show commands\n * ********************************************************************** */\n\n/** Prints the location information for a given instruction */\nvoid debugger_showlocation(debugger *debug, instructionindx indx) {\n    vm *v = debugger_currentvm(debug);\n    \n    value module=MORPHO_NIL; // Find location information\n    int line=0;\n    objectfunction *fn=NULL;\n    objectclass *klass=NULL;\n    debug_infofromindx(debugger_currentprogram(debug), indx, &module, &line, NULL, &fn, &klass);\n    \n    morpho_printf(v, \"in \");\n    \n    if (klass) {\n        morpho_printvalue(v, klass->name);\n        morpho_printf(v, \".\");\n    }\n    \n    if (!MORPHO_ISNIL(fn->name)) morpho_printvalue(v, fn->name);\n    else if (v->current->global==fn) morpho_printf(v, \"global\");\n    else morpho_printf(v, \"anonymous fn\");\n    \n    if (!MORPHO_ISNIL(module)) {\n        morpho_printf(v, \" in '\");\n        morpho_printvalue(v, module);\n        morpho_printf(v, \"'\");\n    }\n    morpho_printf(v, \" at line %i [instruction %ti]\", line, indx);\n}\n\n/** Shows the address of an object */\nbool debugger_showaddress(debugger *debug, indx rindx) {\n    vm *v = debugger_currentvm(debug);\n    bool success=false;\n    if (rindx>=0 && rindx<v->fp->function->nregs) {\n        value *reg = v->stack.data + debug->currentvm->fp->roffset;\n        if (MORPHO_ISOBJECT(reg[rindx])) {\n            morpho_printf(v, \"Object in register %i at %p.\\n\", (int) rindx, (void *) MORPHO_GETOBJECT(reg[rindx]));\n            success=true;\n        } else debugger_error(debug, DEBUGGER_REGISTEROBJ, (int) rindx);\n    } else debugger_error(debug, DEBUGGER_INVLDREGISTER);\n    return success;\n}\n\n/** Shows active breakpoints */\nvoid debugger_showbreakpoints(debugger *debug) {\n    vm *v = debugger_currentvm(debug);\n    morpho_printf(v, \"Active breakpoints:\\n\");\n    for (instructionindx i=0; i<debug->breakpoints.count; i++) {\n        if (debug->breakpoints.data[i]!='\\0') {\n            morpho_printf(v, \"  Breakpoint \");\n            debugger_showlocation(debug, i);\n            morpho_printf(v, \"\\n\");\n        } else if (DECODE_OP(debugger_currentprogram(debug)->code.data[i])==OP_BREAK) {\n            morpho_printf(v, \"  Break \");\n            debugger_showlocation(debug, i);\n            morpho_printf(v, \"\\n\");\n        }\n    }\n}\n\n/** List a particular global */\nbool debugger_showglobal(debugger *debug, indx id) {\n    bool success=false;\n    vm *v = debugger_currentvm(debug);\n    if (id>=0 && id<v->globals.count) {\n        value symbol;\n        morpho_printf(v, \"  g%lu:\", id);\n        morpho_printvalue(v, v->globals.data[id]);\n        if (debug_symbolforglobal(v->current, id, &symbol)) {\n            morpho_printf(v, \" (\");\n            morpho_printvalue(v, symbol);\n            morpho_printf(v, \")\");\n        }\n        morpho_printf(v, \"\\n\");\n        success=true;\n    } else debugger_error(debug, DEBUGGER_INVLDGLOBAL);\n    return success;\n}\n\n/** Show all globals */\nvoid debugger_showglobals(debugger *debug) {\n    vm *v = debugger_currentvm(debug);\n    morpho_printf(v, \"Globals:\\n\");\n    for (indx i=0; i<v->globals.count; i++) debugger_showglobal(debug, i);\n}\n\n/** Show the contents of all registers */\nvoid debugger_showregisters(debugger *debug) {\n    vm *v = debugger_currentvm(debug);\n    callframe *frame = v->fp;\n    \n    unsigned int nreg=frame->function->nregs;\n    value symbols[nreg];\n    instructionindx cinstr=frame->pc-v->current->code.data;\n    bool sym = debug_symbolsforfunction(v->current, frame->function, &cinstr, symbols);\n    \n    morpho_printf(v, \"Register contents:\\n\");\n    value *reg = v->stack.data + frame->roffset;\n    for (unsigned int i=0; i<nreg; i++) {\n        morpho_printf(v, \"  r%u: \", i);\n        morpho_printvalue(v, reg[i]);\n        if (sym && !MORPHO_ISNIL(symbols[i])) {\n            morpho_printf(v, \" (\");\n            morpho_printvalue(v, symbols[i]);\n            morpho_printf(v, \")\");\n        }\n        morpho_printf(v, \"\\n\");\n    }\n}\n\n/** Show the contents of the stack */\nvoid debugger_showstack(debugger *debug) {\n    vm *v = debugger_currentvm(debug);\n    \n    /* Determine points on the stack that correspond to different function calls. */\n    ptrdiff_t fbounds[MORPHO_CALLFRAMESTACKSIZE];\n    callframe *f;\n    unsigned int k=0;\n    for (f=v->frame; f!=v->fp; f++) {\n        fbounds[k]=f->roffset;\n        k++;\n    }\n    fbounds[k]=f->roffset;\n    \n    f=v->frame; k=0;\n    morpho_printf(v, \"Stack contents:\\n\");\n    for (unsigned int i=0; i<v->fp->roffset+v->fp->function->nregs; i++) {\n        if (i==fbounds[k]) {\n            morpho_printf(v, \"---\");\n            if (f->function) morpho_printvalue(v, f->function->name);\n            morpho_printf(v, \"\\n\");\n            k++; f++;\n        }\n        morpho_printf(v, \"  s%u: \", i);\n        morpho_printvalue(v, v->stack.data[i]);\n        morpho_printf(v, \"\\n\");\n    }\n}\n\n/** Show the current value of a symbol */\nbool debugger_showsymbol(debugger *debug, value match) {\n    vm *v = debugger_currentvm(debug);\n    \n    value symbol, *val=NULL;\n    if (debug_findsymbol(v, match, NULL, &symbol, &val)) {\n        morpho_printvalue(v, symbol);\n        morpho_printf(v, \" = \");\n        morpho_printvalue(v, *val);\n        morpho_printf(v, \"\\n\");\n    } else debugger_error(debug, DEBUGGER_FINDSYMBOL, MORPHO_GETCSTRING(match));\n    \n    return val;\n}\n\n/** Shows all symbols currently in view */\nvoid debugger_showsymbols(debugger *debug) {\n    vm *v = debugger_currentvm(debug);\n    \n    for (callframe *f=v->fp; f>=v->frame; f--) {\n        morpho_printf(v, \"in %s\", (f==v->frame ? \"global\" : \"\"));\n        if (!MORPHO_ISNIL(f->function->name)) morpho_printvalue(v, f->function->name);\n        morpho_printf(v, \":\\n\");\n        \n        value symbols[f->function->nregs];\n        instructionindx indx = f->pc-v->current->code.data;\n        \n        debug_symbolsforfunction(v->current, f->function, &indx, symbols);\n        \n        for (int i=0; i<f->function->nregs; i++) {\n            if (!MORPHO_ISNIL(symbols[i])) {\n                morpho_printf(v, \"  \");\n                morpho_printvalue(v, symbols[i]);\n                morpho_printf(v, \"=\");\n                morpho_printvalue(v, v->stack.data[f->roffset+i]);\n                morpho_printf(v, \"\\n\");\n            }\n        }\n    }\n}\n\n/** Show the current value of a property */\nbool debugger_showproperty(debugger *debug, value matchobj, value matchproperty) {\n    bool success=false;\n    vm *v = debugger_currentvm(debug);\n    \n    callframe *frame;\n    value symbol, *instance=NULL, val;\n    \n    if (debug_findsymbol(v, matchobj, &frame, &symbol, &instance)) {\n        if (MORPHO_ISINSTANCE(*instance)) {\n            objectinstance *obj = MORPHO_GETINSTANCE(*instance);\n                    \n            if (objectinstance_getproperty(obj, matchproperty, &val)) {\n                morpho_printvalue(v, symbol);\n                morpho_printf(v, \".\");\n                morpho_printvalue(v, matchproperty);\n                morpho_printf(v, \" = \");\n                morpho_printvalue(v, val);\n                morpho_printf(v, \"\\n\");\n                success=true;\n            } else debugger_error(debug, DEBUGGER_SYMBOLPROP, MORPHO_GETCSTRING(matchproperty));\n        }\n    }\n    \n    return success;\n}\n\n/* **********************************************************************\n * Set commands\n * ********************************************************************** */\n\n/** Sets the contents of a register to a given value */\nbool debugger_setregister(debugger *debug, indx reg, value val) {\n    vm *v=debugger_currentvm(debug);\n    bool success=false;\n    \n    if (reg>=0 && reg<v->fp->function->nregs) {\n        v->stack.data[v->fp->roffset+reg]=val;\n        success=true;\n    }\n    \n    return success;\n}\n\n/** Sets a symbol to a given value */\nbool debugger_setsymbol(debugger *debug, value symbol, value val) {\n    value *dest=NULL;\n    \n    if (debug_findsymbol(debugger_currentvm(debug), symbol, NULL, NULL, &dest)) {\n        *dest=val;\n    } else debugger_error(debug, DEBUGGER_FINDSYMBOL, MORPHO_GETCSTRING(symbol));\n    \n    return (dest!=NULL);\n}\n\n/** Sets a property to a given value */\nbool debugger_setproperty(debugger *debug, value symbol, value property, value val) {\n    value *dest=NULL;\n    bool success=false;\n    \n    if (debug_findsymbol(debugger_currentvm(debug), symbol, NULL, NULL, &dest)) {\n        if (MORPHO_ISINSTANCE(*dest)) {\n            objectinstance *obj = MORPHO_GETINSTANCE(*dest);\n            \n            value key = dictionary_intern(&obj->fields, property);\n            success=objectinstance_setproperty(obj, key, val);\n        } else debugger_error(debug, DEBUGGER_SETPROPERTY);\n    } else debugger_error(debug, DEBUGGER_FINDSYMBOL, MORPHO_GETCSTRING(symbol));\n    \n    return success;\n}\n\n/* **********************************************************************\n * Enter the debugger (called by the VM)\n * ********************************************************************** */\n\n/** Enters the debugger, if one is active. */\nbool debugger_enter(debugger *debug, vm *v) {\n    if (debug && v->debuggerfn) {\n        debug->currentvm = v;\n        \n        // Get instruction index\n        debug->iindx = vm_currentinstruction(v);\n        \n        // Retain previous line and function information\n        int oline=debug->currentline;\n        objectfunction *ofunc=debug->currentfunc;\n        \n        // Fetch info from annotations\n        debug_infofromindx(debugger_currentprogram(debug), debug->iindx, &debug->currentmodule, &debug->currentline, NULL, &debug->currentfunc, NULL);\n        \n        // If we're in single step mode, only stop when we've changed line OR if a breakpoint is explicitly set\n        if (debugger_insinglestep(debug) &&\n            oline==debug->currentline &&\n            ofunc==debug->currentfunc &&\n            !debugger_shouldbreakat(debug, debug->iindx)) return false;\n        \n        (v->debuggerfn) (v, v->debuggerref);\n    }\n    return debug;\n}\n\n/* **********************************************************************\n * Run a program with debugging active\n * ********************************************************************** */\n\n/** Run a program with debugging\n * @param[in] v - the virtual machine to use\n * @param[in] p - program to run\n * @returns true on success, false otherwise */\nbool morpho_debug(vm *v, program *p) {\n    debugger debug;\n\n    debugger_init(&debug, p);\n    v->debug=&debug;\n    \n    bool success=morpho_run(v, p);\n    \n    debugger_clear(&debug);\n    \n    return success;\n}\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\n/** Intialize the debugger library */\nvoid debugger_initialize(void) {\n    morpho_defineerror(DEBUGGER_FINDSYMBOL, ERROR_DEBUGGER, DEBUGGER_FINDSYMBOL_MSG);\n    morpho_defineerror(DEBUGGER_SETPROPERTY, ERROR_DEBUGGER, DEBUGGER_SETPROPERTY_MSG);\n    morpho_defineerror(DEBUGGER_INVLDREGISTER, ERROR_DEBUGGER, DEBUGGER_INVLDREGISTER_MSG);\n    morpho_defineerror(DEBUGGER_INVLDGLOBAL, ERROR_DEBUGGER, DEBUGGER_INVLDGLOBAL_MSG);\n    morpho_defineerror(DEBUGGER_INVLDINSTR, ERROR_DEBUGGER, DEBUGGER_INVLDINSTR_MSG);\n    morpho_defineerror(DEBUGGER_REGISTEROBJ, ERROR_DEBUGGER, DEBUGGER_REGISTEROBJ_MSG);\n    morpho_defineerror(DEBUGGER_SYMBOLPROP, ERROR_DEBUGGER, DEBUGGER_SYMBOLPROP_MSG);\n}\n"
  },
  {
    "path": "src/debug/debug.h",
    "content": "/** @file debug.h\n *  @author T J Atherton\n *\n *  @brief Debugger, dissassembly and other tools\n */\n\n#ifndef debug_h\n#define debug_h\n\n#include <stdio.h>\n#include \"syntaxtree.h\"\n#include \"object.h\"\n#include \"program.h\"\n#include \"debugannotation.h\"\n\n#define DEBUG_ISSINGLESTEP(d) ((d) && (d->singlestep))\n\n/* -------------------------------------------------------\n * Debugger error messages\n * ------------------------------------------------------- */\n\n#define DEBUGGER_FINDSYMBOL                \"DbgSymbl\"\n#define DEBUGGER_FINDSYMBOL_MSG            \"Can't find symbol '%s' in current context.\"\n\n#define DEBUGGER_SETPROPERTY               \"DbgStPrp\"\n#define DEBUGGER_SETPROPERTY_MSG           \"Object does not support setting properties.\"\n\n#define DEBUGGER_INVLDREGISTER             \"DbgInvldRg\"\n#define DEBUGGER_INVLDREGISTER_MSG         \"Invalid register.\"\n\n#define DEBUGGER_INVLDGLOBAL               \"DbgInvldGlbl\"\n#define DEBUGGER_INVLDGLOBAL_MSG           \"Invalid global.\"\n\n#define DEBUGGER_INVLDINSTR                \"DbgInvldInstr\"\n#define DEBUGGER_INVLDINSTR_MSG            \"Invalid instruction.\"\n\n#define DEBUGGER_REGISTEROBJ               \"DbgRgObj\"\n#define DEBUGGER_REGISTEROBJ_MSG           \"Register %i does not contain an object.\"\n\n#define DEBUGGER_SYMBOLPROP                \"DbgSymblPrpty\"\n#define DEBUGGER_SYMBOLPROP_MSG            \"Symbol lacks property '%s'.\"\n\n/* -------------------------------------------------------\n * Debugger interface\n * ------------------------------------------------------- */\n\nbool debug_infofromindx(program *code, instructionindx indx, value *module, int *line, int *posn, objectfunction **func, objectclass **klass);\n\nvoid debugger_init(debugger *d, program *p);\nvoid debugger_clear(debugger *d);\n\nvoid debugger_seterror(debugger *d, error *err);\n\nvm *debugger_currentvm(debugger *d);\nbool debugger_isactive(debugger *d);\n\nvoid debugger_setsinglestep(debugger *d, bool singlestep);\nbool debugger_insinglestep(debugger *d);\n\nbool debugger_setbreakpoint(debugger *d, instructionindx indx);\nbool debugger_clearbreakpoint(debugger *d, instructionindx indx);\nbool debugger_shouldbreakat(debugger *d, instructionindx indx);\n\nvoid debugger_disassembleinstruction(vm *v, instruction instruction, instructionindx indx, value *konst, value *reg);\nvoid debugger_disassemble(vm *v, program *code, int *matchline);\n\nvoid debugger_garbagecollect(debugger *debug);\nvoid debugger_quit(debugger *debug);\n\nbool debugger_breakatinstruction(debugger *debug, bool set, instructionindx indx);\nbool debugger_breakatline(debugger *debug, bool set, value file, int line);\nbool debugger_breakatfunction(debugger *debug, bool set, value klass, value function);\n\nvoid debugger_showlocation(debugger *debug, instructionindx indx);\nbool debugger_showaddress(debugger *debug, indx rindx);\nvoid debugger_showbreakpoints(debugger *debug);\nvoid debugger_showglobals(debugger *debug);\nbool debugger_showglobal(debugger *debug, indx g);\nvoid debugger_showregisters(debugger *debug);\nvoid debugger_showstack(debugger *debug);\n\nbool debugger_showsymbol(debugger *debug, value symbol);\nbool debugger_showproperty(debugger *debug, value obj, value property);\nvoid debugger_showsymbols(debugger *debug);\n\nbool debugger_setregister(debugger *debug, indx reg, value val);\nbool debugger_setsymbol(debugger *debug, value symbol, value val);\nbool debugger_setproperty(debugger *debug, value symbol, value property, value val);\n\nbool debugger_enter(debugger *debug, vm *v);\n\nvoid debugger_initialize(void);\n\n#endif /* debug_h */\n"
  },
  {
    "path": "src/debug/profile.c",
    "content": "/** @file profile.c\n *  @author T J Atherton\n *\n *  @brief Profiler\n */\n\n#include \"profile.h\"\n#include \"strng.h\"\n#include \"platform.h\"\n\n/* **********************************************************************\n* Profiler\n* ********************************************************************** */\n\n#ifdef MORPHO_PROFILER\n\n#define PROFILER_NVALUES 2\n#define PROFILER_VALUEPOSN 1\n\n#define PROFILER_SAMPLINGINTERVAL (0.0001*CLOCKS_PER_SEC)\n\n#define PROFILER_GLOBAL \"(global)\"\n#define PROFILER_ANON   \"(anonymous)\"\n#define PROFILER_GC     \"(garbage collector)\"\n\n/** Record a sample */\nvoid profiler_sample(profiler *profile, value func) {\n    value v=MORPHO_INTEGER(1);\n    \n    if (dictionary_get(&profile->profile_dict, func, &v)) {\n        v=MORPHO_INTEGER(MORPHO_GETINTEGERVALUE(v)+1);\n    }\n    \n    dictionary_insert(&profile->profile_dict, func, v);\n}\n\n/** Profiler monitor thread */\nMorphoThreadFnReturnType profiler_thread(void *arg) {\n    vm *v = (vm *) arg;\n    profiler *profile = v->profiler;\n    if (!v->profiler) MorphoThread_exit();\n    clock_t last = clock();\n    clock_t time = last;\n    \n    while (true) {\n        MorphoMutex_lock(&profile->profile_lock);\n        bool quit=profile->profiler_quit;\n        MorphoMutex_unlock(&profile->profile_lock);\n        \n        if (quit) MorphoThread_exit(); \n        \n        while (time-last<PROFILER_SAMPLINGINTERVAL) time = clock();\n        last = time;\n        \n        objectbuiltinfunction *infunction=v->fp->inbuiltinfunction;\n        \n        if (v->status==VM_INGC) {\n            profiler_sample(profile, MORPHO_INTEGER(1));\n        } else if (infunction) {\n            profiler_sample(profile, MORPHO_OBJECT(infunction));\n        } else {\n            profiler_sample(profile, MORPHO_OBJECT(v->fp->function));\n        }\n    }\n}\n\n/** Initialize profiler data structure */\nvoid profiler_init(profiler *profile, program *p) {\n    dictionary_init(&profile->profile_dict);\n    MorphoMutex_init(&profile->profile_lock);\n    profile->profiler_quit=false;\n    profile->program=p;\n}\n\n/** Clear profiler data structure */\nvoid profiler_clear(profiler *profile) {\n    MorphoMutex_clear(&profile->profile_lock);\n    dictionary_clear(&profile->profile_dict);\n}\n\n/** Kills a profiler thread */\nvoid profiler_kill(profiler *profile) {\n    MorphoMutex_lock(&profile->profile_lock);\n    profile->profiler_quit=true;\n    MorphoMutex_unlock(&profile->profile_lock);\n    MorphoThread_join(profile->profiler);\n    MorphoThread_clear(profile->profiler);\n}\n\n/** Sorting function */\nint profiler_sort(const void *a, const void *b) {\n    value *aa = (value *) a;\n    value *bb = (value *) b;\n    return morpho_comparevalue(aa[PROFILER_VALUEPOSN], bb[PROFILER_VALUEPOSN]);\n}\n\n/** Returns the function name */\nbool profiler_getname(value func, value *name, value *klass) {\n    bool success=false;\n    objectclass *k = NULL;\n    \n    if (MORPHO_ISINTEGER(func)) {\n        *name = MORPHO_NIL; // In the Garbage collector\n        success=true;\n    } else if (MORPHO_ISBUILTINFUNCTION(func)) {\n        *name = MORPHO_GETBUILTINFUNCTION(func)->name;\n        k=MORPHO_GETBUILTINFUNCTION(func)->klass;\n        success=true;\n    } else if (MORPHO_ISFUNCTION(func)) {\n        *name = MORPHO_GETFUNCTION(func)->name;\n        k=MORPHO_GETFUNCTION(func)->klass;\n        success=true;\n    }\n    \n    *klass = (k ? k->name : MORPHO_NIL);\n    return success;\n}\n\n/** Calculates the length to display */\nsize_t profiler_calculatelength(profiler *p, value func) {\n    value name, klass;\n    size_t length=0;\n    \n    if (profiler_getname(func, &name, &klass)) {\n        if (MORPHO_ISSTRING(name)) {\n            length+=MORPHO_GETSTRINGLENGTH(name);\n        } else if (MORPHO_ISINTEGER(func)) {\n            length+=strlen(PROFILER_GC);\n        } else if (MORPHO_ISNIL(name)) {\n            if (MORPHO_ISSAME(func, MORPHO_OBJECT(p->program->global))) length+=strlen(PROFILER_GLOBAL);\n            else length+=strlen(PROFILER_ANON);\n        }\n                 \n        if (MORPHO_ISSTRING(klass)) length+=MORPHO_GETSTRINGLENGTH(klass)+1; // extra length for the '.'\n    }\n    \n    return length;\n}\n\n/** Display the function name and its sampling count */\nvoid profiler_display(profiler *p, value func, vm *v) {\n    value name, klass;\n    if (profiler_getname(func, &name, &klass)) {\n        if (MORPHO_ISSTRING(klass)) {\n            morpho_printvalue(v, klass);\n            morpho_printf(v, \".\");\n        }\n        \n        if (MORPHO_ISSTRING(name)) {\n            morpho_printvalue(v, name);\n        } else if (MORPHO_ISINTEGER(func)) {\n            morpho_printf(v, PROFILER_GC);\n        } else if (MORPHO_ISNIL(name)) {\n            if (MORPHO_ISSAME(func, MORPHO_OBJECT(p->program->global))) morpho_printf(v, PROFILER_GLOBAL);\n            else morpho_printf(v, PROFILER_ANON);\n        }\n    }\n}\n\n/** Report the outcome of profiling */\nvoid profiler_report(profiler *profile, vm *v) {\n    varray_value samples;\n    varray_valueinit(&samples);\n    \n    for (unsigned int i=0; i<profile->profile_dict.capacity; i++) {\n        if (!MORPHO_ISNIL(profile->profile_dict.contents[i].key)) {\n            varray_valuewrite(&samples, profile->profile_dict.contents[i].key);\n            varray_valuewrite(&samples, profile->profile_dict.contents[i].val);\n        }\n    }\n    \n    qsort(samples.data, samples.count/PROFILER_NVALUES, sizeof(value)*PROFILER_NVALUES, profiler_sort);\n    \n    /* Calculate the length of the names to display */\n    long nsamples = 0;\n    size_t maxlength = 0;\n    for (unsigned int i=0; i<samples.count; i+=PROFILER_NVALUES) {\n        size_t length = profiler_calculatelength(profile, samples.data[i]);\n        if (length>maxlength) maxlength = length;\n        \n        nsamples += (long) MORPHO_GETINTEGERVALUE(samples.data[i+PROFILER_VALUEPOSN]);\n    }\n    \n    // Now report the output\n    morpho_printf(v, \"===Profiler output: Execution took %.3f seconds with %ld samples===\\n\", ((double) profile->end - profile->start)/((double) CLOCKS_PER_SEC), nsamples);\n    for (unsigned int i=0; i<samples.count; i+=PROFILER_NVALUES) {\n        profiler_display(profile, samples.data[i], v); // Display the function or method\n        size_t length = profiler_calculatelength(profile, samples.data[i]);\n        for (int j=0; j<maxlength-length; j++) morpho_printf(v, \" \");\n        \n        int fsamples = MORPHO_GETINTEGERVALUE(samples.data[i+PROFILER_VALUEPOSN]); // Display the count\n        morpho_printf(v, \" %.2f%% [%i samples]\\n\", 100.0*((float) fsamples)/nsamples, fsamples);\n    }\n    morpho_printf(v, \"===\\n\");\n    \n    varray_valueclear(&samples);\n}\n\n/** Profile the execution of a program\n * @param[in] v - the virtual machine to use\n * @param[in] p - program to run\n * @returns true on success, false otherwise */\nbool morpho_profile(vm *v, program *p) {\n    profiler profile;\n\n    profiler_init(&profile, p);\n    \n    if (MorphoThread_create(&profile.profiler, profiler_thread, v)) {\n        UNREACHABLE(\"Unable to run profiler.\");\n    }\n    \n    v->profiler=&profile;\n    \n    profile.start=clock();\n    bool success=morpho_run(v, p);\n    profile.end=clock();\n    \n    profiler_kill(&profile);\n    \n    profiler_report(&profile, v);\n    profiler_clear(&profile);\n    \n    return success;\n}\n\n#else\n\nbool morpho_profile(vm *v, program *p) {\n    return morpho_run(v, p);\n}\n\n#endif\n"
  },
  {
    "path": "src/debug/profile.h",
    "content": "/** @file profile.h\n *  @author T J Atherton\n *\n *  @brief Profiler\n */\n\n#ifndef profile_h\n#define profile_h\n\n#include \"compile.h\"\n#include \"vm.h\"\n#include \"morpho.h\"\n\nbool morpho_profile(vm *v, program *p);\n\n#endif /* profile_h */\n"
  },
  {
    "path": "src/geometry/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        fespace.c           fespace.h\n        field.c             field.h\n        functional.c        functional.h\n        geometry.c          geometry.h\n        integrate.c         integrate.h\n        mesh.c              mesh.h\n        selection.c         selection.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        fespace.h\n        field.h\n        functional.h\n        geometry.h\n        integrate.h\n        mesh.h\n        selection.h\n)\n"
  },
  {
    "path": "src/geometry/fespace.c",
    "content": "/** @file fespace.c\n *  @author T J Atherton\n *\n *  @brief Finite element fespaces\n */\n\n#include \"geometry.h\"\n\n/* **********************************************************************\n * Discretization objects\n * ********************************************************************** */\n\nobjecttype objectfespacetype;\n\n/** Field object definitions */\nvoid objectfespace_printfn(object *obj, void *v) {\n    objectfespace *disc=(objectfespace *) obj;\n    morpho_printf(v, \"<FunctionSpace %s>\", disc->fespace->name);\n}\n\nsize_t objectfespace_sizefn(object *obj) {\n    return sizeof(objectfespace);\n}\n\nobjecttypedefn objectfespacedefn = {\n    .printfn=objectfespace_printfn,\n    .markfn=NULL,\n    .freefn=NULL,\n    .sizefn=objectfespace_sizefn\n};\n\n/** Creates a new fespace object\n * @param[in] fespace - fespace definition to use */\nobjectfespace *objectfespace_new(fespace *disc) {\n    objectfespace *new = (objectfespace *) object_new(sizeof(objectfespace), OBJECT_FESPACE);\n    if (new) new->fespace=disc;\n    \n    return new;\n}\n\n/* **********************************************************************\n * Discretization definitions\n * ********************************************************************** */\n\n#define LINE_OPCODE 1\n#define AREA_OPCODE 2\n#define QUANTITY_OPCODE 255\n\n#define LINE(id, v1, v2)      LINE_OPCODE, id, v1, v2           // Identify a grade 1 subelement given by two vertex indices\n#define AREA(id, v1, v2, v3)  AREA_OPCODE, id, v1, v2, v3       // Identify a grade 2 subelement given by three vertex indices\n#define QUANTITY(grade, id, qno) QUANTITY_OPCODE, grade, id, qno  // Fetch quantity from subelement of grade with id and quantity number\n#define ENDDEFN -1\n\n/* -------------------------------------------------------\n * CG1 element in 1D\n * ------------------------------------------------------- */\n\n/*\n *   0 - 1    // One degree of freedom per vertex\n */\n\nvoid cg1_1dinterpolate(double *lambda, double *wts) {\n    wts[0]=lambda[0];\n    wts[1]=lambda[1];\n}\n\nvoid cg1_1dgrad(double *lambda, double *grad) {\n    double g[] =\n    { 1, 0,\n      0, 1 };\n    memcpy(grad, g, sizeof(g));\n}\n\nunsigned int cg1_1dshape[] = { 1, 0 };\n\ndouble cg1_1dnodes[] = { 0.0, 1.0 };\n\neldefninstruction cg1_1ddefn[] = {\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    ENDDEFN\n};\n\nfespace cg1_1d = {\n    .name = \"CG1\",\n    .grade = 1,\n    .shape = cg1_1dshape,\n    .degree = 1,\n    .nnodes = 2,\n    .nsubel = 0,\n    .nodes = cg1_1dnodes,\n    .ifn = cg1_1dinterpolate,\n    .gfn = cg1_1dgrad,\n    .eldefn = cg1_1ddefn,\n    .lower = NULL\n};\n\n/* -------------------------------------------------------\n * CG2 element in 1D\n * ------------------------------------------------------- */\n\n/*\n *   0 - 2 - 1    // One degree of freedom per vertex; one at the midpoint\n */\n\nvoid cg2_1dinterpolate(double *lambda, double *wts) {\n    double dl = (lambda[0]-lambda[1]);\n    wts[0]=lambda[0]*dl;\n    wts[1]=-lambda[1]*dl;\n    wts[2]=4*lambda[0]*lambda[1];\n}\n\nvoid cg2_1dgrad(double *lambda, double *grad) {\n    // Gij = d Xi[i] / d lambda[j]\n    // Note this is in column-major order!\n    double g[] =\n    { 2*lambda[0]-lambda[1],            -lambda[1], 4*lambda[1],\n                 -lambda[0], 2*lambda[1]-lambda[0], 4*lambda[0] };\n    memcpy(grad, g, sizeof(g));\n}\n\n\nunsigned int cg2_1dshape[] = { 1, 1 };\n\ndouble cg2_1dnodes[] = { 0.0, 1.0, 0.5 };\n\neldefninstruction cg2_1ddefn[] = {\n    LINE(0,0,1),     // Identify line subelement with vertex indices (0,1)\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    QUANTITY(1,0,0), // Fetch quantity from line subelement\n    ENDDEFN\n};\n\nfespace cg2_1d = {\n    .name = \"CG2\",\n    .grade = 1,\n    .shape = cg2_1dshape,\n    .degree = 2,\n    .nnodes = 3,\n    .nsubel = 1,\n    .nodes = cg2_1dnodes,\n    .ifn = cg2_1dinterpolate,\n    .gfn = cg2_1dgrad,\n    .eldefn = cg2_1ddefn,\n    .lower = NULL\n};\n\n/* -------------------------------------------------------\n * CG3 element in 1D\n * ------------------------------------------------------- */\n\n/*\n *   0 - 2 - 3 - 1    // One degree of freedom per vertex; two on the line\n */\n\nvoid cg3_1dinterpolate(double *lambda, double *wts) {\n    double a = (9.0/2.0)*lambda[0]*lambda[1];\n    wts[0]=lambda[0]*(1-a);\n    wts[1]=lambda[1]*(1-a);\n    wts[2]=a*(2*lambda[0]-lambda[1]);\n    wts[3]=a*(2*lambda[1]-lambda[0]);\n}\n\nunsigned int cg3_1dshape[] = { 1, 2 };\n\ndouble cg3_1dnodes[] = { 0.0, 1.0, 1.0/3.0, 2.0/3.0 };\n\neldefninstruction cg3_1ddefn[] = {\n    LINE(0,0,1),     // Identify line subelement with vertex indices (0,1)\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    QUANTITY(1,0,0), // Fetch quantity from line subelement\n    QUANTITY(1,0,1), // Fetch quantity from line subelement\n    ENDDEFN\n};\n\nfespace cg3_1d = {\n    .name = \"CG3\",\n    .grade = 1,\n    .shape = cg3_1dshape,\n    .degree = 3,\n    .nnodes = 4,\n    .nsubel = 1,\n    .nodes = cg3_1dnodes,\n    .ifn = cg3_1dinterpolate,\n    .eldefn = cg3_1ddefn,\n    .lower = NULL\n};\n\n/* -------------------------------------------------------\n * CG1 element in 2D\n * ------------------------------------------------------- */\n\n/*   2\n *   |\\\n *   0-1    // One degree of freedom per vertex\n */\n\nvoid cg1_2dinterpolate(double *lambda, double *wts) {\n    wts[0]=lambda[0];\n    wts[1]=lambda[1];\n    wts[2]=lambda[2];\n}\n\nvoid cg1_2dgrad(double *lambda, double *grad) {\n    double g[] =\n    { 1, 0, 0,\n      0, 1, 0,\n      0, 0, 1 };\n    memcpy(grad, g, sizeof(g));\n}\n\nunsigned int cg1_2dshape[] = { 1, 0, 0 };\n\ndouble cg1_2dnodes[] = { 0.0, 0.0,\n                         1.0, 0.0,\n                         0.0, 1.0 };\n\neldefninstruction cg1_2deldefn[] = {\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    QUANTITY(0,2,0), // Fetch quantity on vertex 2\n    ENDDEFN\n};\n\nfespace *cg1_2d_lower[] = {\n    &cg1_1d,\n    NULL\n};\n\nfespace cg1_2d = {\n    .name = \"CG1\",\n    .grade = 2,\n    .shape = cg1_2dshape,\n    .degree = 1,\n    .nnodes = 3,\n    .nsubel = 0,\n    .nodes = cg1_2dnodes,\n    .ifn = cg1_2dinterpolate,\n    .gfn = cg1_2dgrad,\n    .eldefn = cg1_2deldefn,\n    .lower = cg1_2d_lower\n};\n\n/* -------------------------------------------------------\n * CG2 element in 2D\n * ------------------------------------------------------- */\n\n/*   2\n *   |\\\n *   5 4\n *   |  \\\n *   0-3-1    // One degree of freedom per vertex; one at the midpoint\n */\n\nvoid cg2_2dinterpolate(double *lambda, double *wts) {\n    wts[0]=lambda[0]*(2*lambda[0]-1);\n    wts[1]=lambda[1]*(2*lambda[1]-1);\n    wts[2]=lambda[2]*(2*lambda[2]-1);\n    wts[3]=4*lambda[0]*lambda[1];\n    wts[4]=4*lambda[1]*lambda[2];\n    wts[5]=4*lambda[2]*lambda[0];\n}\n\nvoid cg2_2dgrad(double *lambda, double *grad) {\n    // Gij = d Xi[i] / d lambda[j]\n    // Note this is in column-major order!\n    double g[] =\n    { 4*lambda[0]-1,             0,             0, 4*lambda[1],           0, 4*lambda[2],\n                  0, 4*lambda[1]-1,             0, 4*lambda[0], 4*lambda[2],           0,\n                  0,             0, 4*lambda[2]-1,           0, 4*lambda[1], 4*lambda[0] };\n    memcpy(grad, g, sizeof(g));\n}\n\nunsigned int cg2_2dshape[] = { 1, 1, 0 };\n\ndouble cg2_2dnodes[] = { 0.0, 0.0,\n                         1.0, 0.0,\n                         0.0, 1.0,\n                         0.5, 0.0,\n                         0.5, 0.5,\n                         0.0, 0.5 };\n\neldefninstruction cg2_2deldefn[] = {\n    LINE(0,0,1),     // Identify line subelement with vertex indices (0,1)\n    LINE(1,1,2),     // Identify line subelement with vertex indices (1,2)\n    LINE(2,2,0),     // Identify line subelement with vertex indices (2,0)\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    QUANTITY(0,2,0), // Fetch quantity on vertex 2\n    QUANTITY(1,0,0), // Fetch quantity from line 0\n    QUANTITY(1,1,0), // Fetch quantity from line 1\n    QUANTITY(1,2,0), // Fetch quantity from line 2\n    ENDDEFN\n};\n\nfespace *cg2_2d_lower[] = {\n    &cg2_1d,\n    NULL\n};\n\nfespace cg2_2d = {\n    .name = \"CG2\",\n    .grade = 2,\n    .shape = cg2_2dshape,\n    .degree = 2,\n    .nnodes = 6,\n    .nsubel = 3,\n    .nodes = cg2_2dnodes,\n    .ifn = cg2_2dinterpolate,\n    .gfn = cg2_2dgrad,\n    .eldefn = cg2_2deldefn,\n    .lower = cg2_2d_lower\n};\n\n/* -------------------------------------------------------\n * CG1 element in 3D\n * ------------------------------------------------------- */\n\n/*   z=0    z=1\n *   2\n *   |\\\n *   0-1    3 // One degree of freedom per vertex\n */\n\nvoid cg1_3dinterpolate(double *lambda, double *wts) {\n    wts[0]=lambda[0];\n    wts[1]=lambda[1];\n    wts[2]=lambda[2];\n    wts[3]=lambda[3];\n}\n\nvoid cg1_3dgrad(double *lambda, double *grad) {\n    double g[] =\n    { 1, 0, 0, 0,\n      0, 1, 0, 0,\n      0, 0, 1, 0,\n      0, 0, 0, 1 };\n    memcpy(grad, g, sizeof(g));\n}\n\nunsigned int cg1_3dshape[] = { 1, 0, 0, 0 };\n\ndouble cg1_3dnodes[] = { 0.0, 0.0, 0.0,\n                         1.0, 0.0, 0.0,\n                         0.0, 1.0, 0.0,\n                         0.0, 0.0, 1.0 };\n\neldefninstruction cg1_3deldefn[] = {\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    QUANTITY(0,2,0), // Fetch quantity on vertex 2\n    QUANTITY(0,3,0), // Fetch quantity on vertex 3\n    ENDDEFN\n};\n\nfespace *cg1_3d_lower[] = {\n    &cg1_2d,\n    &cg1_1d,\n    NULL\n};\n\nfespace cg1_3d = {\n    .name = \"CG1\",\n    .grade = 3,\n    .shape = cg1_3dshape,\n    .degree = 1,\n    .nnodes = 4,\n    .nsubel = 0,\n    .nodes = cg1_3dnodes,\n    .ifn = cg1_3dinterpolate,\n    .gfn = cg1_3dgrad,\n    .eldefn = cg1_3deldefn,\n    .lower = cg1_3d_lower\n};\n\n/* -------------------------------------------------------\n * CG2 element in 3D\n * ------------------------------------------------------- */\n\n/*   z=0       z=0.5     z=1\n *   2\n *   |\\\n *   6 5       9\n *   |  \\      | \\\n *   0-4-1     7--8      3  - i.e. vertices\n */\n\nvoid cg2_3dinterpolate(double *lambda, double *wts) {\n    wts[0]=lambda[0]*(2*lambda[0]-1);\n    wts[1]=lambda[1]*(2*lambda[1]-1);\n    wts[2]=lambda[2]*(2*lambda[2]-1);\n    wts[3]=lambda[3]*(2*lambda[3]-1);\n    wts[4]=4*lambda[0]*lambda[1];\n    wts[5]=4*lambda[1]*lambda[2];\n    wts[6]=4*lambda[2]*lambda[0];\n    wts[7]=4*lambda[0]*lambda[3];\n    wts[8]=4*lambda[1]*lambda[3];\n    wts[9]=4*lambda[2]*lambda[3];\n}\n\nvoid cg2_3dgrad(double *lambda, double *grad) { // TODO: FIX\n    // Gij = d Xi[i] / d lambda[j]\n    // Note this is in column-major order!\n    double g[] =\n    { 4*lambda[0]-1,             0,             0,             0,\n        4*lambda[1],             0,   4*lambda[2],   4*lambda[3],            0,             0,\n        \n                  0, 4*lambda[1]-1,             0,             0,\n        4*lambda[0],   4*lambda[2],             0,             0,  4*lambda[3],             0,\n        \n                  0,             0, 4*lambda[2]-1,             0,\n                  0,   4*lambda[1],   4*lambda[0],             0,            0,   4*lambda[3],\n        \n                  0,             0,             0, 4*lambda[3]-1,\n                  0,             0,             0,   4*lambda[0],  4*lambda[1],   4*lambda[2]\n    };\n    \n    memcpy(grad, g, sizeof(g));\n}\n\nunsigned int cg2_3dshape[] = { 1, 1, 0, 0 };\n\ndouble cg2_3dnodes[] = { 0,     0,   0,\n                         1,     0,   0,\n                         0,     1,   0,\n                         0,     0,   1,\n                         0.5,   0,   0,\n                         0.5, 0.5,   0,\n                         0,   0.5,   0,\n                         0,     0, 0.5,\n                         0.5,   0, 0.5,\n                         0,   0.5, 0.5 };\n\neldefninstruction cg2_3deldefn[] = {\n    LINE(0,0,1),     // Identify line subelement with vertex indices (0,1)\n    LINE(1,1,2),     // Identify line subelement with vertex indices (1,2)\n    LINE(2,2,0),     // Identify line subelement with vertex indices (2,0)\n    LINE(3,0,3),     // Identify line subelement with vertex indices (0,3)\n    LINE(4,1,3),     // Identify line subelement with vertex indices (1,3)\n    LINE(5,2,3),     // Identify line subelement with vertex indices (2,3)\n    QUANTITY(0,0,0), // Fetch quantity on vertex 0\n    QUANTITY(0,1,0), // Fetch quantity on vertex 1\n    QUANTITY(0,2,0), // Fetch quantity on vertex 2\n    QUANTITY(0,3,0), // Fetch quantity on vertex 3\n    QUANTITY(1,0,0), // Fetch quantity from line 0\n    QUANTITY(1,1,0), // Fetch quantity from line 1\n    QUANTITY(1,2,0), // Fetch quantity from line 2\n    QUANTITY(1,3,0), // Fetch quantity from line 3\n    QUANTITY(1,4,0), // Fetch quantity from line 4\n    QUANTITY(1,5,0), // Fetch quantity from line 5\n    ENDDEFN\n};\n\nfespace *cg2_3d_lower[] = {\n    &cg2_2d,\n    &cg2_1d,\n    NULL\n};\n\nfespace cg2_3d = {\n    .name = \"CG2\",\n    .grade = 3,\n    .shape = cg2_3dshape,\n    .degree = 2,\n    .nnodes = 10,\n    .nsubel = 6,\n    .nodes = cg2_3dnodes,\n    .ifn = cg2_3dinterpolate,\n    .gfn = cg2_3dgrad,\n    .eldefn = cg2_3deldefn,\n    .lower = cg2_3d_lower\n};\n\n/* -------------------------------------------------------\n * List of finite elements\n * ------------------------------------------------------- */\n\nfespace *fespaces[] = {\n    &cg1_1d,\n    &cg2_1d,\n    &cg1_2d,\n    &cg2_2d,\n    &cg1_3d,\n    &cg2_3d,\n    NULL\n};\n\n/* **********************************************************************\n * Discretization functions\n * ********************************************************************** */\n\n/** Find a fespace definition based on a name and grade */\nfespace *fespace_find(char *name, grade g) {\n    for (int i=0; fespaces[i]!=NULL; i++) {\n        if (strcmp(name, fespaces[i]->name)==0 &&\n            g==fespaces[i]->grade) return fespaces[i];\n    }\n    return NULL;\n}\n\n/** Finds a linear fespace for a given grade */\nfespace *fespace_findlinear(grade g) {\n    for (int i=0; fespaces[i]!=NULL; i++) {\n        if (fespaces[i]->grade && fespaces[i]->degree==1) return fespaces[i];\n    }\n    return NULL;\n}\n\n#define FETCH(instr) (*(instr++))\n\n/** Steps through an element definition, generating subelements and identifying quantities */\nbool fespace_doftofieldindx(objectfield *field, fespace *disc, int nv, int *vids, fieldindx *findx) {\n    elementid subel[disc->nsubel+1]; // Element IDs of sub elements\n    int sid, svids[nv], nmatch, k=0;\n    \n    objectsparse *vmatrix[disc->grade+1]; // Vertex->elementid connectivity matrices\n    for (grade g=0; g<=disc->grade; g++) vmatrix[g]=mesh_addconnectivityelement(field->mesh, g, 0);\n    \n    for (eldefninstruction *instr=disc->eldefn; instr!=NULL && *instr!=ENDDEFN; ) {\n        eldefninstruction op=FETCH(instr);\n        switch(op) {\n            case LINE_OPCODE: // Find an element defined by n vertices\n            case AREA_OPCODE: // TODO: Need to cope with (mis) orientation of these subelements\n            {\n                sid = FETCH(instr);\n                for (int i=0; i<=op; i++) svids[i] = vids[FETCH(instr)];\n                \n                if (!mesh_matchelements(vmatrix[1], op, op+1, svids, 1, &nmatch, &subel[sid])) return false;\n            }\n                break;\n            case QUANTITY_OPCODE:\n            {\n                findx[k].g=FETCH(instr);\n                int sid=FETCH(instr);\n                findx[k].id=(findx[k].g==0 ? vids[sid]: subel[sid]);\n                findx[k].indx=FETCH(instr);\n                k++;\n            }\n                break;\n            default:\n                UNREACHABLE(\"Error in finite element definition\");\n        }\n    }\n    return true;\n}\n\n/** Searches a fespace's lower list to find a fespace to use on a lower grade */\nbool fespace_lower(fespace *disc, grade target, fespace **out) {\n    if (disc->lower) for (int i=0; disc->lower[i]!=NULL; i++) {\n        if (disc->lower[i]->grade==target) {\n            *out = disc->lower[i];\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Constructs a layout matrix that maps element ids (columns) to degree of freedom indices in a field */\nbool fespace_layout(objectfield *field, fespace *disc, objectsparse **out) {\n    objectsparse *conn = mesh_getconnectivityelement(field->mesh, 0, disc->grade);\n    elementid nel=mesh_nelements(conn);\n    \n    objectsparse *new = object_newsparse(NULL, NULL);\n    if (!new) return false;\n    sparseccs_resize(&new->ccs, field->nelements, nel, nel*disc->nnodes, NULL);\n    \n    for (elementid id=0; id<nel; id++) {\n        int nv, *vids;\n        if (!mesh_getconnectivity(conn, id, &nv, &vids)) goto fespace_layout_cleanup;\n     \n        new->ccs.cptr[id]=id*disc->nnodes;\n        fieldindx findx[disc->nnodes];\n        if (!fespace_doftofieldindx(field, disc, nv, vids, findx)) goto fespace_layout_cleanup;\n        for (int i=0; i<disc->nnodes; i++) {\n            if (!field_getindex(field, findx[i].g, findx[i].id, findx[i].indx, new->ccs.rix+new->ccs.cptr[id]+i)) goto fespace_layout_cleanup;\n        }\n    }\n    new->ccs.cptr[nel]=nel*disc->nnodes; // Last column pointer points to next column\n    \n    *out=new;\n    return true;\n    \nfespace_layout_cleanup:\n    if (new) object_free((object *) new);\n    return false;\n}\n\n/** @brief Calculates the gradient of the basis functions with respect to the reference coordinates.\n *  @param[in] disc - fespace to query\n *  @param[in] lambda - position in barycentric coordinates\n *  @param[out] grad - gradient of basis functions with respect to reference coordinates (disc->nnodes x disc->grade)\n */\nvoid fespace_gradient(fespace *disc, double *lambda, objectmatrix *grad) {\n    int nbary = disc->grade+1;\n    \n    // Compute gradients of the basis functions with respect to barycentric coordinates\n    double gdata[disc->nnodes*nbary];\n    (disc->gfn) (lambda, gdata);\n    \n    for (int i=0; i<disc->grade; i++) {\n        functional_vecsub(disc->nnodes, gdata+(i+1)*disc->nnodes, gdata, grad->elements+i*disc->nnodes);\n    }\n}\n\n/* **********************************************************************\n * FunctionSpace class\n * ********************************************************************** */\n\n/** Constructs a fespace object */\nvalue fespace_constructor(vm *v, int nargs, value *args) {\n    value grd=MORPHO_INTEGER(1);\n    value out=MORPHO_NIL;\n    int nfixed;\n    \n    if (!builtin_options(v, nargs, args, &nfixed, 1, field_gradeoption, &grd))\n        morpho_runtimeerror(v, FNSPC_ARGS);\n    \n    if (nfixed==1 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISINTEGER(grd)) {\n        char *label = MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)); \n        \n        fespace *d=fespace_find(label, MORPHO_GETINTEGERVALUE(grd));\n        \n        if (d) {\n            objectfespace *obj=objectfespace_new(d);\n            if (obj) {\n                out = MORPHO_OBJECT(obj);\n                morpho_bindobjects(v, 1, &out);\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        } else morpho_runtimeerror(v, FNSPC_NOTFOUND, label, MORPHO_GETINTEGERVALUE(grd));\n        \n    } else morpho_runtimeerror(v, FNSPC_ARGS);\n    \n    return out;\n}\n\nvalue FiniteElementSpace_layout(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectfespace *slf = MORPHO_GETFESPACE(MORPHO_SELF(args));\n    if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectfield *field = MORPHO_GETFIELD(MORPHO_GETARG(args, 0));\n        objectsparse *new;\n        \n        if (fespace_layout(field, slf->fespace, &new)) {\n            out=MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n    }\n    return out;\n}\n\nMORPHO_BEGINCLASS(FiniteElementSpace)\nMORPHO_METHOD(FINITEELEMENTSPACE_LAYOUT_METHOD, FiniteElementSpace_layout, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nvoid fespace_initialize(void) {\n    objectfespacetype=object_addtype(&objectfespacedefn);\n    \n    builtin_addfunction(FINITEELEMENTSPACE_CLASSNAME, fespace_constructor, BUILTIN_FLAGSEMPTY);\n    \n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value fespaceclass=builtin_addclass(FINITEELEMENTSPACE_CLASSNAME, MORPHO_GETCLASSDEFINITION(FiniteElementSpace), objclass);\n    object_setveneerclass(OBJECT_FESPACE, fespaceclass);\n    \n    morpho_defineerror(FNSPC_ARGS, ERROR_HALT, FNSPC_ARGS_MSG);\n    morpho_defineerror(FNSPC_NOTFOUND, ERROR_HALT, FNSPC_NOTFOUND_MSG);\n}\n"
  },
  {
    "path": "src/geometry/fespace.h",
    "content": "/** @file fespace.h\n *  @author T J Atherton\n *\n *  @brief Finite element fespaces\n */\n\n#ifndef fespace_h\n#define fespace_h\n\n#include \"geometry.h\"\n\n/* -------------------------------------------------------\n * Discretization type definitions\n * ------------------------------------------------------- */\n\n/** @brief Interpolation functions are called to assign weights to the nodes given barycentric coordinates */\ntypedef void (*interpolationfn) (double *, double *);\n\n/** @brief Element definitions comprise a sequence of instructions to map field degrees of freedom to local nodes */\ntypedef int eldefninstruction;\n\n/** @brief Discretization definitions */\ntypedef struct sfespace {\n    char *name; /**  Name of the fespace */\n    grade grade; /** Grade of element this fespace is defined on */\n    unsigned int *shape; /** Number of degrees of freedom on each grade; must have grade+1 entries */\n    int degree; /** Highest degree of polynomial represented by this element */\n    int nnodes; /** Number of nodes for this element type */\n    int nsubel; /** Number of subelements used by */\n    double *nodes; /** Node positions */\n    interpolationfn ifn; /** Interpolation function; receives barycentric coordinates as input and returns weights per node */\n    interpolationfn gfn; /** Gradient interpolation function */\n    eldefninstruction *eldefn; /** Element definition */\n    struct sfespace **lower; /** Discretization to be used for interpolation on lower grades */\n} fespace;\n\n/* -------------------------------------------------------\n * Discretization object type\n * ------------------------------------------------------- */\n\nextern objecttype objectfespacetype;\n#define OBJECT_FESPACE objectfespacetype\n\ntypedef struct {\n    object obj;\n    fespace *fespace;\n} objectfespace;\n\n/** Tests whether an object is a fespace */\n#define MORPHO_ISFESPACE(val) object_istype(val, OBJECT_FESPACE)\n\n/** Gets the object as a fespace */\n#define MORPHO_GETFESPACE(val)   ((objectfespace *) MORPHO_GETOBJECT(val))\n\n/* -------------------------------------------------------\n * FunctionSpace veneer class\n * ------------------------------------------------------- */\n\n#define FINITEELEMENTSPACE_CLASSNAME \"FiniteElementSpace\"\n\n#define FINITEELEMENTSPACE_LAYOUT_METHOD \"layout\"\n\n/* -------------------------------------------------------\n * Discretization error messages\n * ------------------------------------------------------- */\n\n#define FNSPC_ARGS                       \"FnSpcArgs\"\n#define FNSPC_ARGS_MSG                   \"Function space must be initialized with a label and a grade.\"\n\n#define FNSPC_NOTFOUND                   \"FnSpcNtFnd\"\n#define FNSPC_NOTFOUND_MSG               \"Function space '%s' on grade %i not found.\"\n\n/* -------------------------------------------------------\n * Discretization interface\n * ------------------------------------------------------- */\n\nfespace *fespace_find(char *name, grade g);\nfespace *fespace_findlinear(grade g);\n\nbool fespace_doftofieldindx(objectfield *field, fespace *disc, int nv, int *vids, fieldindx *findx);\n\nbool fespace_lower(fespace *disc, grade target, fespace **out);\n\nbool fespace_layout(objectfield *field, fespace *disc, objectsparse **out);\nvoid fespace_gradient(fespace *disc, double *lambda, objectmatrix *grad);\n\nvoid fespace_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/geometry/field.c",
    "content": "/** @file field.c\n *  @author T J Atherton\n *\n *  @brief Fields\n */\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include \"field.h\"\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n#include \"matrix.h\"\n#include \"sparse.h\"\n#include \"geometry.h\"\n\nvalue field_gradeoption;\nvalue field_functionspaceoption;\n\n/* **********************************************************************\n * Field objects\n * ********************************************************************** */\n\nobjecttype objectfieldtype;\n\n/** Field object definitions */\nvoid objectfield_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Field>\");\n}\n\nvoid objectfield_markfn(object *obj, void *v) {\n    objectfield *c = (objectfield *) obj;\n    morpho_markvalue(v, c->prototype);\n    morpho_markvalue(v, c->fnspc);\n    morpho_markobject(v, (object *) c->mesh);\n}\n\nvoid objectfield_freefn(object *obj) {\n    objectfield *f = (objectfield *) obj;\n    \n    if (f->dof) MORPHO_FREE(f->dof);\n    if (f->offset) MORPHO_FREE(f->offset);\n    if (f->pool) MORPHO_FREE(f->pool);\n}\n\nsize_t objectfield_sizefn(object *obj) {\n    return sizeof(objectfield)+(((objectfield *) obj)->ngrades * sizeof(int));\n}\n\nobjecttypedefn objectfielddefn = {\n    .printfn=objectfield_printfn,\n    .markfn=objectfield_markfn,\n    .freefn=objectfield_freefn,\n    .sizefn=objectfield_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * Constructors\n * ********************************************************************** */\n\n/** Checks if a prototype object is acceptable */\nbool field_checkprototype(value v) {\n    return (MORPHO_ISNUMBER(v) || MORPHO_ISMATRIX(v) || MORPHO_ISSPARSE(v));\n}\n\nunsigned int field_sizeprototype(value prototype) {\n    unsigned int size = 1;\n    \n    if (MORPHO_ISMATRIX(prototype)) {\n        objectmatrix *m = (MORPHO_GETMATRIX(prototype));\n        size = m->ncols*m->nrows;\n    }\n    \n    return size;\n}\n\n/** Determines the overall size of storage required for the field\n * @param[in] mesh - mesh to use\n * @param[in] prototype - prototype object\n * @param[in] ngrades - size of grade array\n * @param[in] dof - number of degrees of freedom per grade\n * @param[out] offsets - offsets into the store (ngrades + 1 elements)\n * @returns the overall size of storage required */\nunsigned int field_size(objectmesh *mesh, value prototype, unsigned int ngrades, unsigned int *dof, unsigned int *offsets) {\n    unsigned int size = 0;\n    unsigned int psize = field_sizeprototype(prototype);\n    for (unsigned int i=0; i<ngrades; i++) offsets[i]=0;\n    \n    if (!dof) { // Assume 1 element per vertex\n        size=offsets[1]=mesh_nvertices(mesh)*psize;\n        for (grade i=2; i<ngrades; i++) offsets[i]=offsets[1];\n    } else {\n        for (grade i=0; i<ngrades; i++) {\n            unsigned int nel=mesh_nelementsforgrade(mesh, i);\n            offsets[i+1]=offsets[i]+nel*dof[i];\n            size=offsets[i+1]*psize;\n        }\n    }\n    \n    return size;\n}\n\n/** Creates a new field\n * @param[in] mesh - Mesh the field is attached to\n * @param[in] prototype - a prototype object\n * @param[in] disc - a prototype object\n * @param[in] shape -  (optional) number of degrees of freedom per entry in each grade (should be maxgrade entries) */\nobjectfield *object_newfield(objectmesh *mesh, value prototype, value fnspc, unsigned int *shape) {\n    int ngrades=mesh_maxgrade(mesh)+1;\n\n    unsigned int dof[ngrades]; // Extract shape from fespace or the provided function space\n    if (MORPHO_ISFESPACE(fnspc)) {\n        fespace *disc = MORPHO_GETFESPACE(fnspc)->fespace;\n        for (int i=0; i<=disc->grade; i++) dof[i]=disc->shape[i];\n        for (int i=disc->grade+1; i<ngrades; i++) dof[i]=0;\n    } else if (shape) {\n        for (int i=0; i<ngrades; i++) dof[i]=shape[i];\n    } else { // Default is simply functions on vertices\n        for (unsigned int i=0; i<ngrades; i++) dof[i]=0;\n        dof[0]=1;\n    }\n\n    unsigned int offset[ngrades+1];\n    unsigned int size=field_size(mesh, prototype, ngrades, dof, offset);\n    objectfield *new=NULL;\n    unsigned int *ndof = MORPHO_MALLOC(sizeof(int)*ngrades);\n    unsigned int *noffset = MORPHO_MALLOC(sizeof(unsigned int)*(ngrades+1));\n\n    if (ndof && noffset) {\n        new = (objectfield *) object_new(sizeof(objectfield)+sizeof(double)*size, OBJECT_FIELD);\n    }\n\n    if (new) {\n        new->mesh=mesh;\n        new->prototype=(MORPHO_ISNUMBER(prototype)? MORPHO_NIL : prototype);\n        new->psize=field_sizeprototype(prototype);\n        new->nelements=size/new->psize;\n        new->ngrades=ngrades;\n        new->fnspc=(MORPHO_ISFESPACE(fnspc) ? fnspc : MORPHO_NIL);\n\n        new->offset=noffset;\n        memcpy(noffset, offset, sizeof(unsigned int)*(ngrades+1));\n\n        new->dof=ndof;\n        memcpy(ndof, dof, sizeof(unsigned int)*ngrades);\n\n        new->pool=NULL;\n\n        /* Initialize the store */\n        object_init(&new->data.obj, OBJECT_MATRIX);\n        new->data.ncols=1;\n        new->data.nrows=size;\n        new->data.elements=new->data.matrixdata;\n\n        if (MORPHO_ISMATRIX(prototype)) {\n            objectmatrix *mat = MORPHO_GETMATRIX(prototype);\n            int mel = mat->ncols*mat->nrows;\n            for (unsigned int i=0; i<new->nelements; i++) {\n                memcpy(new->data.elements+i*mel, mat->elements, sizeof(double)*mel);\n            }\n        } else if(MORPHO_ISNUMBER(prototype)){\n            // if we have a number for our prototype set all the elements equal to it\n            for (elementid i=0; i<mesh->vert->ncols; i++) {\n                field_setelement(new, MESH_GRADE_VERTEX, i, 0, prototype);\n            }\n\n        } else memset(new->data.elements, 0, sizeof(double)*size);\n\n    } else { // Cleanup partially allocated structure\n        if (noffset) MORPHO_FREE(noffset);\n        if (ndof) MORPHO_FREE(ndof);\n    }\n\n    return new;\n}\n\n/** Applies an initialization function to every vertex */\nbool field_applyfunctiontovertices(vm *v, objectmesh *mesh, value fn, objectfield *field) {\n    value coords[mesh->dim]; // Vertex coords\n    value ret=MORPHO_NIL; // Return value\n    int nv = mesh_nvertices(mesh);\n\n    for (elementid i=0; i<nv; i++) { // for each vertex\n        if (mesh_getvertexcoordinatesasvalues(mesh, i, coords)) {\n            //get the vertex coordinates\n            if (!morpho_call(v, fn, mesh->dim, coords, &ret)) return false;\n\n            if (!field_setelement(field, MESH_GRADE_VERTEX, i, 0, ret)) {\n                // if we can't set the field value to the ouptut of the function clean up\n                morpho_runtimeerror(v, FIELD_OPRETURN);\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\n/** Applies an initialization function to every DOF in an element */\nbool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, value fnspc, objectfield *field) {\n    if (!MORPHO_ISFESPACE(fnspc)) return false;\n    fespace *disc = MORPHO_GETFESPACE(fnspc)->fespace;\n\n    objectsparse *conn = mesh_getconnectivityelement(mesh, 0, disc->grade);\n    if (!conn) return false;\n    elementid nel = mesh_nelements(conn);\n\n    for (elementid id=0; id<nel; id++) {\n        int nv, *vids;\n        if (!mesh_getconnectivity(conn, id, &nv, &vids)) return false;\n\n        double *x[nv]; // Fetch vertex positions\n        for (int i=0; i<nv; i++) mesh_getvertexcoordinatesaslist(mesh, vids[i], &x[i]);\n\n        fieldindx findx[disc->nnodes];\n        if (!fespace_doftofieldindx(field, disc, nv, vids, findx)) return false;\n\n        for (int i=0; i<disc->nnodes; i++) { // Loop over nodes\n            int indx;\n            if (!field_getindex(field, findx[i].g, findx[i].id, findx[i].indx, &indx)) return false;\n            \n            double lambda[nv], ll=0.0; // Convert node positions in reference element to barycentric coordinates\n            for (int j=0; j<nv-1; j++) { lambda[j+1]=disc->nodes[i*disc->grade+j]; ll+=lambda[j+1]; }\n            lambda[0]=1-ll;\n\n            double xx[mesh->dim]; // Interpolate position in physical space using barycentric coordinates\n            for (int j=0; j<mesh->dim; j++) xx[j]=0.0;\n            for (int j=0; j<nv; j++) functional_vecaddscale(mesh->dim, xx, lambda[j], x[j], xx);\n\n            /*printf(\"<<\");\n            for (int j=0; j<nv-1; j++) printf(\"%g \", disc->nodes[i*disc->grade+j]);\n            printf(\">> \");\n            printf(\"[\");\n            for (int j=0; j<nv; j++) printf(\"%g \", lambda[j]);\n            printf(\"] \");\n            for (int j=0; j<mesh->dim; j++) printf(\"%g \", xx[j]);\n            printf(\": \");*/\n\n            value coords[mesh->dim], ret;\n            for (int j=0; j<mesh->dim; j++) coords[j]=MORPHO_FLOAT(xx[j]);\n\n            if (!morpho_call(v, fn, mesh->dim, coords, &ret)) return false;\n\n            //morpho_printvalue(v, ret);\n            //printf(\" -> %i\\n\", indx[i]);\n\n            if (!field_setelementwithindex(field, indx, ret)) {\n                morpho_runtimeerror(v, FIELD_OPRETURN);\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\n/** Creates a field by applying a function to the vertices of a mesh\n * @param[in] v - virtual machine to use for function calls\n * @param[in] mesh - mesh to use\n * @param[in] fn - function to call\n * @returns field object or NULL on failure */\nobjectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn, value fnspc) {\n    value ret=MORPHO_NIL; // Return value\n    value coords[mesh->dim]; // Vertex coords\n    objectfield *new = NULL;\n    int handle = -1;\n\n    /* Use the first element to find a prototype **/\n    if (mesh_getvertexcoordinatesasvalues(mesh, 0, coords)) {\n        if (!morpho_call(v, fn, mesh->dim, coords, &ret)) goto field_newwithfunction_cleanup;\n        if (MORPHO_ISOBJECT(ret)) handle=morpho_retainobjects(v, 1, &ret);\n    }\n\n    new=object_newfield(mesh, ret, fnspc, NULL);\n\n    if (new) {\n        if (MORPHO_ISFESPACE(fnspc)) {\n            if (!field_applyfunctiontoelements(v, mesh, fn, fnspc, new)) goto field_newwithfunction_cleanup;\n        } else {\n            if (!field_applyfunctiontovertices(v, mesh, fn, new)) goto field_newwithfunction_cleanup;\n        }\n    }\n\n    if (handle>=0) morpho_releaseobjects(v, handle);\n    return new;\n\nfield_newwithfunction_cleanup:\n    if (new) object_free((object *) new);\n    if (handle>=0) morpho_releaseobjects(v, handle);\n    return NULL;\n}\n\n/** Zeros a field */\nvoid field_zero(objectfield *f) {\n    memset(f->data.elements, 0, sizeof(double)*(f->data.nrows));\n}\n\n/** Adds the object pool. This is a collection of statically allocated objects */\nbool field_addpool(objectfield *f) {\n    unsigned int nel = f->nelements;\n    if (!f->pool && MORPHO_ISMATRIX(f->prototype)) {\n        objectmatrix *prototype=MORPHO_GETMATRIX(f->prototype);\n        f->pool=MORPHO_MALLOC(sizeof(objectmatrix)*nel);\n        if (f->pool) {\n            objectmatrix *m = (objectmatrix *) f->pool;\n            for (unsigned int i=0; i<nel; i++) {\n                object_init(&m[i].obj, OBJECT_MATRIX);\n                m[i].elements=f->data.elements+i*f->psize;\n                m[i].ncols=prototype->ncols;\n                m[i].nrows=prototype->nrows;\n            }\n        }\n        return true;\n    }\n    return false;\n}\n\n/** Clones a field */\nobjectfield *field_clone(objectfield *f) {\n    objectfield *new = object_newfield(f->mesh, f->prototype, f->fnspc, f->dof);\n    if (new) memcpy(new->data.elements, f->data.elements, f->data.nrows*sizeof(double));\n    return new;\n}\n\n/* **********************************************************************\n * Field operations\n * ********************************************************************* */\n\n/** Retrieve a value from a field object\n * @param[in] field - field to use\n * @param[in] grade - grade to access\n * @param[in] el - element id\n * @param[in] indx - index within the element\n * @param[out] out - the retrieved value\n * @return true on success */\nbool field_getelement(objectfield *field, grade grade, elementid el, int indx, value *out) {\n    unsigned int ix=field->offset[grade]+field->dof[grade]*el+indx;\n    if (!(ix<field->offset[grade+1] && indx<field->dof[grade])) return false;\n    \n    if (MORPHO_ISNIL(field->prototype)) {\n        *out=MORPHO_FLOAT(field->data.elements[ix]);\n        return true;\n    } else if (MORPHO_ISMATRIX(field->prototype)) {\n        if (!field->pool) field_addpool(field);\n        if (field->pool) {\n            objectmatrix *mpool = (objectmatrix *) field->pool;\n            *out = MORPHO_OBJECT(&mpool[ix]);\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Retrieve a value from a field object given a single index\n * @param[in] field - field to use\n * @param[in] indx - index within the element\n * @param[out] out - the retrieved value\n * @return true on success */\nbool field_getelementwithindex(objectfield *field, int indx, value *out) {\n    if (MORPHO_ISNIL(field->prototype)) {\n        *out=MORPHO_FLOAT(field->data.elements[indx]);\n        return true;\n    } else if (MORPHO_ISMATRIX(field->prototype)) {\n        if (!field->pool) field_addpool(field);\n        if (field->pool) {\n            objectmatrix *mpool = (objectmatrix *) field->pool;\n            *out = MORPHO_OBJECT(&mpool[indx]);\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Constructs a single index, suitable for use with fieldgetelementwithindex from the grade, element id and quantity number\n * @param[in] field - field to use\n * @param[in] grade - grade to access\n * @param[in] el - element id\n * @param[in] indx - index within the element\n * @param[out] out - the retrieved index\n * @return true on success */\nbool field_getindex(objectfield *field, grade grade, elementid el, int indx, int *out) {\n    int ix=field->offset[grade]+field->dof[grade]*el+indx;\n    if (!(ix<field->offset[grade+1] && indx<field->dof[grade])) return false;\n\n    *out=ix;\n    return true;\n}\n\n/** Retrieve the list of doubles that represent an entry in a field\n * @param[in] field - field to use\n * @param[in] grade - grade to access\n * @param[in] el - element id\n * @param[in] indx - index within the element\n * @param[out] nentries - number of entries\n * @param[out] out - the retrieved list\n * @return true on success */\nbool field_getelementaslist(objectfield *field, grade grade, elementid el, int indx, unsigned int *nentries, double **out) {\n    bool success=false;\n    unsigned int ix=field->offset[grade]+field->dof[grade]*el+indx;\n    if (!(ix<field->offset[grade+1] && indx<field->dof[grade])) return false;\n    \n    if (MORPHO_ISNIL(field->prototype)) {\n        *out = &field->data.elements[ix];\n        *nentries=1;\n        success=true;\n    } else if (MORPHO_ISMATRIX(field->prototype)) {\n        *out = &field->data.elements[ix*(field->psize)];\n        *nentries=field->psize;\n        success=true;\n    }\n    return success;\n}\n\n/** Sets the value of an entry in a field object\n * @param[in] field - field to use\n * @param[in] grade - grade to access\n * @param[in] el - element id\n * @param[in] indx - index within the element\n * @param[in] val - value to set\n * @return true on success */\nbool field_setelement(objectfield *field, grade grade, elementid el, int indx, value val) {\n    unsigned int ix=field->offset[grade]+field->dof[grade]*el+indx;\n    if (!(ix<field->offset[grade+1] && indx<field->dof[grade])) return false;\n    \n    if (MORPHO_ISNIL(field->prototype)) {\n        if (MORPHO_ISNUMBER(val)) {\n            return morpho_valuetofloat(val, &field->data.elements[ix]);\n        }\n    } else {\n        unsigned int psize = field_sizeprototype(val);\n        if (MORPHO_ISMATRIX(val)) {\n            objectmatrix *m = MORPHO_GETMATRIX(val);\n            if (psize==field->psize) {\n                memcpy(field->data.elements+ix*psize, m->elements, psize*sizeof(double));\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\n/** Sets the value of an entry in a field object given a single index\n * @param[in] field - field to use\n * @param[in] ix - index of the element\n * @param[in] val - value to set\n * @return true on success */\nbool field_setelementwithindex(objectfield *field, int ix, value val) {\n    if (ix>=field->nelements) return false;\n    \n    if (MORPHO_ISNIL(field->prototype)) {\n        if (MORPHO_ISNUMBER(val)) {\n            return morpho_valuetofloat(val, &field->data.elements[ix]);\n        }\n    } else {\n        unsigned int psize = field_sizeprototype(val);\n        if (MORPHO_ISMATRIX(val)) {\n            objectmatrix *m = MORPHO_GETMATRIX(val);\n            if (psize==field->psize) {\n                memcpy(field->data.elements+ix*psize, m->elements, psize*sizeof(double));\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\n/** Checks if two fields have the same shape */\nbool field_compareshape(objectfield *a, objectfield *b) {\n    if (a->data.nrows==b->data.nrows &&\n        a->ngrades==b->ngrades) {\n        for (unsigned int i=0; i<a->ngrades; i++) {\n            if (a->dof[i]!=b->dof[i]) return false;\n        }\n        return true;\n    }\n    return false;\n}\n\n/** Returns the number of degrees of freedom in a given grade */\nunsigned int field_dofforgrade(objectfield *f, grade g) {\n    return (g<=f->ngrades ? f->dof[g] : 0);\n}\n\n/** Adds two fields together */\nbool field_add(objectfield *left, objectfield *right, objectfield *out) {\n    return (matrix_add(&left->data, &right->data, &out->data)==MATRIX_OK);\n}\n\n/** Subtracts one field from another */\nbool field_sub(objectfield *left, objectfield *right, objectfield *out) {\n    return (matrix_sub(&left->data, &right->data, &out->data)==MATRIX_OK);\n}\n\n/** Accumulate, i.e. a <- a + lambda*b */\nbool field_accumulate(objectfield *left, double lambda, objectfield *right) {\n    return (matrix_accumulate(&left->data, lambda, &right->data)==MATRIX_OK);\n}\n\nbool field_inner(objectfield *left, objectfield *right, double *out) {\n    return (matrix_inner(&left->data, &right->data, out)==MATRIX_OK);\n}\n\n/** Calls a function fn on every element of a field, optionally with other fields as arguments */\nbool field_op(vm *v, value fn, objectfield *f, int nargs, objectfield **args, value *out) {\n    unsigned int nel = f->nelements;\n    value ret=MORPHO_NIL;\n    value fargs[nargs+1];\n    objectfield *fld=NULL;\n    int handle = -1;\n    \n    for (int i=0; i<nel; i++) {\n        if (!field_getelementwithindex(f, i, &fargs[0])) return false;\n        for (unsigned int k=0; k<nargs; k++) {\n            if (!field_getelementwithindex(args[k], i, &fargs[k+1])) return false;\n        }\n        \n        if (morpho_call(v, fn, nargs+1, fargs, &ret)) {\n            if (!fld) {\n                if (field_checkprototype(ret)) {\n                    if (MORPHO_ISOBJECT(ret)) handle=morpho_retainobjects(v, 1, &ret);\n                    fld=object_newfield(f->mesh, ret, f->fnspc, f->dof);\n                    if (!fld) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; }\n                } else {\n                    morpho_runtimeerror(v, FIELD_OPRETURN); return false;\n                }\n            }\n            \n            if (!field_setelementwithindex(fld, i, ret)) return false;\n        } else return false;\n    }\n    \n    if (handle>=0) morpho_releaseobjects(v, handle);\n    if (fld) *out = MORPHO_OBJECT(fld);\n    \n    return true;\n}\n\n/* **********************************************************************\n * Field veneer class\n * ********************************************************************* */\n\n/** Constructs a Field object */\nvalue field_constructor(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectfield *new=NULL;\n    objectmesh *mesh=NULL; // The mesh used by the object\n    value fn = MORPHO_NIL; // A function to call\n    value prototype=MORPHO_NIL; // Prototype object\n\n    value grd = MORPHO_NIL;\n    value fnspc = MORPHO_NIL;\n    int nfixed;\n\n    if (!builtin_options(v, nargs, args, &nfixed, 2, field_gradeoption, &grd, field_functionspaceoption, &fnspc))\n        morpho_runtimeerror(v, FIELD_ARGS);\n\n    for (unsigned int i=0; i<nfixed; i++) {\n        if (MORPHO_ISMESH(MORPHO_GETARG(args, i))) mesh = MORPHO_GETMESH(MORPHO_GETARG(args, i)); // if the ith argument is a mesh get that mesh and assign it\n        else if (morpho_iscallable(MORPHO_GETARG(args, i))) fn = MORPHO_GETARG(args, i); // if the ith argument is a function to call put that in the fn spot\n        else if (field_checkprototype(MORPHO_GETARG(args, i))) prototype = MORPHO_GETARG(args, i); //if the ith argument is a prototype put that in the prototype spot\n    }\n\n    if (!mesh) {\n        // if we don't have a mesh return a nil and thorw and error\n        morpho_runtimeerror(v,FIELD_MESHARG);\n        return MORPHO_NIL;\n    }\n    \n    unsigned int ngrades = mesh_maxgrade(mesh)+1;\n    unsigned int dof[ngrades];\n    for (unsigned int i=0; i<ngrades; i++) dof[i]=0;\n\n    /* Process optional arguments */\n    if (MORPHO_ISFESPACE(fnspc)) {\n        \n    } else if (MORPHO_ISINTEGER(grd)) {\n        dof[MORPHO_GETINTEGERVALUE(grd)]=1;\n    } else if (MORPHO_ISLIST(grd)) {\n        objectlist *list = MORPHO_GETLIST(grd);\n        if (!array_valuelisttoindices(list->val.count, list->val.data, dof)) return MORPHO_NIL;\n    }\n\n    if (MORPHO_ISNIL(fn)) {\n        new = object_newfield(mesh, prototype, fnspc, (MORPHO_ISNIL(grd) ? NULL: dof));\n    } else {\n        new = field_newwithfunction(v, mesh, fn, fnspc);\n    }\n\n    if (new) {\n        out=morpho_wrapandbind(v, (object *) new);\n    } else if (!morpho_checkerror(morpho_geterror(v))) {\n        morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    }\n\n    return out;\n}\n\n\n/** Gets the field element with given indices */\nvalue Field_getindex(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    unsigned int indx[nargs];\n    value out = MORPHO_NIL;\n    \n    if (array_valuelisttoindices(nargs, args+1, indx)) {\n        grade g = (nargs>1 ? indx[0] : MESH_GRADE_VERTEX);\n        elementid el = (nargs>1 ? indx[1] : indx[0]);\n        int elindx = (nargs>2 ? indx[2] : 0);\n        \n        /* If only one index is specified, increment g to the lowest nonempty grade */\n        if (nargs==1) while (g<f->ngrades && f->dof[g]==0) g++;\n        \n        if (!field_getelement(f, g, el, elindx, &out)) morpho_runtimeerror(v, FIELD_INDICESOUTSIDEBOUNDS);\n    } else morpho_runtimeerror(v, FIELD_INVLDINDICES);\n    \n    return out;\n}\n\n/** Sets the field element with given indices */\nvalue Field_setindex(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    unsigned int indx[nargs];\n    int nindices = nargs-1;\n    \n    if (array_valuelisttoindices(nindices, args+1, indx)) {\n        grade g = (nindices>1 ? indx[0] : MESH_GRADE_VERTEX);\n        elementid el = (nindices>1 ? indx[1] : indx[0]);\n        int elindx = (nindices>2 ? indx[2] : 0);\n\n        /* If only one index is specified, treat it as a single index */\n        if (nindices==1) {\n            if (!field_setelementwithindex(f, indx[0], MORPHO_GETARG(args, nargs-1))) {\n                morpho_runtimeerror(v, FIELD_INCOMPATIBLEVAL);\n                return MORPHO_NIL;\n            }\n        } else if (!field_setelement(f, g, el, elindx, MORPHO_GETARG(args, nargs-1))) {\n            morpho_runtimeerror(v, FIELD_INCOMPATIBLEVAL);\n            return MORPHO_NIL;\n        }\n    } else morpho_runtimeerror(v, FIELD_INVLDINDICES);\n    \n    return MORPHO_NIL;\n}\n\n/** Enumerate protocol */\nvalue Field_enumerate(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    \n    if (nargs==1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            \n            if (i<0) out=MORPHO_INTEGER(a->nelements);\n            else if (i<a->nelements) {\n                if (!field_getelementwithindex(a, i, &out)) UNREACHABLE(\"Could not get field element.\");\n            }\n            /* Note no need to bind as we are an object pool */\n        }\n    }\n    \n    return out;\n}\n\n\n/** Number of field elements */\nvalue Field_count(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    \n    return MORPHO_INTEGER(f->nelements);\n}\n\n/** Field assign */\nvalue Field_assign(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n \n    if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0));\n        \n        if (field_compareshape(a, b)) {\n            matrix_copy(&b->data, &a->data);\n        } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES);\n    } else if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        if (matrix_copy(b, &a->data)!=MATRIX_OK) morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, FIELD_ARITHARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Field add */\nvalue Field_add(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0));\n        \n        if (field_compareshape(a, b)) {\n            objectfield *new = object_newfield(a->mesh, a->prototype, a->fnspc, a->dof);\n            \n            if (new) {\n                out=MORPHO_OBJECT(new);\n                field_add(a, b, new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, FIELD_ARITHARGS);\n    \n    return out;\n}\n\n/** Right add */\nvalue Field_addr(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ||\n                     MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) {\n        int i=0;\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))<MORPHO_EPS ? 0 : 1);\n        \n        if (i==0) {\n            out=MORPHO_SELF(args);\n        } else UNREACHABLE(\"Right addition to non-zero value.\");\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Field subtraction */\nvalue Field_sub(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0));\n        \n        if (field_compareshape(a, b)) {\n            objectfield *new = object_newfield(a->mesh, a->prototype, a->fnspc, a->dof);\n            \n            if (new) {\n                out=MORPHO_OBJECT(new);\n                field_sub(a, b, new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, FIELD_ARITHARGS);\n    \n    return out;\n}\n\n/** Right subtract */\nvalue Field_subr(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ||\n                     MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)))) {\n        int i=(MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ? 0 : MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)));\n        \n        if (i==0) {\n            objectfield *new=field_clone(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(&new->data, -1.0);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else morpho_runtimeerror(v, VM_INVALIDARGS);\n    } else morpho_runtimeerror(v, VM_INVALIDARGS);\n    \n    return out;\n}\n\n/** Field accumulate */\nvalue Field_acc(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n \n    if (nargs==2 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISFIELD(MORPHO_GETARG(args, 1))) {\n        objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 1));\n        \n        if (field_compareshape(a, b)) {\n            double lambda=1.0;\n            morpho_valuetofloat(MORPHO_GETARG(args, 0), &lambda);\n            field_accumulate(a, lambda, b);\n        } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, FIELD_ARITHARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Field multiply by a scalar */\nvalue Field_mul(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double scale=1.0;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) {\n            objectfield *new = field_clone(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(&new->data, scale);\n                morpho_bindobjects(v, 1, &out);\n            }\n        }\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Field multiply by a scalar */\nvalue Field_div(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        /* Division by a scalar */\n        double scale=1.0;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) {\n            if (fabs(scale)<MORPHO_EPS) MORPHO_RAISE(v, VM_DVZR);\n            \n            objectfield *new = field_clone(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(&new->data, 1.0/scale);\n                morpho_bindobjects(v, 1, &out);\n            }\n        }\n    }\n    \n    return out;\n}\n\n/** Frobenius inner product */\nvalue Field_inner(vm *v, int nargs, value *args) {\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0));\n        \n        double prod=0.0;\n        if (field_inner(a, b, &prod)) {\n            out = MORPHO_FLOAT(prod);\n        } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, FIELD_ARITHARGS);\n    \n    return out;\n}\n\n/** Generalized operations */\nvalue Field_op(vm *v, int nargs, value *args) {\n    objectfield *slf=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    value fn=MORPHO_NIL;\n    objectfield *flds[nargs];\n    \n    if (nargs>0) fn=MORPHO_GETARG(args, 0);\n    if (morpho_iscallable(fn)) {\n        for (unsigned int i=1; i<nargs; i++) {\n            if (MORPHO_ISFIELD(MORPHO_GETARG(args, i))) flds[i-1]=MORPHO_GETFIELD(MORPHO_GETARG(args, i));\n            else { morpho_runtimeerror(v, FIELD_OP); return MORPHO_NIL; }\n        }\n        \n        if (field_op(v, fn, slf, nargs-1, flds, &out)) {\n            morpho_bindobjects(v, 1, &out);\n        }\n    } else morpho_runtimeerror(v, FIELD_OP);\n    \n    return out;\n}\n\n/** Print the mesh */\nvalue Field_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISFIELD(self)) return Object_print(v, nargs, args);\n    \n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    morpho_printf(v, \"<Field>\\n\");\n    matrix_print(v, &f->data);\n    return MORPHO_NIL;\n}\n\n/** Clones a field */\nvalue Field_clone(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args));\n    objectfield *new=field_clone(a);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    return out;\n}\n\n/** Get the shape (number of dofs per grade) of a field */\nvalue Field_shape(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n\n    value shape[f->ngrades];\n    for (unsigned int i=0; i<f->ngrades; i++) {\n        shape[i]=MORPHO_INTEGER(f->dof[i]);\n    }\n    \n    objectlist *new=object_newlist(f->ngrades, shape);\n    if (new) {\n        out = MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n/** Get the functionspace used by a field */\nvalue Field_fnspace(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    \n    return f->fnspc;\n}\n\n/** Get a prototype used by the field */\nvalue Field_prototype(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    \n    return f->prototype;\n}\n\n/** Get the mesh associated with a field */\nvalue Field_mesh(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    \n    return MORPHO_OBJECT(f->mesh);\n}\n\n/** Get the matrix that stores the Field */\nvalue Field_linearize(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n    \n    objectmatrix *m=object_clonematrix(&f->data);\n    if (m) {\n        out = MORPHO_OBJECT(m);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n/** Directly the matrix that stores the Field\n @warning only use when you know what you're doing.  */\nvalue Field_unsafelinearize(vm *v, int nargs, value *args) {\n    objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args));\n    \n    return MORPHO_OBJECT(&f->data);\n}\n\nMORPHO_BEGINCLASS(Field)\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Field_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Field_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Field_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Field_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ASSIGN_METHOD, Field_assign, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADD_METHOD, Field_add, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADDR_METHOD, Field_addr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUB_METHOD, Field_sub, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUBR_METHOD, Field_subr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ACC_METHOD, Field_acc, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MUL_METHOD, Field_mul, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MULR_METHOD, Field_mul, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIV_METHOD, Field_div, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_INNER_METHOD, Field_inner, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD_OP_METHOD, Field_op, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Field_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Field_clone, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD_SHAPE_METHOD, Field_shape, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD_FESPACE_METHOD, Field_fnspace, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD_PROTOTYPE_METHOD, Field_prototype, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD_MESH_METHOD, Field_mesh, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD_LINEARIZE_METHOD, Field_linearize, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FIELD__LINEARIZE_METHOD, Field_unsafelinearize, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************* */\n\nvoid field_initialize(void) {\n    objectfieldtype=object_addtype(&objectfielddefn);\n    \n    field_gradeoption=builtin_internsymbolascstring(FIELD_GRADEOPTION);\n    field_functionspaceoption=builtin_internsymbolascstring(FIELD_FESPACEOPTION);\n    \n    builtin_addfunction(FIELD_CLASSNAME, field_constructor, BUILTIN_FLAGSEMPTY);\n    \n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value fieldclass=builtin_addclass(FIELD_CLASSNAME, MORPHO_GETCLASSDEFINITION(Field), objclass);\n    object_setveneerclass(OBJECT_FIELD, fieldclass);\n    \n    morpho_defineerror(FIELD_INDICESOUTSIDEBOUNDS, ERROR_HALT, FIELD_INDICESOUTSIDEBOUNDS_MSG);\n    morpho_defineerror(FIELD_INVLDINDICES, ERROR_HALT, FIELD_INVLDINDICES_MSG);\n    morpho_defineerror(FIELD_ARITHARGS, ERROR_HALT, FIELD_ARITHARGS_MSG);\n    morpho_defineerror(FIELD_INCOMPATIBLEMATRICES, ERROR_HALT, FIELD_INCOMPATIBLEMATRICES_MSG);\n    morpho_defineerror(FIELD_INCOMPATIBLEVAL, ERROR_HALT, FIELD_INCOMPATIBLEVAL_MSG);\n    morpho_defineerror(FIELD_ARGS, ERROR_HALT, FIELD_ARGS_MSG);\n    morpho_defineerror(FIELD_OP, ERROR_HALT, FIELD_OP_MSG);\n    morpho_defineerror(FIELD_OPRETURN, ERROR_HALT, FIELD_OPRETURN_MSG);\n    morpho_defineerror(FIELD_MESHARG, ERROR_HALT, FIELD_MESHARG_MSG);\n}\n\n#endif\n"
  },
  {
    "path": "src/geometry/field.h",
    "content": "/** @file field.h\n *  @author T J Atherton\n *\n *  @brief Fields\n */\n\n#ifndef field_h\n#define field_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include \"object.h\"\n#include \"mesh.h\"\n#include \"matrix.h\"\n#include <stdio.h>\n\n/* -------------------------------------------------------\n * Field objects\n * ------------------------------------------------------- */\n\nextern objecttype objectfieldtype;\n#define OBJECT_FIELD objectfieldtype\n\ntypedef struct {\n    object obj;\n    objectmesh *mesh; /** The mesh the selection is referring to */\n    \n    unsigned int ngrades; /** Number of grades */\n    unsigned int *dof; /** number of degrees of freedom per entry in each grade */\n    unsigned int *offset; /** Offsets into the store for each grade */\n    \n    value prototype; /** Prototype object */\n    unsigned int psize; /** Number of dofs per copy of the prototype */\n    unsigned int nelements; /** Total number of elements in the fireld */\n    void *pool; /** Pool of statically allocated objects */\n    \n    value fnspc; /** Function space used */\n    \n    objectmatrix data; /** Underlying data store */\n} objectfield;\n\n/** Tests whether an object is a field */\n#define MORPHO_ISFIELD(val) object_istype(val, OBJECT_FIELD)\n\n/** Gets the object as a field */\n#define MORPHO_GETFIELD(val)   ((objectfield *) MORPHO_GETOBJECT(val))\n\n/** Creates an empty field object */\nobjectfield *object_newfield(objectmesh *mesh, value prototype, value disc, unsigned int *shape);\n\n/* -------------------------------------------------------\n * Indexing a Field\n * ------------------------------------------------------- */\n\ntypedef struct {\n    grade g;      // The grade\n    elementid id; // The element\n    int indx;     // Quantity index\n} fieldindx;\n\n/* -------------------------------------------------------\n * Field class\n * ------------------------------------------------------- */\n\nextern value field_gradeoption;\n\n#define FIELD_CLASSNAME \"Field\"\n\n#define FIELD_GRADEOPTION \"grade\"\n#define FIELD_FESPACEOPTION \"finiteelementspace\"\n\n#define FIELD_OP_METHOD      \"op\"\n#define FIELD_SHAPE_METHOD   \"shape\"\n#define FIELD_FESPACE_METHOD   \"finiteelementspace\"\n#define FIELD_PROTOTYPE_METHOD   \"prototype\"\n#define FIELD_MESH_METHOD    \"mesh\"\n#define FIELD_LINEARIZE_METHOD    \"linearize\"\n#define FIELD__LINEARIZE_METHOD    \"__linearize\"\n\n#define FIELD_INDICESOUTSIDEBOUNDS       \"FldBnds\"\n#define FIELD_INDICESOUTSIDEBOUNDS_MSG   \"Field index out of bounds.\"\n\n#define FIELD_INVLDINDICES               \"FldInvldIndx\"\n#define FIELD_INVLDINDICES_MSG           \"Field indices must be numerical.\"\n\n#define FIELD_ARITHARGS                  \"FldInvldArg\"\n#define FIELD_ARITHARGS_MSG              \"Field arithmetic methods expect a field or number as their argument.\"\n\n#define FIELD_INCOMPATIBLEMATRICES       \"FldIncmptbl\"\n#define FIELD_INCOMPATIBLEMATRICES_MSG   \"Fields have incompatible shape.\"\n\n#define FIELD_INCOMPATIBLEVAL            \"FldIncmptblVal\"\n#define FIELD_INCOMPATIBLEVAL_MSG        \"Assignment value has incompatible shape with field elements.\"\n\n#define FIELD_ARGS                       \"FldArgs\"\n#define FIELD_ARGS_MSG                   \"Field allows 'grade' as an optional argument.\"\n\n#define FIELD_OP                         \"FldOp\"\n#define FIELD_OP_MSG                     \"Method 'op' requires a callable object as the first argument; all other arguments must be fields of compatible shape.\"\n\n#define FIELD_OPRETURN                   \"FldOpFn\"\n#define FIELD_OPRETURN_MSG               \"Could not construct a Field from the return value of the function passed to 'op'.\"\n\n#define FIELD_MESHARG                    \"FldMshArg\"\n#define FIELD_MESHARG_MSG                \"Field expects a mesh as its first argurment\"\n\nobjectfield *field_clone(objectfield *f);\n\nvoid field_zero(objectfield *field);\nbool field_addpool(objectfield *field);\nunsigned int field_sizeprototype(value prototype);\n\nunsigned int field_dofforgrade(objectfield *f, grade g);\nbool field_getelement(objectfield *field, grade grade, elementid el, int indx, value *out);\nbool field_getelementwithindex(objectfield *field, int indx, value *out);\nbool field_getindex(objectfield *field, grade grade, elementid el, int indx, int *out);\nbool field_getelementaslist(objectfield *field, grade grade, elementid el, int indx, unsigned int *nentries, double **out);\n\nbool field_setelement(objectfield *field, grade grade, elementid el, int indx, value val);\nbool field_setelementwithindex(objectfield *field, int ix, value val);\n\nvoid field_initialize(void);\n\n#endif\n\n#endif /* field_h */\n"
  },
  {
    "path": "src/geometry/functional.c",
    "content": "/** @file functional.c\n *  @author T J Atherton\n *\n *  @brief Functionals\n */\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include <float.h>\n#include <math.h>\n\n#include \"functional.h\"\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"common.h\"\n\n#include \"threadpool.h\"\n\n#include \"matrix.h\"\n#include \"sparse.h\"\n#include \"geometry.h\"\n\n#ifndef M_PI\n    #define M_PI 3.14159265358979323846\n#endif\n\nvalue functional_gradeproperty;\nvalue functional_fieldproperty;\n//static value functional_functionproperty;\n\n/* **********************************************************************\n * Utility functions\n * ********************************************************************** */\n\ndouble fddelta1, // = pow(MORPHO_EPS, 1.0/3.0),\n       fddelta2; // = pow(MORPHO_EPS, 1.0/4.0);\n\n// Estimates the correct stepsize for cell centered finite differences\ndouble functional_fdstepsize(double x, int order) {\n    double h = fddelta1;\n    if (order==2) h = fddelta2;\n    \n    // h should be multiplied by an estimate of the lengthscale over which f changes,\n    //      | f / f''' | ^ (1/3)\n    double absx = fabs(x);\n    if (absx>1) h*=absx; // In the absence of other information, and unless we're near 0, use x as the best estimate.\n    \n    // Ensure stepsize results in a representable number\n    volatile double temp = x+h; // Prevent compiler optimizing this away\n    return temp-x;\n}\n\nstatic void functional_clearmapinfo(functional_mapinfo *info) {\n    info->mesh=NULL;\n    info->field=NULL;\n    info->sel=NULL;\n    info->g=0;\n    info->id=0;\n    info->integrand=NULL;\n    info->grad=NULL;\n    info->dependencies=NULL;\n    info->cloneref=NULL;\n    info->freeref=NULL;\n    info->ref=NULL;\n    info->sym=SYMMETRY_NONE;\n}\n\n/** Validates the arguments provided to a functional\n * @param[in] v - vm\n * @param[in] nargs - number of arguments\n * @param[in] args - the arguments\n * @param[out] info - mapinfo block  */\nbool functional_validateargs(vm *v, int nargs, value *args, functional_mapinfo *info) {\n    functional_clearmapinfo(info);\n\n    for (unsigned int i=0; i<nargs; i++) {\n        if (MORPHO_ISMESH(MORPHO_GETARG(args,i))) {\n            info->mesh = MORPHO_GETMESH(MORPHO_GETARG(args,i));\n        } else if (MORPHO_ISSELECTION(MORPHO_GETARG(args,i))) {\n            info->sel = MORPHO_GETSELECTION(MORPHO_GETARG(args,i));\n        } else if (MORPHO_ISFIELD(MORPHO_GETARG(args,i))) {\n            info->field = MORPHO_GETFIELD(MORPHO_GETARG(args,i));\n            if (info->field) info->mesh = (info->field->mesh); // Retrieve the mesh from the field\n        } else if (MORPHO_ISINTEGER(MORPHO_GETARG(args,i))) {\n            info->id = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args,i));\n        }\n    }\n\n\n    if (info->mesh) return true;\n    morpho_runtimeerror(v, FUNC_INTEGRAND_MESH);\n    return false;\n}\n\n\n/* **********************************************************************\n * Common routines\n * ********************************************************************** */\n\n/** Internal function to count the number of elements */\nstatic bool functional_countelements(vm *v, objectmesh *mesh, grade g, int *n, objectsparse **s) {\n    /* How many elements? */\n    if (g==MESH_GRADE_VERTEX) {\n        *n=mesh->vert->ncols;\n    } else {\n        *s=mesh_getconnectivityelement(mesh, 0, g);\n        if (*s) {\n            *n=(*s)->ccs.ncols; // Number of elements\n        } else {\n            morpho_runtimeerror(v, FUNC_ELNTFND, g);\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic int functional_symmetryimagelistfn(const void *a, const void *b) {\n    elementid i=*(elementid *) a; elementid j=*(elementid *) b;\n    return (int) i-j;\n}\n\n/** Gets a list of all image elements (those that map onto a target element)\n * @param[in] mesh - the mesh\n * @param[in] g - grade to look up\n * @param[in] sort - whether to sort othe results\n * @param[out] ids - varray is filled with image element ids */\nvoid functional_symmetryimagelist(objectmesh *mesh, grade g, bool sort, varray_elementid *ids) {\n    objectsparse *conn=mesh_getconnectivityelement(mesh, g, g);\n\n    ids->count=0; // Initialize the varray\n\n    if (conn) {\n        int i,j;\n        void *ctr=sparsedok_loopstart(&conn->dok);\n\n        while (sparsedok_loop(&conn->dok, &ctr, &i, &j)) {\n            varray_elementidwrite(ids, j);\n        }\n\n        if (sort) qsort(ids->data, ids->count, sizeof(elementid), functional_symmetryimagelistfn);\n    }\n}\n\n/** Sums forces on symmetry vertices\n * @param[in] mesh - mesh object\n * @param frc - force object; updated if symmetries are present. */\nbool functional_symmetrysumforces(objectmesh *mesh, objectmatrix *frc) {\n    objectsparse *s=mesh_getconnectivityelement(mesh, 0, 0); // Checking for vertex symmetries\n\n    if (s) {\n        int i,j;\n        void *ctr = sparsedok_loopstart(&s->dok);\n        double *fi, *fj, fsum[mesh->dim];\n\n        while (sparsedok_loop(&s->dok, &ctr, &i, &j)) {\n            if (matrix_getcolumn(frc, i, &fi) &&\n                matrix_getcolumn(frc, j, &fj)) {\n\n                for (unsigned int k=0; k<mesh->dim; k++) fsum[k]=fi[k]+fj[k];\n                matrix_setcolumn(frc, i, fsum);\n                matrix_setcolumn(frc, j, fsum);\n            }\n        }\n    }\n\n    return s;\n}\n\nbool functional_inlist(varray_elementid *list, elementid id) {\n    for (unsigned int i=0; i<list->count; i++) if (list->data[i]==id) return true;\n    return false;\n}\n\nbool functional_containsvertex(int nv, int *vid, elementid id) {\n    for (unsigned int i=0; i<nv; i++) if (vid[i]==id) return true;\n    return false;\n}\n\n/* **********************************************************************\n * Map functions\n * ********************************************************************** */\n\n/** Sums an integrand\n * @param[in] v - virtual machine in use\n * @param[in] info - map info\n * @param[out] out - a matrix of integrand values\n * @returns true on success, false otherwise. Error reporting through VM. */\nbool functional_sumintegrandX(vm *v, functional_mapinfo *info, value *out) {\n    bool success=false;\n    objectmesh *mesh = info->mesh;\n    objectselection *sel = info->sel;\n    grade g = info->g;\n    functional_integrand *integrand = info->integrand;\n    void *ref = info->ref;\n    objectsparse *s=NULL;\n    int n=0;\n\n    if (!functional_countelements(v, mesh, g, &n, &s)) return false;\n\n    /* Find any image elements so we can skip over them */\n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    functional_symmetryimagelist(mesh, g, true, &imageids);\n\n    if (n>0) {\n        int vertexid; // Use this if looping over grade 0\n        int *vid=(g==0 ? &vertexid : NULL),\n            nv=(g==0 ? 1 : 0); // The vertex indices\n        int sindx=0; // Index into imageids array\n        double sum=0.0, c=0.0, y, t, result;\n\n        if (sel) { // Loop over selection\n            if (sel->selected[g].count>0) for (unsigned int k=0; k<sel->selected[g].capacity; k++) {\n                if (!MORPHO_ISINTEGER(sel->selected[g].contents[k].key)) continue;\n                elementid i = MORPHO_GETINTEGERVALUE(sel->selected[g].contents[k].key);\n\n                // Skip this element if it's an image element:\n                if ((imageids.count>0) && (sindx<imageids.count) && imageids.data[sindx]==i) { sindx++; continue; }\n\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if ((*integrand) (v, mesh, i, nv, vid, ref, &result)) {\n                        y=result-c; t=sum+y; c=(t-sum)-y; sum=t; // Kahan summation\n                    } else goto functional_sumintegrand_cleanup;\n                }\n            }\n        } else { // Loop over elements\n            for (elementid i=0; i<n; i++) {\n                // Skip this element if it's an image element\n                if ((imageids.count>0) && (sindx<imageids.count) && imageids.data[sindx]==i) { sindx++; continue; }\n\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if ((*integrand) (v, mesh, i, nv, vid, ref, &result)) {\n                        y=result-c; t=sum+y; c=(t-sum)-y; sum=t; // Kahan summation\n                    } else goto functional_sumintegrand_cleanup;\n                }\n            }\n        }\n\n        *out=MORPHO_FLOAT(sum);\n    }\n\n    success=true;\n\nfunctional_sumintegrand_cleanup:\n    varray_elementidclear(&imageids);\n    return success;\n}\n\n/** Calculate an integrand\n * @param[in] v - virtual machine in use\n * @param[in] info - map info\n * @param[out] out - a matrix of integrand values\n * @returns true on success, false otherwise. Error reporting through VM. */\nbool functional_mapintegrandX(vm *v, functional_mapinfo *info, value *out) {\n    objectmesh *mesh = info->mesh;\n    objectselection *sel = info->sel;\n    grade g = info->g;\n    functional_integrand *integrand = info->integrand;\n    void *ref = info->ref;\n    objectsparse *s=NULL;\n    objectmatrix *new=NULL;\n    bool ret=false;\n    int n=0;\n\n    /* How many elements? */\n    if (!functional_countelements(v, mesh, g, &n, &s)) return false;\n\n    /* Find any image elements so we can skip over them */\n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    functional_symmetryimagelist(mesh, g, true, &imageids);\n\n    /* Create the output matrix */\n    if (n>0) {\n        new=object_newmatrix(1, n, true);\n        if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; }\n    }\n\n    if (new) {\n        int vertexid; // Use this if looping over grade 0\n        int *vid=(g==0 ? &vertexid : NULL),\n            nv=(g==0 ? 1 : 0); // The vertex indices\n        int sindx=0; // Index into imageids array\n        double result;\n\n        if (sel) { // Loop over selection\n            if (sel->selected[g].count>0) for (unsigned int k=0; k<sel->selected[g].capacity; k++) {\n                if (!MORPHO_ISINTEGER(sel->selected[g].contents[k].key)) continue;\n                elementid i = MORPHO_GETINTEGERVALUE(sel->selected[g].contents[k].key);\n\n                // Skip this element if it's an image element\n                if ((imageids.count>0) && (sindx<imageids.count) && imageids.data[sindx]==i) { sindx++; continue; }\n\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if ((*integrand) (v, mesh, i, nv, vid, ref, &result)) {\n                        matrix_setelement(new, 0, i, result);\n                    } else goto functional_mapintegrand_cleanup;\n                }\n            }\n        } else { // Loop over elements\n            for (elementid i=0; i<n; i++) {\n                // Skip this element if it's an image element\n                if ((imageids.count>0) && (sindx<imageids.count) && imageids.data[sindx]==i) { sindx++; continue; }\n\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if ((*integrand) (v, mesh, i, nv, vid, ref, &result)) {\n                        matrix_setelement(new, 0, i, result);\n                    } else goto functional_mapintegrand_cleanup;\n                }\n            }\n        }\n        *out = MORPHO_OBJECT(new);\n        ret=true;\n    }\n\n    varray_elementidclear(&imageids);\n    return ret;\n\nfunctional_mapintegrand_cleanup:\n    object_free((object *) new);\n    varray_elementidclear(&imageids);\n    return false;\n}\n\n/** Calculate gradient\n * @param[in] v - virtual machine in use\n * @param[in] info - map info structure\n * @param[out] out - a matrix of integrand values\n * @returns true on success, false otherwise. Error reporting through VM. */\nbool functional_mapgradientX(vm *v, functional_mapinfo *info, value *out) {\n    objectmesh *mesh = info->mesh;\n    objectselection *sel = info->sel;\n    grade g = info->g;\n    functional_gradient *grad = info->grad;\n    void *ref = info->ref;\n    symmetrybhvr sym = info->sym;\n    objectsparse *s=NULL;\n    objectmatrix *frc=NULL;\n    bool ret=false;\n    int n=0;\n\n    /* How many elements? */\n    if (!functional_countelements(v, mesh, g, &n, &s)) return false;\n\n    /* Create the output matrix */\n    if (n>0) {\n        frc=object_newmatrix(mesh->vert->nrows, mesh->vert->ncols, true);\n        if (!frc)  { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; }\n    }\n\n    if (frc) {\n        int vertexid; // Use this if looping over grade 0\n        int *vid=(g==0 ? &vertexid : NULL),\n            nv=(g==0 ? 1 : 0); // The vertex indices\n\n\n        if (sel) { // Loop over selection\n            if (sel->selected[g].count>0) for (unsigned int k=0; k<sel->selected[g].capacity; k++) {\n                if (!MORPHO_ISINTEGER(sel->selected[g].contents[k].key)) continue;\n\n                elementid i = MORPHO_GETINTEGERVALUE(sel->selected[g].contents[k].key);\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if (!(*grad) (v, mesh, i, nv, vid, ref, frc)) goto functional_mapgradient_cleanup;\n                }\n            }\n        } else { // Loop over elements\n            for (elementid i=0; i<n; i++) {\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if (!(*grad) (v, mesh, i, nv, vid, ref, frc)) goto functional_mapgradient_cleanup;\n                }\n            }\n        }\n\n        if (sym==SYMMETRY_ADD) functional_symmetrysumforces(mesh, frc);\n\n        *out = MORPHO_OBJECT(frc);\n        ret=true;\n    }\n\nfunctional_mapgradient_cleanup:\n    if (!ret) object_free((object *) frc);\n\n    return ret;\n}\n\n/** Calculate field gradient\n * @param[in] v - virtual machine in use\n * @param[in] info - map info structure\n * @param[out] out - a field of integrand values\n * @returns true on success, false otherwise. Error reporting through VM. */\nbool functional_mapfieldgradientX(vm *v, functional_mapinfo *info, value *out) {\n    objectmesh *mesh = info->mesh;\n    objectfield *field = info->field;\n    objectselection *sel = info->sel;\n    objectfield *grad=NULL;\n    grade g = info->g;\n    functional_fieldgradient *fgrad = info->fieldgrad;\n    void *ref = info->ref;\n    objectsparse *s=NULL;\n    bool ret=false;\n    int n=0;\n\n    /* How many elements? */\n    if (!functional_countelements(v, mesh, g, &n, &s)) return false;\n\n    /* Create the output field */\n    if (n>0) {\n        grad=object_newfield(mesh, field->prototype, field->fnspc, field->dof);\n        if (!grad) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; }\n        field_zero(grad);\n    }\n\n    if (grad) {\n        int vertexid; // Use this if looping over grade 0\n        int *vid=(g==0 ? &vertexid : NULL),\n            nv=(g==0 ? 1 : 0); // The vertex indices\n\n\n        if (sel) { // Loop over selection\n            if (sel->selected[g].count>0) for (unsigned int k=0; k<sel->selected[g].capacity; k++) {\n                if (!MORPHO_ISINTEGER(sel->selected[g].contents[k].key)) continue;\n\n                elementid i = MORPHO_GETINTEGERVALUE(sel->selected[g].contents[k].key);\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if (!(*fgrad) (v, mesh, i, nv, vid, ref, grad)) goto functional_mapfieldgradient_cleanup;\n                }\n            }\n        } else { // Loop over elements\n            for (elementid i=0; i<n; i++) {\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n                    if (!(*fgrad) (v, mesh, i, nv, vid, ref, grad)) goto functional_mapfieldgradient_cleanup;\n                }\n            }\n        }\n\n        *out = MORPHO_OBJECT(grad);\n        ret=true;\n    }\n\nfunctional_mapfieldgradient_cleanup:\n    if (!ret) object_free((object *) grad);\n\n    return ret;\n}\n\nstatic bool functional_numericalremotegradient(vm *v, functional_mapinfo *info, objectsparse *conn, elementid remoteid, elementid i, int nv, int *vid, objectmatrix *frc);\n\n/* Calculates a numerical gradient */\nbool functional_numericalgradient(vm *v, objectmesh *mesh, elementid i, int nv, int *vid, functional_integrand *integrand, void *ref, objectmatrix *frc) {\n    double f0,fp,fm,x0,eps=1e-6;\n\n    // Loop over vertices in element\n    for (unsigned int j=0; j<nv; j++) {\n        // Loop over coordinates\n        for (unsigned int k=0; k<mesh->dim; k++) {\n            matrix_getelement(frc, k, vid[j], &f0);\n\n            matrix_getelement(mesh->vert, k, vid[j], &x0);\n            \n            eps=functional_fdstepsize(x0, 1);\n            \n            matrix_setelement(mesh->vert, k, vid[j], x0+eps);\n            if (!(*integrand) (v, mesh, i, nv, vid, ref, &fp)) return false;\n            matrix_setelement(mesh->vert, k, vid[j], x0-eps);\n            if (!(*integrand) (v, mesh, i, nv, vid, ref, &fm)) return false;\n            matrix_setelement(mesh->vert, k, vid[j], x0);\n\n            matrix_setelement(frc, k, vid[j], f0+(fp-fm)/(2*eps));\n        }\n    }\n    return true;\n}\n\nstatic bool functional_numericalremotegradient(vm *v, functional_mapinfo *info, objectsparse *conn, elementid remoteid, elementid i, int nv, int *vid, objectmatrix *frc) {\n    objectmesh *mesh = info->mesh;\n    double f0,fp,fm,x0,eps=1e-6;\n\n    // Loop over coordinates\n    for (unsigned int k=0; k<mesh->dim; k++) {\n        matrix_getelement(frc, k, remoteid, &f0);\n\n        matrix_getelement(mesh->vert, k, remoteid, &x0);\n        eps=functional_fdstepsize(x0, 1);\n        \n        matrix_setelement(mesh->vert, k, remoteid, x0+eps);\n        if (!(*info->integrand) (v, mesh, i, nv, vid, info->ref, &fp)) return false;\n        matrix_setelement(mesh->vert, k, remoteid, x0-eps);\n        if (!(*info->integrand) (v, mesh, i, nv, vid, info->ref, &fm)) return false;\n        matrix_setelement(mesh->vert, k, remoteid, x0);\n\n        matrix_setelement(frc, k, remoteid, f0+(fp-fm)/(2*eps));\n    }\n\n    return true;\n}\n\ndouble functional_sumlist(double *list, unsigned int nel) {\n    double sum=0.0, c=0.0, y,t;\n\n    for (unsigned int i=0; i<nel; i++) {\n        y=list[i]-c;\n        t=sum+y;\n        c=(t-sum)-y;\n        sum=t;\n    }\n\n    return sum;\n}\n\n/* *************************\n * Map functions\n * ************************* */\n\n/** Map numerical gradient over the elements\n * @param[in] v - virtual machine in use\n * @param[in] info - map info\n * @param[out] out - a matrix of integrand values\n * @returns true on success, false otherwise. Error reporting through VM. */\nbool functional_mapnumericalgradientX(vm *v, functional_mapinfo *info, value *out) {\n    objectmesh *mesh = info->mesh;\n    objectselection *sel = info->sel;\n    grade g = info->g;\n    functional_integrand *integrand = info->integrand;\n    void *ref = info->ref;\n    symmetrybhvr sym = info->sym;\n    objectsparse *s=NULL;\n    objectmatrix *frc=NULL;\n    bool ret=false;\n    int n=0;\n\n    varray_elementid dependencies;\n    if (info->dependencies) varray_elementidinit(&dependencies);\n\n    /* Find any image elements so we can skip over them */\n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    functional_symmetryimagelist(mesh, g, true, &imageids);\n\n    /* How many elements? */\n    if (!functional_countelements(v, mesh, g, &n, &s)) return false;\n\n    /* Create the output matrix */\n    if (n>0) {\n        frc=object_newmatrix(mesh->vert->nrows, mesh->vert->ncols, true);\n        if (!frc)  { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; }\n    }\n\n    if (frc) {\n        int vertexid; // Use this if looping over grade 0\n        int *vid=(g==0 ? &vertexid : NULL),\n            nv=(g==0 ? 1 : 0); // The vertex indices\n        int sindx=0; // Index into imageids array\n\n        if (sel) { // Loop over selection\n            if (sel->selected[g].count>0) for (unsigned int k=0; k<sel->selected[g].capacity; k++) {\n                if (!MORPHO_ISINTEGER(sel->selected[g].contents[k].key)) continue;\n\n                elementid i = MORPHO_GETINTEGERVALUE(sel->selected[g].contents[k].key);\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                // Skip this element if it's an image element\n                if ((imageids.count>0) && (sindx<imageids.count) && imageids.data[sindx]==i) { sindx++; continue; }\n\n                if (vid && nv>0) {\n                    if (!functional_numericalgradient(v, mesh, i, nv, vid, integrand, ref, frc)) goto functional_numericalgradient_cleanup;\n\n                    if (info->dependencies && // Loop over dependencies if there are any\n                        (info->dependencies) (info, i, &dependencies)) {\n                        for (int j=0; j<dependencies.count; j++) {\n                            if (!functional_numericalremotegradient(v, info, s, dependencies.data[j], i, nv, vid, frc)) goto functional_numericalgradient_cleanup;\n                        }\n                        dependencies.count=0;\n                    }\n                }\n            }\n        } else { // Loop over elements\n            for (elementid i=0; i<n; i++) {\n                // Skip this element if it's an image element\n                if ((imageids.count>0) && (sindx<imageids.count) && imageids.data[sindx]==i) { sindx++; continue; }\n\n                if (s) sparseccs_getrowindices(&s->ccs, i, &nv, &vid);\n                else vertexid=i;\n\n                if (vid && nv>0) {\n\n                    if (!functional_numericalgradient(v, mesh, i, nv, vid, integrand, ref, frc)) goto functional_numericalgradient_cleanup;\n\n                    if (info->dependencies && // Loop over dependencies if there are any\n                        (info->dependencies) (info, i, &dependencies)) {\n                        for (int j=0; j<dependencies.count; j++) {\n                            if (functional_containsvertex(nv, vid, dependencies.data[j])) continue;\n                            if (!functional_numericalremotegradient(v, info, s, dependencies.data[j], i, nv, vid, frc)) goto functional_numericalgradient_cleanup;\n                        }\n                        dependencies.count=0;\n                    }\n                }\n            }\n        }\n\n        if (sym==SYMMETRY_ADD) functional_symmetrysumforces(mesh, frc);\n\n        *out = MORPHO_OBJECT(frc);\n        ret=true;\n    }\n\nfunctional_numericalgradient_cleanup:\n    varray_elementidclear(&imageids);\n    if (info->dependencies) varray_elementidclear(&dependencies);\n    if (!ret) object_free((object *) frc);\n\n    return ret;\n}\n\nbool functional_mapnumericalfieldgradientX(vm *v, functional_mapinfo *info, value *out) {\n    objectmesh *mesh = info->mesh;\n    objectselection *sel = info->sel;\n    objectfield *field = info->field;\n    grade grd = info->g;\n    functional_integrand *integrand = info->integrand;\n    void *ref = info->ref;\n    //symmetrybhvr sym = info->sym;\n\n    double eps=1e-6;\n    bool ret=false;\n    objectsparse *conn=mesh_getconnectivityelement(mesh, 0, grd); // Connectivity for the element\n\n    /* Create the output field */\n    objectfield *grad=object_newfield(mesh, field->prototype, field->fnspc, field->dof);\n    if (!grad) return false;\n\n    field_zero(grad);\n\n    /* Loop over elements in the field */\n    for (grade g=0; g<field->ngrades; g++) {\n        if (field->dof[g]==0) continue;\n        int nentries=1, *entries, nv, *vid;\n        double fr,fl;\n        objectsparse *rconn=mesh_addconnectivityelement(mesh, grd, g); // Find dependencies for the grade\n\n        for (elementid id=0; id<mesh_nelementsforgrade(mesh, g); id++) {\n            entries = &id; // if there's no connectivity matrix, we'll just use the id itself\n\n            if ((!rconn) || mesh_getconnectivity(rconn, id, &nentries, &entries)) {\n                for (int i=0; i<nentries; i++) {\n                    if (conn) {\n                        if (sel) if (!selection_isselected(sel, grd, entries[i])) continue;\n                        // Check selections here\n                        sparseccs_getrowindices(&conn->ccs, entries[i], &nv, &vid);\n                    } else {\n                        if (sel) if (!selection_isselected(sel, grd, id)) continue;\n                        nv=1; vid=&id;\n                    }\n\n                    /* Loop over dofs in field entry */\n                    for (int j=0; j<field->psize*field->dof[g]; j++) {\n                        int k=field->offset[g]+id*field->psize*field->dof[g]+j;\n                        double fld=field->data.elements[k];\n                        \n                        eps=functional_fdstepsize(fld, 1);\n                        \n                        field->data.elements[k]+=eps;\n\n                        if (!(*integrand) (v, mesh, id, nv, vid, ref, &fr)) goto functional_mapnumericalfieldgradient_cleanup;\n\n                        field->data.elements[k]=fld-eps;\n\n                        if (!(*integrand) (v, mesh, id, nv, vid, ref, &fl)) goto functional_mapnumericalfieldgradient_cleanup;\n\n                        field->data.elements[k]=fld;\n\n                        grad->data.elements[k]+=(fr-fl)/(2*eps);\n                    }\n                }\n            }\n        }\n\n        *out = MORPHO_OBJECT(grad);\n        ret=true;\n    }\n\nfunctional_mapnumericalfieldgradient_cleanup:\n    if (!ret) object_free((object *) grad);\n\n    return ret;\n}\n\n/* **********************************************************************\n * Multithreaded map functions\n * ********************************************************************** */\n\nthreadpool functional_pool;\nbool functional_poolinitialized;\n\n/** Gradient function */\ntypedef bool (functional_mapfn) (vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, void *out);\n\n/** Optionally process results from mapfn */\ntypedef bool (functional_processfn) (void *task);\n\n/** Work to be done is divided into \"tasks\" which are then dispatched to the threadpool for execution. */\ntypedef struct {\n    elementid start, end; /* Start and end indices for the task */\n    elementid id; /* Current element id */\n    elementid nel; /* Current element id */\n    \n    varray_elementid *skip; /* Sorted list of element ids to skip; set to NULL if not needed */\n    unsigned int sindx;\n    \n    grade g; /* Grade of element */\n    objectsparse *conn; /* Connectivity matrix */\n    \n    functional_mapfn *mapfn; /* Map function */\n    functional_processfn *processfn; /* Post process results */\n    \n    vm *v; /* Virtual machine in use */\n    objectmesh *mesh; /* Mesh in use */\n    objectfield *field; /* Field in use */\n    objectselection *selection; /* Selection to use if any */\n    void *ref; /* Ref as an opaque pointer */\n    \n    void *result; /* Result of individual element as an opaque pointer */\n    void *out; /* Overall output as an opaque pointer */\n    _MORPHO_PADDING;\n} functional_task;\n\n/* Initialize a task structure */\nvoid functionaltask_init(functional_task *task, elementid start, elementid end, functional_mapinfo *info)  {\n    task->start=start;\n    task->end=end;\n    task->nel=0;\n    task->id=0;\n    task->g=(info ? info->g : 0);\n    \n    task->skip=NULL;\n    task->sindx=0;\n    \n    task->conn=NULL;\n    \n    task->mapfn=NULL;\n    task->processfn=NULL;\n    \n    task->mesh=(info ? info->mesh : NULL);\n    task->field=(info ? info->field : NULL);\n    task->selection=(info ? info->sel : NULL);\n    \n    task->v=NULL;\n    task->ref=(info ? info->ref : NULL);\n    task->out=NULL;\n    task->result=NULL;\n}\n\n/** Check if we should skip element id */\nbool functional_checkskip(functional_task *task) {\n    if ((task->skip) &&\n        (task->sindx<task->skip->count) &&\n        task->skip->data[task->sindx]==task->id) {\n        task->sindx++;\n        return true;\n    }\n    return false;\n}\n\n/** Worker function to map a function over elements */\nbool functional_mapfn_elements(void *arg) {\n    functional_task *task = (functional_task *) arg;\n    dictionary *selected=NULL;\n    elementid *vid=&task->id; /* Will hold element definition */\n    int nv=1; /* Number of vertices per element; default to 1  */\n    \n    if (task->selection) {\n        selected=&task->selection->selected[task->g];\n        if (selected->count==0) return true;\n    }\n    \n    // Loop over required elements\n    for (elementid i=task->start; i<task->end; i++) {\n        if (selected) {\n            // Skip empty dictionary entries\n            if (!MORPHO_ISINTEGER(selected->contents[i].key)) continue;\n            \n            // Fetch the element id from the dictionary\n            task->id = MORPHO_GETINTEGERVALUE(selected->contents[i].key);\n        } else task->id = i;\n        \n        // Skip this element if it's an image element\n        if (functional_checkskip(task)) continue;\n        \n        // Fetch element definition\n        if (task->conn) {\n            if (!sparseccs_getrowindices(&task->conn->ccs, task->id, &nv, &vid)) return false;\n        }\n        \n        // Perform the map function\n        if (!(*task->mapfn) (task->v, task->mesh, task->id, nv, vid, task->ref, task->result)) return false;\n        \n        // Perform post-processing if needed\n        if (task->processfn) if (!(*task->processfn) (task)) return false;\n        \n        // Clean out temporary objects\n        vm_cleansubkernel(task->v);\n    }\n    return true;\n}\n\n/** Dispatches tasks to threadpool */\nbool functional_parallelmap(int ntasks, functional_task *tasks) {\n    int nthreads = morpho_threadnumber();\n    if (!nthreads) nthreads=1;\n    \n    if (!functional_poolinitialized) {\n        functional_poolinitialized=threadpool_init(&functional_pool, nthreads);\n        if (!functional_poolinitialized) return false;\n    }\n    \n    for (int i=0; i<ntasks; i++) {\n       threadpool_add_task(&functional_pool, functional_mapfn_elements, (void *) &tasks[i]);\n    }\n    threadpool_fence(&functional_pool);\n    \n    return true;\n}\n\n/** Calculate bin sizes */\nvoid functional_binbounds(int nel, int nbins, int *binbounds) {\n    int binsizes[nbins+1];\n    \n    int defsize = nel / nbins;\n    for (int i=0; i<nbins; i++) binsizes[i]=defsize;\n    \n    int rem = nel % nbins;\n    while (rem>0) {\n        for (int i=0; i<nbins && rem>0; i++) { binsizes[i]++; rem--; }\n    }\n    \n    int bindx=0;\n    for (int i=0; i<=nbins; i++) {\n        binbounds[i]=bindx;\n        bindx+=binsizes[i];\n    }\n}\n\n/** Prepare tasks for submitting\n * @param[in] v - Virtual machine to use\n * @param[in] info - Info structure with functional information\n * @param[in] ntask - Number of tasks\n * @param[out] task - Task structures updated\n * @param[out] imageids - Updated to include symmetry image ids */\nint functional_preparetasks(vm *v, functional_mapinfo *info, int ntask, functional_task *task, varray_elementid *imageids) {\n    int nel=0;\n    objectsparse *conn=NULL; // The associated connectivity matrix if any\n    \n    /* Work out the number of elements */\n    if (!functional_countelements(v, info->mesh, info->g, &nel, &conn)) return false;\n    \n    int cmax=nel;\n    if (info->sel) {\n        cmax=info->sel->selected[info->g].capacity;\n    }\n    \n    int bins[ntask+1];\n    functional_binbounds(cmax, ntask, bins);\n    \n    /* Ensure all mesh topology matrices have CCS */\n    int maxgrade=mesh_maxgrade(info->mesh);\n    for (int i=0; i<=maxgrade; i++) {\n        for (int j=0; j<=maxgrade; j++) {\n            objectsparse *s = mesh_getconnectivityelement(info->mesh, i, j);\n            if (s) sparse_checkformat(s, SPARSE_CCS, true, false);\n        }\n    }\n    \n    /* Find any image elements so they can be skipped */\n    functional_symmetryimagelist(info->mesh, info->g, true, imageids);\n    if (info->field) field_addpool(info->field);\n    \n    vm *subkernels[ntask];\n    if (!vm_subkernels(v, ntask, subkernels)) return false; \n    \n    /** Initialize task structures */\n    for (int i=0; i<ntask; i++) {\n        functionaltask_init(task+i, bins[i], bins[i+1], info); // Setup the task\n        \n        task[i].v=subkernels[i];\n        task[i].nel=nel;\n        task[i].conn=conn;\n        if (imageids->count>0) task[i].skip=imageids;\n    }\n    \n    return true;\n}\n\n/** Cleans up task structures after executing them. */\nvoid functional_cleanuptasks(vm *v, int ntask, functional_task *task) {\n    for (int i=0; i<ntask; i++) {\n        if (task[i].v!=v) vm_releasesubkernel(task[i].v);\n    }\n}\n\n/* ----------------------------\n * Sum integrands\n * ---------------------------- */\n\n/* Structure to store intermediate results for Kahan summation */\ntypedef struct {\n    double result;\n    double c;\n    double sum;\n    _MORPHO_PADDING;\n} functional_sumintermediate;\n\n/** Perform Kahan summation for total */\nbool functional_sumintegrandprocessfn(void *arg) {\n    functional_task *task = (functional_task *) arg;\n    functional_sumintermediate *ks = (functional_sumintermediate *) task->out;\n    double y=ks->result-ks->c;\n    double t=ks->sum+y;\n    ks->c=(t-ks->sum)-y;\n    ks->sum=t; // Kahan summation\n    return true;\n}\n\n/** Sum the integrand, mapping over integrand function */\nbool functional_sumintegrand(vm *v, functional_mapinfo *info, value *out) {\n    int ntask=morpho_threadnumber();\n    if (!ntask) return functional_sumintegrandX(v, info, out);\n    \n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    functional_sumintermediate sums[ntask];\n    \n    for (int i=0; i<ntask; i++) {\n        task[i].mapfn=(functional_mapfn *) info->integrand;\n        task[i].processfn=functional_sumintegrandprocessfn;\n        \n        task[i].result=(void *) &sums[i].result;\n        task[i].out=(void *) &sums[i];\n        sums[i].c=0.0; sums[i].sum=0.0;\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    // Sum up the results from each task...\n    double sumlist[ntask];\n    for (int i=0; i<ntask; i++) sumlist[i]=sums[i].sum;\n    \n    // ...and return the result\n    *out = MORPHO_FLOAT(functional_sumlist(sumlist, ntask));\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    return true;\n}\n\n/* ----------------------------\n * Map integrands\n * ---------------------------- */\n\n/** Calculate the integrand at a particular element\n * @param[in] v - virtual machine in use\n * @param[in] info - map info\n * @param[out] out - a matrix of integrand values\n * @returns true on success, false otherwise. Error reporting through VM. */\nbool functional_mapintegrandforelement(vm *v, functional_mapinfo *info, value *out) {\n    objectmesh *mesh = info->mesh;\n    grade g = info->g;\n    elementid id = info->id;\n    functional_integrand *integrand = info->integrand;\n    void *ref = info->ref;\n    objectsparse *s=NULL;\n    bool ret=false;\n    int n=0;\n    \n    /* How many elements? */\n    if (!functional_countelements(v, mesh, g, &n, &s)) return false;\n    // Check if the requested element id is out of range\n    if (id>=n) return false;\n    \n    int vertexid; // Use this if looping over grade 0\n    int *vid=(g==0 ? &vertexid : NULL),\n        nv=(g==0 ? 1 : 0); // The vertex indices\n    if (s) sparseccs_getrowindices(&s->ccs, id, &nv, &vid);\n    else vertexid=id;\n\n    double result=0.0;\n    if (vid && nv>0) {\n        if (! (*integrand) (v, mesh, id, nv, vid, ref, &result)) {\n            return false;\n        }\n    }\n    *out = MORPHO_FLOAT(result);\n    ret=true;\n    \n    return ret;\n}\n\n/** Set relevant matrix element to the result of the integrand */\nbool functional_mapintegrandprocessfn(void *arg) {\n    functional_task *task = (functional_task *) arg;\n    objectmatrix *new = (objectmatrix *) task->out;\n    matrix_setelement(new, 0, task->id, *(double *) task->result);\n    return true;\n}\n\n/** Map integrand function, storing the results in a matrix */\nbool functional_mapintegrand(vm *v, functional_mapinfo *info, value *out) {\n    int ntask=morpho_threadnumber();\n    if (!ntask) return functional_mapintegrandX(v, info, out);\n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    objectmatrix *new = NULL;\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    /* Create output matrix */\n    if (task[0].nel>0) {\n        new=object_newmatrix(1, task[0].nel, true);\n        if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; }\n    }\n    \n    functional_sumintermediate sums[ntask];\n    \n    for (int i=0; i<ntask; i++) {\n        task[i].mapfn=(functional_mapfn *) info->integrand;\n        task[i].processfn=functional_mapintegrandprocessfn;\n    \n        task[i].result=(void *) &sums[i].result;\n        task[i].out=(void *) new;\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    // ...and return the result\n    *out = MORPHO_OBJECT(new);\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    return true;\n}\n\n/* ----------------------------\n * Map gradients\n * ---------------------------- */\n\n/** Compute the gradient */\nbool functional_mapgradient(vm *v, functional_mapinfo *info, value *out) {\n    int success=false;\n    int ntask=morpho_threadnumber();\n    if (!ntask) return functional_mapgradientX(v, info, out);\n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    objectmatrix *new[ntask];\n    for (int i=0; i<ntask; i++) new[i]=NULL;\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    /* Create output matrix */\n    for (int i=0; i<ntask; i++) {\n        // Create one per thread\n        new[i]=object_newmatrix(info->mesh->vert->nrows, info->mesh->vert->ncols, true);\n        if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapgradient_cleanup; }\n        \n        task[i].mapfn=(functional_mapfn *) info->grad;\n        task[i].result=(void *) new[i];\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    /* Then add up all the matrices */\n    for (int i=1; i<ntask; i++) matrix_add(new[0], new[i], new[0]);\n    \n    // Use symmetry actions\n    if (info->sym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]);\n    \n    success=true;\n    \nfunctional_mapgradient_cleanup:\n    for (int i=1; i<ntask; i++) if (new[i]) object_free((object *) new[i]);\n    \n    // ...and return the result\n    *out = MORPHO_OBJECT(new[0]);\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    return success;\n}\n\n/* ----------------------------\n * Map numerical gradients\n * ---------------------------- */\n\n/** Computes the gradient of element eid with respect to vertex i */\nbool functional_numericalgrad(vm *v, objectmesh *mesh, elementid eid, elementid i, int nv, int *vid, functional_integrand *integrand, void *ref, objectmatrix *frc) {\n    double f0,fp,fm,x0,eps=1e-6;\n    \n    // Loop over coordinates\n    for (unsigned int k=0; k<mesh->dim; k++) {\n        matrix_getelement(frc, k, i, &f0);\n\n        matrix_getelement(mesh->vert, k, i, &x0);\n        \n        eps=functional_fdstepsize(x0, 1);\n        matrix_setelement(mesh->vert, k, i, x0+eps);\n        if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fp)) return false;\n        matrix_setelement(mesh->vert, k, i, x0-eps);\n        if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fm)) return false;\n        matrix_setelement(mesh->vert, k, i, x0);\n\n        matrix_setelement(frc, k, i, f0+(fp-fm)/(2*eps));\n    }\n    \n    return true;\n}\n\n/** Computes the gradient of element id with respect to its constituent vertices and any dependencies */\nbool functional_numericalgradientmapfn(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, void *out) {\n    bool success=true;\n    functional_mapinfo *info=(functional_mapinfo *) ref;\n    \n    for (int i=0; i<nv; i++) {\n        if (!functional_numericalgrad(v, mesh, id, vid[i], nv, vid, info->integrand, info->ref, out)) return false;\n    }\n    \n    // Now handle dependencies\n    if (info->dependencies) {\n        varray_elementid dependencies;\n        varray_elementidinit(&dependencies);\n        \n        // Get list of vertices this element depends on\n        if ((info->dependencies) (info, id, &dependencies)) {\n            for (int j=0; j<dependencies.count; j++) {\n                if (functional_containsvertex(nv, vid, dependencies.data[j])) continue;\n                if (!functional_numericalgrad(v, mesh, id, dependencies.data[j], nv, vid, info->integrand, info->ref, out)) success=false;\n            }\n        }\n        \n        varray_elementidclear(&dependencies);\n    }\n    \n    return success;\n}\n\n/** Compute the gradient numerically */\nbool functional_mapnumericalgradient(vm *v, functional_mapinfo *info, value *out) {\n    int success=false;\n    int ntask=morpho_threadnumber();\n    if (!ntask) return functional_mapnumericalgradientX(v, info, out);\n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    objectmatrix *new[ntask]; // Create an output matrix for each thread\n    for (int i=0; i<ntask; i++) new[i]=NULL;\n    \n    objectmesh meshclones[ntask]; // Create shallow clones of the mesh with different vertex matrices\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    for (int i=0; i<ntask; i++) {\n        // Create one output matrix per thread\n        new[i]=object_newmatrix(info->mesh->vert->nrows, info->mesh->vert->ncols, true);\n        if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapgradient_cleanup; }\n        \n        // Clone the vertex matrix for each thread\n        meshclones[i]=*info->mesh;\n        meshclones[i].vert=object_clonematrix(info->mesh->vert);\n        task[i].mesh=&meshclones[i];\n        \n        task[i].ref=(void *) info; // Use this to pass the info structure\n        task[i].mapfn=functional_numericalgradientmapfn;\n        task[i].result=(void *) new[i];\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    /* Then add up all the matrices */\n    for (int i=1; i<ntask; i++) matrix_add(new[0], new[i], new[0]);\n    \n    success=true;\n    \n    // Use symmetry actions\n    if (info->sym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]);\n    \n    // ...and return the result\n    *out = MORPHO_OBJECT(new[0]);\n    \nfunctional_mapgradient_cleanup:\n    // Free the temporary copies of the vertex matrices\n    for (int i=0; i<ntask; i++) object_free((object *) meshclones[i].vert);\n    // Free spare output matrices\n    for (int i=1; i<ntask; i++) if (new[i]) object_free((object *) new[i]);\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    \n    return success;\n}\n\n/* ----------------------------\n * Map field gradients\n * ---------------------------- */\n\n/** Compute the field gradient */\nbool functional_mapfieldgradient(vm *v, functional_mapinfo *info, value *out) {\n    int success=false;\n    int ntask=morpho_threadnumber();\n    if (!ntask) return functional_mapfieldgradientX(v, info, out);\n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    objectfield *new[ntask];\n    for (int i=0; i<ntask; i++) new[i]=NULL;\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    /* Create output fields */\n    for (int i=0; i<ntask; i++) {\n        // Create one per thread\n        new[i]=object_newfield(info->mesh, info->field->prototype, info->field->fnspc, info->field->dof);\n        if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapfieldgradient_cleanup; }\n        field_zero(new[i]);\n        \n        task[i].mapfn=(functional_mapfn *) info->fieldgrad;\n        task[i].result=(void *) new[i];\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    /* Then add up all the fields using their underlying data stores */\n    for (int i=1; i<ntask; i++) matrix_add(&new[0]->data, &new[1]->data, &new[0]->data);\n    \n    // TODO: Use symmetry actions\n    //if (info->sym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]);\n    \n    success=true;\n    \nfunctional_mapfieldgradient_cleanup:\n    for (int i=1; i<ntask; i++) if (new[i]) object_free((object *) new[i]);\n    \n    // ...and return the result\n    *out = MORPHO_OBJECT(new[0]);\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    return success;\n}\n\n/* ----------------------------\n * Map numerical field gradients\n * ---------------------------- */\n\n/** Computes the field gradient of element eid with respect to field grade g element i */\nbool functional_numericalfieldgrad(vm *v, objectmesh *mesh, elementid eid, objectfield *field, grade g, elementid i, int nv, int *vid, functional_integrand *integrand, void *ref, objectfield *grad) {\n    double fr,fl,eps=1e-6;\n    \n    /* Loop over dofs in field entry */\n    for (int j=0; j<field->psize*field->dof[g]; j++) {\n        int k=(field->offset[g]+i)*field->psize*field->dof[g]+j;\n        \n        double f0=field->data.elements[k];\n        \n        eps=functional_fdstepsize(f0, 1);\n        \n        field->data.elements[k]+=eps;\n        if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fr)) return false;\n\n        field->data.elements[k]=f0-eps;\n        if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fl)) return false;\n\n        field->data.elements[k]=f0;\n\n        grad->data.elements[k]+=(fr-fl)/(2*eps);\n    }\n    \n    return true;\n}\n\ntypedef struct {\n    objectfield *field;\n    functional_integrand *integrand;\n    fespace *disc;\n    void *ref;\n} functional_numericalfieldgradientref;\n\n/** Computes the gradient of element id with respect to its constituent vertices and any dependencies */\nbool functional_numericalfieldgradientmapfn(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, void *out) {\n    functional_numericalfieldgradientref *tref=(functional_numericalfieldgradientref *) ref;\n    \n    if (tref->disc) {\n        int nnodes=tref->disc->nnodes;\n        fieldindx findx[nnodes];\n        \n        if (fespace_doftofieldindx(tref->field, tref->disc, nv, vid, findx)) {\n            for (int k=0; k<nnodes; k++) {\n                if (!functional_numericalfieldgrad(v, mesh, id, tref->field, findx[k].g, findx[k].id, nv, vid, tref->integrand, tref->ref, out)) return false;\n            }\n        }\n        \n    } else {\n        for (elementid k=0; k<nv; k++) {\n            if (!functional_numericalfieldgrad(v, mesh, id, tref->field, MESH_GRADE_VERTEX, vid[k], nv, vid, tref->integrand, tref->ref, out)) return false;\n        }\n    }\n    \n    return true;\n}\n\n/** Compute the field gradient numerically */\nbool functional_mapnumericalfieldgradient(vm *v, functional_mapinfo *info, value *out) {\n    int success=false;\n    int ntask=morpho_threadnumber();\n    //if (!ntask) return functional_mapnumericalfieldgradientX(v, info, out);\n    if (ntask<1) ntask=1;\n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    objectfield *new[ntask]; // Create an output field for each thread\n    objectfield *fieldclones[ntask]; // Create clones of the field for each thread\n    functional_numericalfieldgradientref tref[ntask];\n    for (int i=0; i<ntask; i++) {\n        new[i]=NULL; fieldclones[i]=NULL;\n    }\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    for (int i=0; i<ntask; i++) {\n        // Create one output field per thread\n        new[i]=object_newfield(info->mesh, info->field->prototype, info->field->fnspc, info->field->dof);\n        if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapfieldgradient_cleanup; }\n        field_zero(new[i]);\n        \n        // Clone the vertex matrix for each thread\n        fieldclones[i]=field_clone(info->field);\n        tref[i].ref=info->ref;\n        if (info->cloneref) {\n            tref[i].ref=(info->cloneref) (info->ref, info->field, fieldclones[i]);\n        } else UNREACHABLE(\"Functional calls numericalfieldgradient but doesn't provide cloneref\");\n        tref[i].integrand=info->integrand;\n        tref[i].field=fieldclones[i];\n        tref[i].disc=NULL;\n        if (MORPHO_ISFESPACE(tref[i].field->fnspc)) {\n            tref[i].disc=MORPHO_GETFESPACE(tref[i].field->fnspc)->fespace;\n            if (info->g<tref[i].disc->grade) {\n                if (!fespace_lower(tref[i].disc, info->g, &tref[i].disc)) return false;\n            }\n        }\n        \n        task[i].ref=(void *) &tref[i]; // Use this to pass the info structure\n        task[i].mapfn=functional_numericalfieldgradientmapfn;\n        task[i].result=(void *) new[i];\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    /* Then add up all the fields */\n    for (int i=1; i<ntask; i++) matrix_add(&new[0]->data, &new[i]->data, &new[0]->data);\n    \n    success=true;\n    \n    // ...and return the result\n    *out = MORPHO_OBJECT(new[0]);\n    \nfunctional_mapfieldgradient_cleanup:\n    for (int i=0; i<ntask; i++) {\n        // Free any cloned references\n        if (info->freeref) (info->freeref) (tref[i].ref);\n        else if (info->cloneref) MORPHO_FREE(tref[i].ref);\n        \n        // Free the temporary copies of the fields\n        object_free((object *) fieldclones[i]);\n    }\n    \n    // Free spare output matrices\n    for (int i=1; i<ntask; i++) if (new[i]) object_free((object *) new[i]);\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    \n    return success;\n}\n\n/* ----------------------------\n * Map numerical hessians\n * ---------------------------- */\n\n/** Adds a value to an element of a sparse matrix */\nbool functional_sparseaccumulate(objectsparse *A, int i, int j, double val) {\n    double f0 = 0.0;\n    value h0;\n    if (sparsedok_get(&A->dok, i, j, &h0)) {\n        if (!morpho_valuetofloat(h0, &f0)) return false;\n    }\n    \n    sparsedok_insert(&A->dok, i, j, MORPHO_FLOAT(f0+val));\n    return true;\n}\n\n/** Computes the contribution to the hessian of element eid with respect to vertices i and j */\nbool functional_numericalhess(vm *v, objectmesh *mesh, elementid eid, elementid i, elementid j, int nv, int *vid, functional_integrand *integrand, void *ref, objectsparse *hess) {\n    double x0,y0,epsx=1e-4,epsy=1e-4;\n    \n    for (unsigned int k=0; k<mesh->dim; k++) { // Loop over coordinates in vertex i\n        matrix_getelement(mesh->vert, k, i, &x0);\n        epsx=functional_fdstepsize(x0, 2);\n        \n        if (i==j) { // Use a special formula for diagonal elements\n            double fc, fr, fl;\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fc)) return false;\n            \n            matrix_setelement(mesh->vert, k, i, x0+epsx);\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fr)) return false;\n            \n            matrix_setelement(mesh->vert, k, i, x0-epsx);\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fl)) return false;\n            \n            matrix_setelement(mesh->vert, k, i, x0); // Restore vertex to original position\n            \n            functional_sparseaccumulate(hess, i*mesh->dim+k, i*mesh->dim+k, (fr + fl - 2*fc)/(epsx*epsx));\n        }\n        \n        // Loop over coordinates in vertex j\n        for (unsigned int l=0; //(i==j? k+1 : k); // Detect whether we're in an off diagonal block\n             l<mesh->dim; l++) {\n            if (i==j && k==l) continue;\n            double fll,frr,flr,frl;\n            \n            matrix_getelement(mesh->vert, l, j, &y0);\n            epsy=functional_fdstepsize(y0, 2);\n            \n            matrix_setelement(mesh->vert, k, i, x0+epsx);\n            matrix_setelement(mesh->vert, l, j, y0+epsy);\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &frr)) return false;\n            \n            matrix_setelement(mesh->vert, l, j, y0-epsy);\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &frl)) return false;\n            \n            matrix_setelement(mesh->vert, k, i, x0-epsx);\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fll)) return false;\n            \n            matrix_setelement(mesh->vert, l, j, y0+epsy);\n            if (!(*integrand) (v, mesh, eid, nv, vid, ref, &flr)) return false;\n            \n            matrix_setelement(mesh->vert, k, i, x0); // Restore vertices to original position\n            matrix_setelement(mesh->vert, l, j, y0);\n            \n            functional_sparseaccumulate(hess, i*mesh->dim+k, j*mesh->dim+l, (frr + fll - flr - frl)/(4*epsx*epsy));\n            //functional_sparseaccumulate(hess, j*mesh->dim+l, i*mesh->dim+k, (frr + fll - flr - frl)/(4*epsx*epsy));\n        }\n    }\n    \n    return true;\n}\n\n/** Computes the gradient of element id with respect to its constituent vertices and any dependencies */\nbool functional_numericalhessianmapfn(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, void *out) {\n    bool success=true;\n    functional_mapinfo *info=(functional_mapinfo *) ref;\n    \n    // TODO: Exploit symmetry of hessian to reduce work\n    \n    for (int i=0; i<nv; i++) {\n        for (int j=0; j<nv; j++) {\n            if (!functional_numericalhess(v, mesh, id, vid[i], vid[j], nv, vid, info->integrand, info->ref, out)) return false;\n        }\n    }\n    \n    // Now handle dependencies\n    if (info->dependencies) {\n        varray_elementid dependencies;\n        varray_elementidinit(&dependencies);\n        \n        // Get list of vertices this element depends on\n        if ((info->dependencies) (info, id, &dependencies)) {\n            for (int i=0; i<dependencies.count; i++) {\n                for (int j=0; j<dependencies.count; j++) {\n                    if (functional_containsvertex(nv, vid, dependencies.data[i]) && functional_containsvertex(nv, vid, dependencies.data[j])) continue;\n                    if (!functional_numericalhess(v, mesh, id, dependencies.data[i], dependencies.data[j], nv, vid, info->integrand, info->ref, out)) success=false;\n                }\n            }\n        }\n        \n        varray_elementidclear(&dependencies);\n    }\n    \n    return success;\n}\n\nstatic int _sparsecmp(const void *a, const void *b) {\n    objectsparse *aa = *(objectsparse **) a;\n    objectsparse *bb = *(objectsparse **) b;\n    return bb->dok.dict.count - aa->dok.dict.count;\n}\n\n/** Compute the hessian numerically */\nbool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) {\n    int success=false;\n    int ntask=morpho_threadnumber();\n    if (ntask==0) ntask = 1;\n    functional_task task[ntask];\n    \n    varray_elementid imageids;\n    varray_elementidinit(&imageids);\n    \n    objectsparse *new[ntask]; // Create an output matrix for each thread\n    objectmesh meshclones[ntask]; // Create shallow clones of the mesh with different vertex matrices\n    \n    for (int i=0; i<ntask; i++) {\n        new[i]=NULL;\n        meshclones[i].vert=NULL;\n    }\n    \n    if (!functional_preparetasks(v, info, ntask, task, &imageids)) return false;\n    \n    for (int i=0; i<ntask; i++) {\n        int N = info->mesh->dim*mesh_nvertices(info->mesh);\n        \n        // Create one output matrix per thread\n        new[i]=object_newsparse(&N, &N);\n        if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_maphessian_cleanup; }\n        \n        // Clone the vertex matrix for each thread\n        meshclones[i]=*info->mesh;\n        meshclones[i].vert=object_clonematrix(info->mesh->vert);\n        if (!meshclones[i].vert) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_maphessian_cleanup; }\n        task[i].mesh=&meshclones[i];\n        \n        task[i].ref=(void *) info; // Use this to pass the info structure\n        task[i].mapfn=functional_numericalhessianmapfn;\n        task[i].result=(void *) new[i];\n    }\n    \n    functional_parallelmap(ntask, task);\n    \n    qsort(new, ntask, sizeof(objectsparse *), _sparsecmp);\n    \n    if (!sparse_checkformat(new[0], SPARSE_CCS, true, true)) {\n        morpho_runtimeerror(v, SPARSE_OPFAILEDERR);\n        goto functional_maphessian_cleanup;\n    }\n    \n    /* Then add up all the matrices */\n    for (int i=1; i<ntask; i++) {\n        if (!new[i]->dok.dict.count) continue;\n        objectsparse out = MORPHO_STATICSPARSE();\n        sparsedok_init(&out.dok);\n        sparseccs_init(&out.ccs);\n        \n        if (sparse_add(new[0], new[i], 1.0, 1.0, &out)==SPARSE_OK) {\n            sparseccs_clear(&new[0]->ccs);\n            new[0]->ccs = out.ccs;\n        } else {\n            morpho_runtimeerror(v, SPARSE_OPFAILEDERR);\n            goto functional_maphessian_cleanup;\n        }\n    }\n    success=true;\n    \n    // Use symmetry actions\n    //if (info->sym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]);\n    \n    sparsedok_clear(&new[0]->dok); // Remove dok info\n    \n    // ...and return the result\n    *out = MORPHO_OBJECT(new[0]);\n    \nfunctional_maphessian_cleanup:\n    // Free the temporary copies of the vertex matrices\n    for (int i=0; i<ntask; i++) object_free((object *) meshclones[i].vert);\n    // Free spare output matrices\n    for (int i=1; i<ntask; i++) if (new[i]) object_free((object *) new[i]);\n    \n    functional_cleanuptasks(v, ntask, task);\n    varray_elementidclear(&imageids);\n    \n    return success;\n}\n\n\n/* **********************************************************************\n * Common library functions\n * ********************************************************************** */\n\n/** Calculate the difference of two vectors */\nvoid functional_vecadd(unsigned int n, double *a, double *b, double *out) {\n    for (unsigned int i=0; i<n; i++) out[i]=a[i]+b[i];\n}\n\n/** Add with scale */\nvoid functional_vecaddscale(unsigned int n, double *a, double lambda, double *b, double *out) {\n    for (unsigned int i=0; i<n; i++) out[i]=a[i]+lambda*b[i];\n}\n\n/** Calculate the difference of two vectors */\nvoid functional_vecsub(unsigned int n, double *a, double *b, double *out) {\n    for (unsigned int i=0; i<n; i++) out[i]=a[i]-b[i];\n}\n\n/** Scale a vector */\nvoid functional_vecscale(unsigned int n, double lambda, double *a, double *out) {\n    for (unsigned int i=0; i<n; i++) out[i]=lambda*a[i];\n}\n\n/** Calculate the norm of a vector */\ndouble functional_vecnorm(unsigned int n, double *a) {\n    return cblas_dnrm2(n, a, 1);\n}\n\n/** Dot product of two vectors */\ndouble functional_vecdot(unsigned int n, double *a, double *b) {\n    return cblas_ddot(n, a, 1, b, 1);\n}\n\n/** 3D cross product  */\nvoid functional_veccross(double *a, double *b, double *out) {\n    out[0]=a[1]*b[2]-a[2]*b[1];\n    out[1]=a[2]*b[0]-a[0]*b[2];\n    out[2]=a[0]*b[1]-a[1]*b[0];\n}\n\n/** 2D cross product  */\nvoid functional_veccross2d(double *a, double *b, double *out) {\n    *out=a[0]*b[1]-a[1]*b[0];\n}\n\nbool length_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out);\nbool area_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out);\nbool volume_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out);\n\n/** Calculate element size */\nbool functional_elementsize(vm *v, objectmesh *mesh, grade g, elementid id, int nv, int *vid, double *out) {\n    switch (g) {\n        case 1: return length_integrand(v, mesh, id, nv, vid, NULL, out);\n        case 2: return area_integrand(v, mesh, id, nv, vid, NULL, out);\n        case 3: return volume_integrand(v, mesh, id, nv, vid, NULL, out);\n    }\n    return false;\n}\n\nbool length_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale);\nbool area_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale);\nbool volume_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale);\n\nbool length_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc);\nbool area_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc);\nbool volume_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc);\n\n/** Calculate a scaled element gradient */\nbool functional_elementgradient_scale(vm *v, objectmesh *mesh, grade g, elementid id, int nv, int *vid, objectmatrix *frc, double scale) {\n    switch (g) {\n        case 1: return length_gradient_scale(v, mesh, id, nv, vid, NULL, frc, scale);\n        case 2: return area_gradient_scale(v, mesh, id, nv, vid, NULL, frc, scale);\n        case 3: return volume_gradient_scale(v, mesh, id, nv, vid, NULL, frc, scale);\n    }\n    return false;\n}\n\n/** Calculate element gradient */\nbool functional_elementgradient(vm *v, objectmesh *mesh, grade g, elementid id, int nv, int *vid, objectmatrix *frc) {\n    switch (g) {\n        case 1: return length_gradient(v, mesh, id, nv, vid, NULL, frc);\n        case 2: return area_gradient(v, mesh, id, nv, vid, NULL, frc);\n        case 3: return volume_gradient(v, mesh, id, nv, vid, NULL, frc);\n    }\n    return false;\n}\n\n/* ----------------------------------------------\n * Length\n * ---------------------------------------------- */\n\n/** Calculate area */\nbool length_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    if (nv!=2) return false;\n    double *x[nv], s0[mesh->dim];\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    functional_vecsub(mesh->dim, x[1], x[0], s0);\n\n    *out=functional_vecnorm(mesh->dim, s0);\n    return true;\n}\n\n/** Calculate scaled gradient */\nbool length_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale) {\n    double *x[nv], s0[mesh->dim], norm;\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    functional_vecsub(mesh->dim, x[1], x[0], s0);\n    norm=functional_vecnorm(mesh->dim, s0);\n    if (norm<MORPHO_EPS) return false;\n\n    matrix_addtocolumn(frc, vid[0], -1.0/norm*scale, s0);\n    matrix_addtocolumn(frc, vid[1], 1./norm*scale, s0);\n\n    return true;\n}\n\n/** Calculate gradient */\nbool length_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n    return length_gradient_scale(v, mesh, id, nv, vid, NULL, frc, 1.0);\n}\n\nFUNCTIONAL_INIT(Length, MESH_GRADE_LINE)\nFUNCTIONAL_INTEGRAND(Length, MESH_GRADE_LINE, length_integrand)\nFUNCTIONAL_INTEGRANDFORELEMENT(Length, MESH_GRADE_LINE, length_integrand)\nFUNCTIONAL_GRADIENT(Length, MESH_GRADE_LINE, length_gradient, SYMMETRY_ADD)\nFUNCTIONAL_TOTAL(Length, MESH_GRADE_LINE, length_integrand)\nFUNCTIONAL_HESSIAN(Length, MESH_GRADE_LINE, length_integrand)\n\nMORPHO_BEGINCLASS(Length)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, Length_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, Length_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRANDFORELEMENT_METHOD, Length_integrandForElement, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, Length_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, Length_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, Length_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Enclosed area\n * ---------------------------------------------- */\n\n/** Calculate area enclosed */\nbool areaenclosed_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    double *x[nv], cx[mesh->dim], normcx;\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    if (mesh->dim==2) {\n        functional_veccross2d(x[0], x[1], cx);\n        normcx=fabs(cx[0]);\n    } else {\n        functional_veccross(x[0], x[1], cx);\n        normcx=functional_vecnorm(mesh->dim, cx);\n    }\n\n    *out=0.5*normcx;\n\n    return true;\n}\n\n/** Calculate gradient */\nbool areaenclosed_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n    double *x[nv], cx[3], s[3];\n    double norm;\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    if (mesh->dim==3) {\n        functional_veccross(x[0], x[1], cx);\n        norm=functional_vecnorm(mesh->dim, cx);\n        if (norm<MORPHO_EPS) return false;\n\n        functional_veccross(x[1], cx, s);\n        matrix_addtocolumn(frc, vid[0], 0.5/norm, s);\n\n        functional_veccross(cx, x[0], s);\n        matrix_addtocolumn(frc, vid[1], 0.5/norm, s);\n    } else if (mesh->dim==2) {\n        functional_veccross2d(x[0], x[1], cx);\n\n    }\n\n    return true;\n}\n\nFUNCTIONAL_INIT(AreaEnclosed, MESH_GRADE_LINE)\nFUNCTIONAL_INTEGRAND(AreaEnclosed, MESH_GRADE_LINE, areaenclosed_integrand)\nFUNCTIONAL_INTEGRANDFORELEMENT(AreaEnclosed, MESH_GRADE_LINE, areaenclosed_integrand)\nFUNCTIONAL_NUMERICALGRADIENT(AreaEnclosed, MESH_GRADE_LINE, areaenclosed_integrand, SYMMETRY_ADD)\n//FUNCTIONAL_GRADIENT(AreaEnclosed, MESH_GRADE_LINE, areaenclosed_gradient, SYMMETRY_ADD)\nFUNCTIONAL_TOTAL(AreaEnclosed, MESH_GRADE_LINE, areaenclosed_integrand)\nFUNCTIONAL_HESSIAN(AreaEnclosed, MESH_GRADE_LINE, areaenclosed_integrand)\n\nMORPHO_BEGINCLASS(AreaEnclosed)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, AreaEnclosed_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, AreaEnclosed_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRANDFORELEMENT_METHOD, AreaEnclosed_integrandForElement, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, AreaEnclosed_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, AreaEnclosed_hessian, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, AreaEnclosed_total, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Area\n * ---------------------------------------------- */\n\n/** Calculate area */\nbool area_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    if (nv!=3) return false;\n    double *x[nv], s0[3], s1[3], cx[3];\n    for (int j=0; j<3; j++) { s0[j]=0; s1[j]=0; cx[j]=0; }\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    functional_vecsub(mesh->dim, x[1], x[0], s0);\n    functional_vecsub(mesh->dim, x[2], x[1], s1);\n\n    functional_veccross(s0, s1, cx);\n    *out=0.5*functional_vecnorm(3, cx);\n    \n    return true;\n}\n\n/** Calculate scaled gradient */\nbool area_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale) {\n    double *x[nv], s0[3], s1[3], s01[3], s010[3], s011[3];\n    double norm;\n    for (int j=0; j<3; j++) { s0[j]=0; s1[j]=0; s01[j]=0; s010[j]=0; s011[j]=0; }\n    for (int j=0; j<nv; j++) if (!matrix_getcolumn(mesh->vert, vid[j], &x[j])) return false;\n\n    functional_vecsub(mesh->dim, x[1], x[0], s0);\n    functional_vecsub(mesh->dim, x[2], x[1], s1);\n\n    functional_veccross(s0, s1, s01);\n    norm=functional_vecnorm(3, s01);\n    if (norm<MORPHO_EPS) return false;\n\n    functional_veccross(s01, s0, s010);\n    functional_veccross(s01, s1, s011);\n\n    matrix_addtocolumn(frc, vid[0], 0.5/norm*scale, s011);\n    matrix_addtocolumn(frc, vid[2], 0.5/norm*scale, s010);\n\n    functional_vecadd(mesh->dim, s010, s011, s0);\n\n    matrix_addtocolumn(frc, vid[1], -0.5/norm*scale, s0);\n\n    return true;\n}\n\n/** Calculate gradient */\nbool area_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n    return area_gradient_scale(v, mesh, id, nv, vid, NULL, frc, 1.0);\n}\n\nFUNCTIONAL_INIT(Area, MESH_GRADE_AREA)\nFUNCTIONAL_INTEGRAND(Area, MESH_GRADE_AREA, area_integrand)\nFUNCTIONAL_INTEGRANDFORELEMENT(Area, MESH_GRADE_AREA, area_integrand)\nFUNCTIONAL_GRADIENT(Area, MESH_GRADE_AREA, area_gradient, SYMMETRY_ADD)\nFUNCTIONAL_TOTAL(Area, MESH_GRADE_AREA, area_integrand)\nFUNCTIONAL_HESSIAN(Area, MESH_GRADE_AREA, area_integrand)\n\nMORPHO_BEGINCLASS(Area)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, Area_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, Area_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRANDFORELEMENT_METHOD, Area_integrandForElement, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, Area_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, Area_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, Area_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Enclosed volume\n * ---------------------------------------------- */\n\n/** Calculate enclosed volume */\nbool volumeenclosed_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    double *x[nv], cx[mesh->dim];\n    for (int j=0; j<nv; j++) if (!matrix_getcolumn(mesh->vert, vid[j], &x[j])) return false;\n\n    functional_veccross(x[0], x[1], cx);\n\n    *out=fabs(functional_vecdot(mesh->dim, cx, x[2]))/6.0;\n    return true;\n}\n\n/** Calculate gradient */\nbool volumeenclosed_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n    double *x[nv], cx[mesh->dim], dot;\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    functional_veccross(x[0], x[1], cx);\n    dot=functional_vecdot(mesh->dim, cx, x[2]);\n    if (fabs(dot)<=DBL_MIN) {\n        morpho_runtimeerror(v, VOLUMEENCLOSED_ZERO);\n        return false;\n    }\n    \n    dot/=fabs(dot);\n\n    matrix_addtocolumn(frc, vid[2], dot/6.0, cx);\n\n    functional_veccross(x[1], x[2], cx);\n    matrix_addtocolumn(frc, vid[0], dot/6.0, cx);\n\n    functional_veccross(x[2], x[0], cx);\n    matrix_addtocolumn(frc, vid[1], dot/6.0, cx);\n\n    return true;\n}\n\nFUNCTIONAL_INIT(VolumeEnclosed, MESH_GRADE_AREA)\nFUNCTIONAL_INTEGRAND(VolumeEnclosed, MESH_GRADE_AREA, volumeenclosed_integrand)\nFUNCTIONAL_GRADIENT(VolumeEnclosed, MESH_GRADE_AREA, volumeenclosed_gradient, SYMMETRY_ADD)\nFUNCTIONAL_TOTAL(VolumeEnclosed, MESH_GRADE_AREA, volumeenclosed_integrand)\nFUNCTIONAL_HESSIAN(VolumeEnclosed, MESH_GRADE_AREA, volumeenclosed_integrand)\n\nMORPHO_BEGINCLASS(VolumeEnclosed)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, VolumeEnclosed_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, VolumeEnclosed_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, VolumeEnclosed_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, VolumeEnclosed_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, VolumeEnclosed_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Volume\n * ---------------------------------------------- */\n\n/** Calculate enclosed volume */\nbool volume_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    double *x[nv], s10[mesh->dim], s20[mesh->dim], s30[mesh->dim], cx[mesh->dim];\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    functional_vecsub(mesh->dim, x[1], x[0], s10);\n    functional_vecsub(mesh->dim, x[2], x[0], s20);\n    functional_vecsub(mesh->dim, x[3], x[0], s30);\n\n    functional_veccross(s20, s30, cx);\n\n    *out=fabs(functional_vecdot(mesh->dim, s10, cx))/6.0;\n    return true;\n}\n\n/** Calculate scaled gradient */\nbool volume_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale) {\n    double *x[nv], s10[mesh->dim], s20[mesh->dim], s30[mesh->dim];\n    double s31[mesh->dim], s21[mesh->dim], cx[mesh->dim], uu;\n    for (int j=0; j<nv; j++) matrix_getcolumn(mesh->vert, vid[j], &x[j]);\n\n    functional_vecsub(mesh->dim, x[1], x[0], s10);\n    functional_vecsub(mesh->dim, x[2], x[0], s20);\n    functional_vecsub(mesh->dim, x[3], x[0], s30);\n    functional_vecsub(mesh->dim, x[3], x[1], s31);\n    functional_vecsub(mesh->dim, x[2], x[1], s21);\n\n    functional_veccross(s20, s30, cx);\n    uu=functional_vecdot(mesh->dim, s10, cx);\n    uu=(uu>0 ? 1.0 : -1.0);\n\n    matrix_addtocolumn(frc, vid[1], uu/6.0*scale, cx);\n\n    functional_veccross(s31, s21, cx);\n    matrix_addtocolumn(frc, vid[0], uu/6.0*scale, cx);\n\n    functional_veccross(s30, s10, cx);\n    matrix_addtocolumn(frc, vid[2], uu/6.0*scale, cx);\n\n    functional_veccross(s10, s20, cx);\n    matrix_addtocolumn(frc, vid[3], uu/6.0*scale, cx);\n\n    return true;\n}\n\n/** Calculate gradient */\nbool volume_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n    return volume_gradient_scale(v, mesh, id, nv, vid, NULL, frc, 1.0);\n}\n\nFUNCTIONAL_INIT(Volume, MESH_GRADE_VOLUME)\nFUNCTIONAL_INTEGRAND(Volume, MESH_GRADE_VOLUME, volume_integrand)\nFUNCTIONAL_GRADIENT(Volume, MESH_GRADE_VOLUME, volume_gradient, SYMMETRY_ADD)\nFUNCTIONAL_TOTAL(Volume, MESH_GRADE_VOLUME, volume_integrand)\nFUNCTIONAL_HESSIAN(Volume, MESH_GRADE_VOLUME, volume_integrand)\n\nMORPHO_BEGINCLASS(Volume)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, Volume_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, Volume_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, Volume_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, Volume_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, Volume_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Scalar potential\n * ---------------------------------------------- */\n\nstatic value scalarpotential_functionproperty;\nstatic value scalarpotential_gradfunctionproperty;\n\ntypedef struct {\n    value fn;\n} scalarpotentialref;\n\nbool scalarpotential_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, scalarpotentialref *ref) {\n    ref->fn=MORPHO_NIL;\n    return (objectinstance_getpropertyinterned(self, scalarpotential_functionproperty, &ref->fn) &&\n            MORPHO_ISCALLABLE(ref->fn));\n}\n\n/** Evaluate the scalar potential */\nbool scalarpotential_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    double *x;\n    value fn = ((scalarpotentialref *) ref)->fn;\n    value args[mesh->dim];\n    value ret;\n\n    matrix_getcolumn(mesh->vert, id, &x);\n    for (int i=0; i<mesh->dim; i++) args[i]=MORPHO_FLOAT(x[i]);\n\n    if (morpho_call(v, fn, mesh->dim, args, &ret)) {\n        return morpho_valuetofloat(ret, out);\n    }\n\n    return false;\n}\n\n/** Evaluate the gradient of the scalar potential */\nbool scalarpotential_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n    double *x;\n    value fn = *(value *) ref;\n    value args[mesh->dim];\n    value ret;\n\n    matrix_getcolumn(mesh->vert, id, &x);\n    for (int i=0; i<mesh->dim; i++) args[i]=MORPHO_FLOAT(x[i]);\n\n    if (morpho_call(v, fn, mesh->dim, args, &ret)) {\n        if (MORPHO_ISMATRIX(ret)) {\n            objectmatrix *vf=MORPHO_GETMATRIX(ret);\n\n            if (vf->nrows*vf->ncols==frc->nrows) {\n                return matrix_addtocolumn(frc, id, 1.0, vf->elements);\n            }\n        }\n    }\n\n    return false;\n}\n\n/** Initialize a scalar potential */\nvalue ScalarPotential_init(vm *v, int nargs, value *args) {\n    objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), functional_gradeproperty, MORPHO_INTEGER(MESH_GRADE_VERTEX));\n\n    /* First argument is the potential function */\n    if (nargs>0) {\n        if (MORPHO_ISCALLABLE(MORPHO_GETARG(args, 0))) {\n            objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_functionproperty, MORPHO_GETARG(args, 0));\n        } else morpho_runtimeerror(v, SCALARPOTENTIAL_FNCLLBL);\n    }\n    /* Second argument is the gradient of the potential function */\n    if (nargs>1) {\n        if (MORPHO_ISCALLABLE(MORPHO_GETARG(args, 1))) {\n            objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_gradfunctionproperty, MORPHO_GETARG(args, 1));\n        } else morpho_runtimeerror(v, SCALARPOTENTIAL_FNCLLBL);\n    }\n\n    return MORPHO_NIL;\n}\n\nFUNCTIONAL_METHOD(ScalarPotential, integrand, MESH_GRADE_VERTEX, scalarpotentialref, scalarpotential_prepareref, functional_mapintegrand, scalarpotential_integrand, NULL, SCALARPOTENTIAL_FNCLLBL, SYMMETRY_NONE)\n\nFUNCTIONAL_METHOD(ScalarPotential, total, MESH_GRADE_VERTEX, scalarpotentialref, scalarpotential_prepareref, functional_sumintegrand, scalarpotential_integrand, NULL, SCALARPOTENTIAL_FNCLLBL, SYMMETRY_NONE)\n\nFUNCTIONAL_METHOD(ScalarPotential, hessian, MESH_GRADE_VERTEX, scalarpotentialref, scalarpotential_prepareref, functional_mapnumericalhessian, scalarpotential_integrand, NULL, SCALARPOTENTIAL_FNCLLBL, SYMMETRY_NONE)\n\n/** Evaluate a gradient */\nvalue ScalarPotential_gradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        value fn;\n        // Check if a gradient function is available\n        if (objectinstance_getpropertyinterned(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_gradfunctionproperty, &fn)) {\n            info.g = MESH_GRADE_VERTEX;\n            info.grad = scalarpotential_gradient;\n            info.ref = &fn;\n            if (MORPHO_ISCALLABLE(fn)) {\n                functional_mapgradient(v, &info, &out);\n            } else morpho_runtimeerror(v, SCALARPOTENTIAL_FNCLLBL);\n        } else if (objectinstance_getpropertyinterned(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_functionproperty, &fn)) {\n            // Otherwise try to use the regular scalar function\n\n            value fn;\n            if (objectinstance_getpropertyinterned(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_functionproperty, &fn)) {\n                info.g = MESH_GRADE_VERTEX;\n                info.integrand = scalarpotential_integrand;\n                info.ref = &fn;\n                if (MORPHO_ISCALLABLE(fn)) {\n                    functional_mapnumericalgradient(v, &info, &out);\n                } else morpho_runtimeerror(v, SCALARPOTENTIAL_FNCLLBL);\n            } else morpho_runtimeerror(v, VM_OBJECTLACKSPROPERTY, SCALARPOTENTIAL_FUNCTION_PROPERTY);\n\n        } else morpho_runtimeerror(v, VM_OBJECTLACKSPROPERTY, SCALARPOTENTIAL_FUNCTION_PROPERTY);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n\n    return out;\n}\n\nMORPHO_BEGINCLASS(ScalarPotential)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, ScalarPotential_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, ScalarPotential_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, ScalarPotential_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, ScalarPotential_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, ScalarPotential_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Linear Elasticity\n * ---------------------------------------------- */\n\nstatic value linearelasticity_referenceproperty;\nstatic value linearelasticity_weightbyreferenceproperty;\nstatic value linearelasticity_poissonproperty;\n\ntypedef struct {\n    objectmesh *refmesh;\n    grade grade;\n    double lambda; // Lamé coefficients\n    double mu;     //\n} linearelasticityref;\n\n/** Calculates the Gram matrix */\nvoid linearelasticity_calculategram(objectmatrix *vert, int dim, int nv, int *vid, objectmatrix *gram) {\n    int gdim=nv-1; // Dimension of Gram matrix\n    double *x[nv], // Positions of vertices\n            s[gdim][nv]; // Side vectors\n\n    for (int j=0; j<nv; j++) matrix_getcolumn(vert, vid[j], &x[j]); // Get vertices\n    for (int j=1; j<nv; j++) functional_vecsub(dim, x[j], x[0], s[j-1]); // u_i = X_i - X_0\n    // <u_i, u_j>\n    for (int i=0; i<nv-1; i++) for (int j=0; j<nv-1; j++) gram->elements[i+j*gdim]=functional_vecdot(dim, s[i], s[j]);\n}\n\n/** Calculate the linear elastic energy */\nbool linearelasticity_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    double weight=0.0;\n    linearelasticityref *info = (linearelasticityref *) ref;\n    int gdim=nv-1; // Dimension of Gram matrix\n\n    /* Construct static matrices */\n    double gramrefel[gdim*gdim], gramdefel[gdim*gdim], qel[gdim*gdim], rel[gdim*gdim], cgel[gdim*gdim];\n    objectmatrix gramref = MORPHO_STATICMATRIX(gramrefel, gdim, gdim); // Gram matrices\n    objectmatrix gramdef = MORPHO_STATICMATRIX(gramdefel, gdim, gdim); //\n    objectmatrix q = MORPHO_STATICMATRIX(qel, gdim, gdim); // Inverse of Gram in source domain\n    objectmatrix r = MORPHO_STATICMATRIX(rel, gdim, gdim); // Intermediate calculations\n    objectmatrix cg = MORPHO_STATICMATRIX(cgel, gdim, gdim); // Cauchy-Green strain tensor\n\n    linearelasticity_calculategram(info->refmesh->vert, mesh->dim, nv, vid, &gramref);\n    linearelasticity_calculategram(mesh->vert, mesh->dim, nv, vid, &gramdef);\n\n    if (matrix_inverse(&gramref, &q)!=MATRIX_OK) return false;\n    if (matrix_mul(&gramdef, &q, &r)!=MATRIX_OK) return false;\n\n    matrix_identity(&cg);\n    matrix_scale(&cg, -0.5);\n    matrix_accumulate(&cg, 0.5, &r);\n\n    double trcg=0.0, trcgcg=0.0;\n    matrix_trace(&cg, &trcg);\n\n    matrix_mul(&cg, &cg, &r);\n    matrix_trace(&r, &trcgcg);\n\n    if (!functional_elementsize(v, info->refmesh, info->grade, id, nv, vid, &weight)) return false;\n\n    *out=weight*(info->mu*trcgcg + 0.5*info->lambda*trcg*trcg);\n\n    return true;\n}\n\n/** Prepares the reference structure from the LinearElasticity object's properties */\nbool linearelasticity_prepareref(objectinstance *self, linearelasticityref *ref) {\n    bool success=false;\n    value refmesh=MORPHO_NIL;\n    value grade=MORPHO_NIL;\n    value poisson=MORPHO_NIL;\n\n    if (objectinstance_getpropertyinterned(self, linearelasticity_referenceproperty, &refmesh) &&\n        objectinstance_getpropertyinterned(self, functional_gradeproperty, &grade) &&\n        MORPHO_ISINTEGER(grade) &&\n        objectinstance_getpropertyinterned(self, linearelasticity_poissonproperty, &poisson) &&\n        MORPHO_ISNUMBER(poisson)) {\n        ref->refmesh=MORPHO_GETMESH(refmesh);\n        ref->grade=MORPHO_GETINTEGERVALUE(grade);\n\n        double nu = MORPHO_GETFLOATVALUE(poisson);\n\n        ref->mu=0.5/(1+nu);\n        ref->lambda=nu/(1+nu)/(1-2*nu);\n        success=true;\n    }\n    return success;\n}\n\nvalue LinearElasticity_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n    /* First argument is the reference mesh */\n    if (nargs>0) {\n        if (MORPHO_ISMESH(MORPHO_GETARG(args, 0))) {\n            objectinstance_setproperty(self, linearelasticity_referenceproperty, MORPHO_GETARG(args, 0));\n            objectmesh *mesh = MORPHO_GETMESH(MORPHO_GETARG(args, 0));\n\n            objectinstance_setproperty(self, functional_gradeproperty, MORPHO_INTEGER(mesh_maxgrade(mesh)));\n            objectinstance_setproperty(self, linearelasticity_poissonproperty, MORPHO_FLOAT(0.3));\n        } else morpho_runtimeerror(v, LINEARELASTICITY_REF);\n    } else morpho_runtimeerror(v, LINEARELASTICITY_REF);\n\n    /* Second (optional) argument is the grade to act on */\n    if (nargs>1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) {\n            objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), functional_gradeproperty, MORPHO_GETARG(args, 1));\n        }\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Integrand function */\nvalue LinearElasticity_integrand(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    linearelasticityref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (linearelasticity_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), &ref)) {\n            info.g = ref.grade;\n            info.integrand = linearelasticity_integrand;\n            info.ref = &ref;\n            functional_mapintegrand(v, &info, &out);\n        } else morpho_runtimeerror(v, LINEARELASTICITY_PRP);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\n/** Total function */\nvalue LinearElasticity_total(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    linearelasticityref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (linearelasticity_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), &ref)) {\n            info.g = ref.grade;\n            info.integrand = linearelasticity_integrand;\n            info.ref = &ref;\n            functional_sumintegrand(v, &info, &out);\n        } else morpho_runtimeerror(v, LINEARELASTICITY_PRP);\n    }\n    return out;\n}\n\n/** Integrand function */\nvalue LinearElasticity_gradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    linearelasticityref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (linearelasticity_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), &ref)) {\n            info.g = ref.grade;\n            info.integrand = linearelasticity_integrand;\n            info.ref = &ref;\n            info.sym = SYMMETRY_ADD;\n            functional_mapnumericalgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, LINEARELASTICITY_PRP);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(LinearElasticity)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LinearElasticity_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, LinearElasticity_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LinearElasticity_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, LinearElasticity_gradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n* Hydrogel\n* ---------------------------------------------- */\n\nstatic value hydrogel_aproperty;\nstatic value hydrogel_bproperty;\nstatic value hydrogel_cproperty;\nstatic value hydrogel_dproperty;\nstatic value hydrogel_phirefproperty;\nstatic value hydrogel_phi0property;\n\ntypedef struct {\n    objectmesh *refmesh;\n    grade grade;\n    double a, b, c, d, phiref; // Hydrogel coefficients\n    value phi0; // Can be a number or a field. (Ensuring flexibility for supplying a phi0 field in the future)\n} hydrogelref;\n\n/** Prepares the reference structure from the object's properties */\nbool hydrogel_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, hydrogelref *ref) {\n    bool success=false;\n    value refmesh=MORPHO_NIL, grade=MORPHO_NIL, phi0=MORPHO_NIL;\n    value a=MORPHO_NIL, b=MORPHO_NIL, c=MORPHO_NIL, d=MORPHO_NIL, phiref=MORPHO_NIL;\n\n    if (objectinstance_getpropertyinterned(self, linearelasticity_referenceproperty, &refmesh) &&\n        objectinstance_getpropertyinterned(self, functional_gradeproperty, &grade) &&\n        MORPHO_ISINTEGER(grade) &&\n        objectinstance_getpropertyinterned(self, hydrogel_aproperty, &a) &&\n        MORPHO_ISNUMBER(a) &&\n        objectinstance_getpropertyinterned(self, hydrogel_bproperty, &b) &&\n        MORPHO_ISNUMBER(b) &&\n        objectinstance_getpropertyinterned(self, hydrogel_cproperty, &c) &&\n        MORPHO_ISNUMBER(c) &&\n        objectinstance_getpropertyinterned(self, hydrogel_dproperty, &d) &&\n        MORPHO_ISNUMBER(d) &&\n        objectinstance_getpropertyinterned(self, hydrogel_phirefproperty, &phiref) &&\n        MORPHO_ISNUMBER(phiref) &&\n        objectinstance_getpropertyinterned(self, hydrogel_phi0property, &phi0) &&\n        (MORPHO_ISNUMBER(phi0) || MORPHO_ISFIELD(phi0))) {\n        ref->refmesh=MORPHO_GETMESH(refmesh);\n        ref->grade=MORPHO_GETINTEGERVALUE(grade);\n\n        if (ref->grade<0) ref->grade=mesh_maxgrade(mesh);\n\n        if (morpho_valuetofloat(a, &ref->a) &&\n            morpho_valuetofloat(b, &ref->b) &&\n            morpho_valuetofloat(c, &ref->c) &&\n            morpho_valuetofloat(d, &ref->d) &&\n            morpho_valuetofloat(phiref, &ref->phiref)) {\n            ref->phi0 = phi0;\n            success=true;\n        }\n    }\n    return success;\n}\n\n/** Calculate the Hydrogel energy */\nbool hydrogel_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    hydrogelref *info = (hydrogelref *) ref;\n    value vphi0 = info->phi0;\n    double V=0.0, V0=0.0, phi0=0.0;\n\n    if (!functional_elementsize(v, info->refmesh, info->grade, id, nv, vid, &V0)) return false;\n    if (!functional_elementsize(v, mesh, info->grade, id, nv, vid, &V)) return false;\n\n    if (V0<1e-8) {\n        morpho_runtimewarning(v, HYDROGEL_ZEEROREFELEMENT, id, V, V0);\n    }\n\n    if (fabs(V)<MORPHO_EPS) return false;\n\n    // Determine phi0 either as a number or by looking up something in a field\n    if (MORPHO_ISFIELD(info->phi0)) {\n        objectfield *p = MORPHO_GETFIELD(info->phi0);\n        if (!field_getelement(p, info->grade, id, 0, &vphi0)) {\n            morpho_runtimeerror(v, HYDROGEL_FLDGRD, (unsigned int) info->grade);\n            return false;\n        }\n    }\n    if (MORPHO_ISNUMBER(vphi0)) {\n        if (!morpho_valuetofloat(vphi0, &phi0)) return false;\n    }\n\n    double phi = phi0/(V/V0);\n    double pr = info->phiref;\n    if (phi<0 || 1-phi<0) {\n        morpho_runtimewarning(v, HYDROGEL_BNDS, id, V, V0, phi, 1-phi);\n    }\n\n    if (phi>1-MORPHO_EPS) phi = 1-MORPHO_EPS;\n    if (phi<MORPHO_EPS) phi = MORPHO_EPS;\n\n    *out = (info->a * phi*log(phi) +\n            info->b * (1-phi)*log(1-phi) +\n            info->c * phi*(1-phi))*V +\n            info->d * (log(pr/phi)/3.0 - pow((pr/phi), (2.0/3)) + 1.0)*V0;\n\n    if (phi<0 || 1-phi<0) return false;\n\n    return true;\n}\n\n/** Calculate gradient */\nbool hydrogel_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) {\n\n    hydrogelref *info = (hydrogelref *) ref;\n    value vphi0 = info->phi0;\n    double V=0.0, V0=0.0, phi0=0.0;\n\n    if (!functional_elementsize(v, info->refmesh, info->grade, id, nv, vid, &V0)) return false;\n    if (!functional_elementsize(v, mesh, info->grade, id, nv, vid, &V)) return false;\n\n    if (V0<1e-8) {\n        morpho_runtimewarning(v, HYDROGEL_ZEEROREFELEMENT, id, V, V0);\n    }\n\n    if (fabs(V)<MORPHO_EPS) return false;\n\n    // Determine phi0 either as a number or by looking up something in a field\n    if (MORPHO_ISFIELD(info->phi0)) {\n        objectfield *p = MORPHO_GETFIELD(info->phi0);\n        if (!field_getelement(p, info->grade, id, 0, &vphi0)) {\n            morpho_runtimeerror(v, HYDROGEL_FLDGRD, (unsigned int) info->grade);\n            return false;\n        }\n    }\n    if (MORPHO_ISNUMBER(vphi0)) {\n        if (!morpho_valuetofloat(vphi0, &phi0)) return false;\n    }\n\n    double phi = phi0/(V/V0);\n    double pr = info->phiref;\n    if (phi<0 || 1-phi<0) {\n        morpho_runtimewarning(v, HYDROGEL_BNDS, id, V, V0, phi, 1-phi);\n    }\n\n    if (phi>1-MORPHO_EPS) phi = 1-MORPHO_EPS;\n    if (phi<MORPHO_EPS) phi = MORPHO_EPS;\n\n    double grad = (-info->a * phi +\n            info->b * ( phi + log(1-phi) ) +\n            info->c * phi*phi +\n            info->d * (pr/phi0) * ((phi/pr)/3.0 - (2.0/3) * pow((phi/pr), (1.0/3)) ) );\n\n    // Compute grad * element gradient\n    if (!functional_elementgradient_scale(v, mesh, info->grade, id, nv, vid, frc, grad)) return false;\n\n    return true;\n}\n\n/** Evaluate a gradient */\nvalue Hydrogel_gradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    value out=MORPHO_NIL;\n    hydrogelref ref;\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (hydrogel_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, -1, info.sel, &ref)) {\n            info.g = ref.grade;\n            info.grad = hydrogel_gradient;\n            info.ref = &ref;\n            info.sym = SYMMETRY_ADD;\n            functional_mapgradient(v, &info, &out);\n        }\n    }\n\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n\n    return out;\n}\n\n\nvalue Hydrogel_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n    int nfixed;\n    value grade=MORPHO_INTEGER(-1);\n    value a=MORPHO_NIL, b=MORPHO_NIL, c=MORPHO_NIL, d=MORPHO_NIL, phiref=MORPHO_NIL, phi0=MORPHO_NIL;\n\n    if (builtin_options(v, nargs, args, &nfixed, 6,\n                        hydrogel_aproperty, &a,\n                        hydrogel_bproperty, &b,\n                        hydrogel_cproperty, &c,\n                        hydrogel_dproperty, &d,\n                        hydrogel_phirefproperty, &phiref,\n                        hydrogel_phi0property, &phi0,\n                        functional_gradeproperty, &grade)) {\n\n        objectinstance_setproperty(self, hydrogel_aproperty, a);\n        objectinstance_setproperty(self, hydrogel_bproperty, b);\n        objectinstance_setproperty(self, hydrogel_cproperty, c);\n        objectinstance_setproperty(self, hydrogel_dproperty, d);\n        objectinstance_setproperty(self, hydrogel_phirefproperty, phiref);\n        objectinstance_setproperty(self, hydrogel_phi0property, phi0);\n        objectinstance_setproperty(self, functional_gradeproperty, grade);\n\n        if (nfixed==1 && MORPHO_ISMESH(MORPHO_GETARG(args, 0))) {\n            objectinstance_setproperty(self, linearelasticity_referenceproperty, MORPHO_GETARG(args, 0));\n        } else morpho_runtimeerror(v, HYDROGEL_ARGS);\n    } else morpho_runtimeerror(v, HYDROGEL_ARGS);\n\n    return MORPHO_NIL;\n}\n\nFUNCTIONAL_METHOD(Hydrogel, integrand, (ref.grade), hydrogelref, hydrogel_prepareref, functional_mapintegrand, hydrogel_integrand, NULL, HYDROGEL_PRP, SYMMETRY_NONE)\n\nFUNCTIONAL_METHOD(Hydrogel, total, (ref.grade), hydrogelref, hydrogel_prepareref, functional_sumintegrand, hydrogel_integrand, NULL, HYDROGEL_PRP, SYMMETRY_NONE)\n\nMORPHO_BEGINCLASS(Hydrogel)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, Hydrogel_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, Hydrogel_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, Hydrogel_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, Hydrogel_gradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Equielement\n * ---------------------------------------------- */\n\nstatic value equielement_weightproperty;\n\ntypedef struct {\n    grade grade;\n    objectsparse *vtoel; // Connect vertices to elements\n    objectsparse *eltov; // Connect elements to vertices\n    objectmatrix *weight; // Weight field\n    double mean;\n} equielementref;\n\n/** Prepares the reference structure from the Equielement object's properties */\nbool equielement_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, equielementref *ref) {\n    bool success=false;\n    value grade=MORPHO_NIL;\n    value weight=MORPHO_NIL;\n\n    if (objectinstance_getpropertyinterned(self, functional_gradeproperty, &grade) &&\n        MORPHO_ISINTEGER(grade) ) {\n        ref->grade=MORPHO_GETINTEGERVALUE(grade);\n        ref->weight=NULL;\n\n        int maxgrade=mesh_maxgrade(mesh);\n        if (ref->grade<0 || ref->grade>maxgrade) ref->grade = maxgrade;\n\n        ref->vtoel=mesh_addconnectivityelement(mesh, ref->grade, 0);\n        ref->eltov=mesh_addconnectivityelement(mesh, 0, ref->grade);\n\n        if (ref->vtoel && ref->eltov) success=true;\n    }\n\n    if (objectinstance_getpropertyinterned(self, equielement_weightproperty, &weight) &&\n        MORPHO_ISMATRIX(weight) ) {\n        ref->weight=MORPHO_GETMATRIX(weight);\n        if (ref->weight) {\n            ref->mean=matrix_sum(ref->weight);\n            ref->mean/=ref->weight->ncols;\n        }\n    }\n\n    return success;\n}\n\n\nbool equielement_contains(varray_elementid *nbrs, elementid id) {\n    for (unsigned int i=0; i<nbrs->count; i++) {\n        if (nbrs->data[i]==id) return true;\n    }\n    return false;\n}\n\n/** Finds the points that a point depends on  */\nbool equielement_dependencies(functional_mapinfo *info, elementid id, varray_elementid *out) {\n    objectmesh *mesh = info->mesh;\n    equielementref *eref = info->ref;\n    bool success=false;\n    varray_elementid nbrs;\n    varray_elementidinit(&nbrs);\n\n    // varray_elementidwrite(out, id); // EquiElement is a vertex element, and hence depends on itself\n    \n    if (mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, eref->grade, &nbrs)>0) {\n        for (unsigned int i=0; i<nbrs.count; i++) {\n            int nentries, *entries; // Get the vertices for this element\n            if (!sparseccs_getrowindices(&eref->eltov->ccs, nbrs.data[i], &nentries, &entries)) goto equieleement_dependencies_cleanup;\n\n            for (unsigned int j=0; j<nentries; j++) {\n                if (entries[j]==id) continue;\n                if (equielement_contains(out, entries[j])) continue;\n                varray_elementidwrite(out, entries[j]);\n            }\n        }\n    }\n    success=true;\n\nequieleement_dependencies_cleanup:\n    varray_elementidclear(&nbrs);\n\n    return success;\n}\n\n/** Calculate the equielement energy */\nbool equielement_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *r, double *out) {\n    equielementref *ref = (equielementref *) r;\n    int nconn, *conn;\n\n    if (sparseccs_getrowindices(&ref->vtoel->ccs, id, &nconn, &conn)) {\n        if (nconn==1) { *out = 0; return true; }\n\n        double size[nconn], mean=0.0, total=0.0;\n\n        for (int i=0; i<nconn; i++) {\n            int nv, *vid;\n            sparseccs_getrowindices(&ref->eltov->ccs, conn[i], &nv, &vid);\n            functional_elementsize(v, mesh, ref->grade, conn[i], nv, vid, &size[i]);\n            mean+=size[i];\n        }\n\n        mean /= ((double) nconn);\n\n        if (fabs(mean)<MORPHO_EPS) return false;\n\n        /* Now evaluate the functional at this vertex */\n        if (!ref->weight || fabs(ref->mean)<MORPHO_EPS) {\n            for (unsigned int i=0; i<nconn; i++) total+=(1.0-size[i]/mean)*(1.0-size[i]/mean);\n        } else {\n            double weight[nconn], wmean=0.0;\n\n            for (int i=0; i<nconn; i++) {\n                weight[i]=1.0;\n                matrix_getelement(ref->weight, 0, conn[i], &weight[i]);\n                wmean+=weight[i];\n            }\n\n            wmean /= ((double) nconn);\n            if (fabs(wmean)<MORPHO_EPS) wmean = 1.0;\n\n            for (unsigned int i=0; i<nconn; i++) {\n                double term = (1.0-weight[i]*size[i]/mean/wmean);\n                total+=term*term;\n            }\n        }\n\n        *out = total;\n    }\n\n    return true;\n}\n\nvalue EquiElement_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n    int nfixed;\n    value grade=MORPHO_INTEGER(-1);\n    value weight=MORPHO_NIL;\n\n    if (builtin_options(v, nargs, args, &nfixed, 2, equielement_weightproperty, &weight, functional_gradeproperty, &grade)) {\n        objectinstance_setproperty(self, equielement_weightproperty, weight);\n        objectinstance_setproperty(self, functional_gradeproperty, grade);\n    } else morpho_runtimeerror(v, EQUIELEMENT_ARGS);\n\n    return MORPHO_NIL;\n}\n\nFUNCTIONAL_METHOD(EquiElement, integrand, MESH_GRADE_VERTEX, equielementref, equielement_prepareref, functional_mapintegrand, equielement_integrand, NULL, EQUIELEMENT_ARGS, SYMMETRY_NONE)\n\nFUNCTIONAL_METHOD(EquiElement, total, MESH_GRADE_VERTEX, equielementref, equielement_prepareref, functional_sumintegrand, equielement_integrand, NULL, EQUIELEMENT_ARGS, SYMMETRY_NONE)\n\nFUNCTIONAL_METHOD(EquiElement, gradient, MESH_GRADE_VERTEX, equielementref, equielement_prepareref, functional_mapnumericalgradient, equielement_integrand, equielement_dependencies, EQUIELEMENT_ARGS, SYMMETRY_ADD)\n\nFUNCTIONAL_METHOD(EquiElement, hessian, MESH_GRADE_VERTEX, equielementref, equielement_prepareref, functional_mapnumericalhessian, equielement_integrand, equielement_dependencies, EQUIELEMENT_ARGS, SYMMETRY_ADD)\n\nMORPHO_BEGINCLASS(EquiElement)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, EquiElement_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, EquiElement_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, EquiElement_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, EquiElement_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, EquiElement_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Curvatures\n * ********************************************************************** */\n\n/* ----------------------------------------------\n * LineCurvatureSq\n * ---------------------------------------------- */\n\nstatic value curvature_integrandonlyproperty;\n\ntypedef struct {\n    objectsparse *lineel; // Lines\n    objectselection *selection; // Selection\n    bool integrandonly; // Output integrated curvature or 'bare' curvature.\n} curvatureref;\n\nbool curvature_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, curvatureref *ref) {\n    bool success = true;\n\n    ref->selection=sel;\n\n    ref->lineel = mesh_getconnectivityelement(mesh, MESH_GRADE_VERTEX, MESH_GRADE_LINE);\n    if (ref->lineel) success=sparse_checkformat(ref->lineel, SPARSE_CCS, true, false);\n\n    if (success) {\n        objectsparse *s = mesh_getconnectivityelement(mesh, MESH_GRADE_LINE, MESH_GRADE_VERTEX);\n        if (!s) s=mesh_addconnectivityelement(mesh, MESH_GRADE_LINE, MESH_GRADE_VERTEX);\n        success=s;\n    }\n\n    if (success) {\n        value integrandonly=MORPHO_FALSE;\n        objectinstance_getpropertyinterned(self, curvature_integrandonlyproperty, &integrandonly);\n        ref->integrandonly=MORPHO_ISTRUE(integrandonly);\n    }\n\n    return success;\n}\n\n/** Finds the points that a point depends on  */\nbool linecurvsq_dependencies(functional_mapinfo *info, elementid id, varray_elementid *out) {\n    objectmesh *mesh = info->mesh;\n    curvatureref *cref = info->ref;\n    bool success=false;\n    varray_elementid nbrs;\n    varray_elementidinit(&nbrs);\n\n    varray_elementidwrite(out, id); // LinecurvSq is a vertex element, and hence depends on itself\n    \n    if (mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, MESH_GRADE_LINE, &nbrs)>0) {\n        for (unsigned int i=0; i<nbrs.count; i++) {\n            int nentries, *entries; // Get the vertices for this edge\n            if (!sparseccs_getrowindices(&cref->lineel->ccs, nbrs.data[i], &nentries, &entries)) goto linecurvsq_dependencies_cleanup;\n            for (unsigned int j=0; j<nentries; j++) {\n                if (entries[j]==id) continue;\n                varray_elementidwrite(out, entries[j]);\n            }\n        }\n    }\n    success=true;\n\nlinecurvsq_dependencies_cleanup:\n    varray_elementidclear(&nbrs);\n\n    return success;\n}\n\n/** Calculate the integral of the curvature squared  */\nbool linecurvsq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    curvatureref *cref = (curvatureref *) ref;\n    double result = 0.0;\n    varray_elementid nbrs;\n    varray_elementid synid;\n    varray_elementidinit(&nbrs);\n    varray_elementidinit(&synid);\n\n    double s0[mesh->dim], s1[mesh->dim], *s[2] = { s0, s1}, sgn=-1.0;\n\n    if (mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, MESH_GRADE_LINE, &nbrs)>0 &&\n        mesh_getsynonyms(mesh, MESH_GRADE_VERTEX, id, &synid)) {\n        if (nbrs.count!=2) goto linecurvsq_integrand_cleanup;\n\n        for (unsigned int i=0; i<2; i++) {\n            int nentries, *entries; // Get the vertices for this edge\n            if (!sparseccs_getrowindices(&cref->lineel->ccs, nbrs.data[i], &nentries, &entries)) break;\n\n            double *x0, *x1;\n            if (mesh_getvertexcoordinatesaslist(mesh, entries[0], &x0) &&\n                mesh_getvertexcoordinatesaslist(mesh, entries[1], &x1)) {\n                functional_vecsub(mesh->dim, x0, x1, s[i]);\n            }\n            if (!(entries[0]==id || functional_inlist(&synid, entries[0]))) sgn*=-1;\n        }\n\n        double s0s0=functional_vecdot(mesh->dim, s0, s0),\n               s0s1=functional_vecdot(mesh->dim, s0, s1),\n               s1s1=functional_vecdot(mesh->dim, s1, s1);\n\n        s0s0=sqrt(s0s0); s1s1=sqrt(s1s1);\n\n        if (s0s0<MORPHO_EPS || s1s1<MORPHO_EPS) return false;\n\n        double u=sgn*s0s1/s0s0/s1s1,\n               len=0.5*(s0s0+s1s1);\n\n        if (u<1) u=acos(u); else u=0;\n\n        result = u*u/len;\n        if (cref->integrandonly) result /= len; // Get the bare curvature.\n    }\n\nlinecurvsq_integrand_cleanup:\n\n    *out = result;\n    varray_elementidclear(&nbrs);\n    varray_elementidclear(&synid);\n\n    return true;\n}\n\nFUNCTIONAL_INIT(LineCurvatureSq, MESH_GRADE_VERTEX)\nFUNCTIONAL_METHOD(LineCurvatureSq, integrand, MESH_GRADE_VERTEX, curvatureref, curvature_prepareref, functional_mapintegrand, linecurvsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(LineCurvatureSq, integrandForElement, MESH_GRADE_VERTEX, curvatureref, curvature_prepareref, functional_mapintegrandforelement, linecurvsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(LineCurvatureSq, total, MESH_GRADE_VERTEX, curvatureref, curvature_prepareref, functional_sumintegrand, linecurvsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(LineCurvatureSq, gradient, MESH_GRADE_VERTEX, curvatureref, curvature_prepareref, functional_mapnumericalgradient, linecurvsq_integrand, linecurvsq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD)\nFUNCTIONAL_METHOD(LineCurvatureSq, hessian, MESH_GRADE_VERTEX, curvatureref, curvature_prepareref, functional_mapnumericalhessian, linecurvsq_integrand, linecurvsq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD)\n\nMORPHO_BEGINCLASS(LineCurvatureSq)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineCurvatureSq_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, LineCurvatureSq_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRANDFORELEMENT_METHOD, LineCurvatureSq_integrandForElement, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, LineCurvatureSq_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LineCurvatureSq_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, LineCurvatureSq_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * LineTorsionSq\n * ---------------------------------------------- */\n\n/** Return a list of vertices that an element depends on  */\nbool linetorsionsq_dependencies(functional_mapinfo *info, elementid id, varray_elementid *out) {\n    objectmesh *mesh = info->mesh;\n    curvatureref *cref = info->ref;\n    bool success=false;\n    varray_elementid nbrs;\n    varray_elementid synid;\n\n    varray_elementidinit(&nbrs);\n    varray_elementidinit(&synid);\n\n    if (mesh_findneighbors(mesh, MESH_GRADE_LINE, id, MESH_GRADE_LINE, &nbrs)>0) {\n        for (unsigned int i=0; i<nbrs.count; i++) {\n            int nentries, *entries; // Get the vertices for this edge\n            if (!sparseccs_getrowindices(&cref->lineel->ccs, nbrs.data[i], &nentries, &entries)) goto linetorsionsq_dependencies_cleanup;\n            for (unsigned int j=0; j<nentries; j++) {\n                varray_elementidwriteunique(out, entries[j]);\n            }\n        }\n    }\n    success=true;\n\nlinetorsionsq_dependencies_cleanup:\n    varray_elementidclear(&nbrs);\n    varray_elementidclear(&synid);\n\n    return success;\n}\n\n\n/** Calculate the integral of the torsion squared  */\nbool linetorsionsq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    curvatureref *cref = (curvatureref *) ref;\n    int tmpi; elementid tmpid;\n    bool success=false;\n\n    //double result = 0.0;\n    varray_elementid nbrs;\n    varray_elementid synid;\n    varray_elementidinit(&nbrs);\n    varray_elementidinit(&synid);\n    elementid vlist[6]; // List of vertices in order  n\n    int type[6];\n    for (unsigned int i=0; i<6; i++) type[i]=-1;\n\n    /* We want an ordered list of vertex indices:\n     *               v the element\n     *    0 --- 1/2 --- 3/4 --- 5\n     * Where 1/2 and 3/4 are the same vertex, but could have different indices due to symmetries */\n     vlist[2] = vid[0]; vlist[3] = vid[1]; // Copy the current element into place\n\n    /* First identify neighbors and get the vertex ids for each element */\n    if (mesh_findneighbors(mesh, MESH_GRADE_LINE, id, MESH_GRADE_LINE, &nbrs)>0) {\n        if (nbrs.count<2) {\n            *out = 0; success=true;\n            goto linecurvsq_torsion_cleanup;\n        }\n\n        for (unsigned int i=0; i<nbrs.count; i++) {\n            int nentries, *entries; // Get the vertices for this edge\n            if (!sparseccs_getrowindices(&cref->lineel->ccs, nbrs.data[i], &nentries, &entries)) goto linecurvsq_torsion_cleanup;\n            for (unsigned int j=0; j<nentries; j++) { // Copy the vertexids\n                vlist[4*i+j] = entries[j];\n            }\n        }\n    }\n\n    /* The vertex ids are not yet in the right order. Let's identify which vertex is which */\n    for (int i=0; i<2; i++) {\n        if (mesh_getsynonyms(mesh, 0, vid[i], &synid)) {\n            for (int j=0; j<6; j++) if (vlist[j]==vid[i] || functional_inlist(&synid, vlist[j])) type[j]=i;\n        }\n    }\n    /* The type array now contains either 0,1 depending on which vertex we have, or -1 if the vertex is not a synonym for the element's vertices */\n#define SWAP(var, i, j, tmp) { tmp=var[i]; var[i]=var[j]; var[j]=tmp; }\n    if (type[0]==1 || type[1]==1) { // Make sure the first segment corresponds to the first vertex\n        SWAP(vlist, 0, 4, tmpid); SWAP(vlist, 1, 5, tmpid);\n        SWAP(type, 0, 4, tmpi); SWAP(type, 1, 5, tmpi);\n    }\n\n    if (type[1]==-1) { // Check order of first segment\n        SWAP(vlist, 0, 1, tmpid);\n        SWAP(type, 0, 1, tmpi);\n    }\n\n    if (type[4]==-1) { // Check order of first segment\n        SWAP(vlist, 4, 5, tmpid);\n        SWAP(type, 4, 5, tmpi);\n    }\n#undef SWAP\n\n    /* We now have an ordered list of vertices.\n       Get the vertex positions */\n    double *x[6];\n    for (int i=0; i<6; i++) matrix_getcolumn(mesh->vert, vlist[i], &x[i]);\n\n    double A[3], B[3], C[3], crossAB[3], crossBC[3];\n    functional_vecsub(3, x[1], x[0], A);\n    functional_vecsub(3, x[3], x[2], B);\n    functional_vecsub(3, x[5], x[4], C);\n\n    functional_veccross(A, B, crossAB);\n    functional_veccross(B, C, crossBC);\n\n    double normB=functional_vecnorm(3, B),\n           normAB=functional_vecnorm(3, crossAB),\n           normBC=functional_vecnorm(3, crossBC);\n\n    double S = functional_vecdot(3, A, crossBC)*normB;\n    if (normAB>MORPHO_EPS) S/=normAB;\n    if (normBC>MORPHO_EPS) S/=normBC;\n\n    S=asin(S);\n    *out=S*S/normB;\n    success=true;\n\nlinecurvsq_torsion_cleanup:\n    varray_elementidclear(&nbrs);\n    varray_elementidclear(&synid);\n\n    return success;\n}\n\nFUNCTIONAL_INIT(LineTorsionSq, MESH_GRADE_LINE)\nFUNCTIONAL_METHOD(LineTorsionSq, integrand, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_mapintegrand, linetorsionsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(LineTorsionSq, total, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_sumintegrand, linetorsionsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(LineTorsionSq, gradient, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_mapnumericalgradient, linetorsionsq_integrand, linetorsionsq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD)\nFUNCTIONAL_METHOD(LineTorsionSq, hessian, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_mapnumericalhessian, linetorsionsq_integrand, linetorsionsq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD)\n\nMORPHO_BEGINCLASS(LineTorsionSq)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineTorsionSq_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, LineTorsionSq_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, LineTorsionSq_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LineTorsionSq_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, LineTorsionSq_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * MeanCurvatureSq\n * ---------------------------------------------- */\n\nstatic value curvature_geodesicproperty;\n\ntypedef struct {\n    objectsparse *areael; // Areas\n    objectselection *selection; // Selection\n    bool integrandonly; // Output integrated curvature or 'bare' curvature.\n    bool geodesic; // Compute the geodesic curvature instead of the Gauss curvature (see https://cuhkmath.wordpress.com/2016/06/21/the-discrete-gauss-bonnet-theorem/)\n} areacurvatureref;\n\nbool areacurvature_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, areacurvatureref *ref) {\n    bool success = true;\n\n    ref->selection=sel;\n\n    ref->areael = mesh_getconnectivityelement(mesh, MESH_GRADE_VERTEX, MESH_GRADE_AREA);\n    if (ref->areael) success=sparse_checkformat(ref->areael, SPARSE_CCS, true, false);\n\n    if (success) {\n        objectsparse *s = mesh_getconnectivityelement(mesh, MESH_GRADE_AREA, MESH_GRADE_VERTEX);\n        if (!s) s=mesh_addconnectivityelement(mesh, MESH_GRADE_AREA, MESH_GRADE_VERTEX);\n        success=s;\n    }\n\n    if (success) {\n        value integrandonly=MORPHO_FALSE;\n        objectinstance_getpropertyinterned(self, curvature_integrandonlyproperty, &integrandonly);\n        ref->integrandonly=MORPHO_ISTRUE(integrandonly);\n\n        value geodesic=MORPHO_FALSE;\n        objectinstance_getpropertyinterned(self, curvature_geodesicproperty, &geodesic);\n        ref->geodesic=MORPHO_ISTRUE(geodesic);\n    }\n\n    return success;\n}\n\n/** Return a list of vertices that an element depends on  */\nbool meancurvaturesq_dependencies(functional_mapinfo *info, elementid id, varray_elementid *out) {\n    objectmesh *mesh = info->mesh;\n    areacurvatureref *cref = info->ref;\n    bool success=false;\n    varray_elementid nbrs;\n    varray_elementid synid;\n\n    varray_elementidinit(&nbrs);\n    varray_elementidinit(&synid);\n\n    mesh_getsynonyms(mesh, MESH_GRADE_VERTEX, id, &synid);\n    varray_elementidwriteunique(&synid, id);\n\n    /* Loop over synonyms of the element id */\n    mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, MESH_GRADE_AREA, &nbrs);\n\n    for (unsigned int i=0; i<nbrs.count; i++) { /* Loop over adjacent triangles */\n        int nvert, *vids;\n        if (!sparseccs_getrowindices(&cref->areael->ccs, nbrs.data[i], &nvert, &vids)) goto meancurvsq_dependencies_cleanup;\n\n        for (unsigned int j=0; j<nvert; j++) {\n            if (vids[j]==id) continue;\n            varray_elementidwriteunique(out, vids[j]);\n        }\n    }\n    success=true;\n\nmeancurvsq_dependencies_cleanup:\n    varray_elementidclear(&nbrs);\n    varray_elementidclear(&synid);\n\n    return success;\n}\n\n/** Orders the vertices in the list vids so that the vertex in synid is first */\nbool curvature_ordervertices(varray_elementid *synid, int nv, int *vids) {\n    int posn=-1;\n    for (unsigned int i=0; i<nv && posn<0; i++) {\n        for (unsigned int k=0; k<synid->count; k++) if (synid->data[k]==vids[i]) { posn = i; break; }\n    }\n\n    if (posn>0) { // If the desired vertex isn't in first position, move it there.\n        int tmp=vids[posn];\n        vids[posn]=vids[0]; vids[0]=tmp;\n    }\n\n    return (posn>=0);\n}\n\n/** Calculate the integral of the mean curvature squared  */\nbool meancurvaturesq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    areacurvatureref *cref = (areacurvatureref *) ref;\n    double areasum = 0;\n    bool success=false;\n\n    varray_elementid nbrs;\n    varray_elementid synid;\n    varray_elementidinit(&nbrs);\n    varray_elementidinit(&synid);\n\n    mesh_getsynonyms(mesh, MESH_GRADE_VERTEX, id, &synid);\n    varray_elementidwriteunique(&synid, id);\n\n    double frc[mesh->dim]; // This will hold the total force due to the triangles present\n    for (unsigned int i=0; i<mesh->dim; i++) frc[i]=0.0;\n\n    mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, MESH_GRADE_AREA, &nbrs);\n\n    for (unsigned int i=0; i<nbrs.count; i++) { /* Loop over adjacent triangles */\n        int nvert, *ovids;\n        if (!sparseccs_getrowindices(&cref->areael->ccs, nbrs.data[i], &nvert, &ovids)) goto meancurvsq_cleanup;\n\n        int vids[nvert]; // Copy so we can reorder\n        for (int j=0; j<nvert; j++) vids[j]=ovids[j];\n        \n        /* Order the vertices */\n        if (!curvature_ordervertices(&synid, nvert, vids)) goto meancurvsq_cleanup;\n\n        double *x[3], s0[3], s1[3], s01[3], s101[3];\n        double norm;\n        for (int j=0; j<3; j++) matrix_getcolumn(mesh->vert, vids[j], &x[j]);\n\n        /* s0 = x1-x0; s1 = x2-x1 */\n        functional_vecsub(mesh->dim, x[1], x[0], s0);\n        functional_vecsub(mesh->dim, x[2], x[1], s1);\n\n        /* F(v0) = (s1 x s0 x s1)/|s0 x x1|/2 */\n        functional_veccross(s0, s1, s01);\n        norm=functional_vecnorm(mesh->dim, s01);\n        if (norm<MORPHO_EPS) goto meancurvsq_cleanup;\n\n        areasum+=norm/2;\n        functional_veccross(s1, s01, s101);\n\n        functional_vecaddscale(mesh->dim, frc, 0.5/norm, s101, frc);\n    }\n\n    *out = functional_vecdot(mesh->dim, frc, frc)/(areasum/3.0)/4.0;\n    if (cref->integrandonly) *out /= (areasum/3.0);\n    success=true;\n\nmeancurvsq_cleanup:\n    varray_elementidclear(&nbrs);\n    varray_elementidclear(&synid);\n\n    return success;\n}\n\nFUNCTIONAL_INIT(MeanCurvatureSq, MESH_GRADE_VERTEX)\nFUNCTIONAL_METHOD(MeanCurvatureSq, integrand, MESH_GRADE_VERTEX, areacurvatureref, areacurvature_prepareref, functional_mapintegrand, meancurvaturesq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(MeanCurvatureSq, total, MESH_GRADE_VERTEX, areacurvatureref, areacurvature_prepareref, functional_sumintegrand, meancurvaturesq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(MeanCurvatureSq, gradient, MESH_GRADE_VERTEX, areacurvatureref, areacurvature_prepareref, functional_mapnumericalgradient, meancurvaturesq_integrand, meancurvaturesq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD)\n\nMORPHO_BEGINCLASS(MeanCurvatureSq)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, MeanCurvatureSq_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, MeanCurvatureSq_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, MeanCurvatureSq_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, MeanCurvatureSq_total, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * GaussCurvature\n * ---------------------------------------------- */\n\n/** Calculate the integral of the gaussian curvature  */\nbool gausscurvature_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    areacurvatureref *cref = (areacurvatureref *) ref;\n    double anglesum = 0, areasum = 0;\n    bool success=false;\n\n    varray_elementid nbrs;\n    varray_elementid synid;\n    varray_elementidinit(&nbrs);\n    varray_elementidinit(&synid);\n\n    mesh_getsynonyms(mesh, MESH_GRADE_VERTEX, id, &synid);\n    varray_elementidwriteunique(&synid, id);\n\n    double frc[mesh->dim]; // This will hold the total force due to the triangles present\n    for (unsigned int i=0; i<mesh->dim; i++) frc[i]=0.0;\n\n    mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, MESH_GRADE_AREA, &nbrs);\n\n    for (unsigned int i=0; i<nbrs.count; i++) { /* Loop over adjacent triangles */\n        int nvert, *ovids;\n        if (!sparseccs_getrowindices(&cref->areael->ccs, nbrs.data[i], &nvert, &ovids)) goto gausscurv_cleanup;\n        \n        int vids[nvert]; // Copy so we can reorder\n        for (int j=0; j<nvert; j++) vids[j]=ovids[j];\n\n        /* Order the vertices */\n        if (!curvature_ordervertices(&synid, nvert, vids)) goto gausscurv_cleanup;\n\n        double *x[3], s0[3], s1[3], s01[3];\n        for (int j=0; j<3; j++) matrix_getcolumn(mesh->vert, vids[j], &x[j]);\n\n        /* s0 = x1-x0; s1 = x2-x0 */\n        functional_vecsub(mesh->dim, x[1], x[0], s0);\n        functional_vecsub(mesh->dim, x[2], x[0], s1);\n\n        functional_veccross(s0, s1, s01);\n        double area = functional_vecnorm(mesh->dim, s01);\n        anglesum+=atan2(area, functional_vecdot(mesh->dim, s0, s1));\n\n        areasum+=area/2;\n    }\n\n    *out = 2*M_PI-anglesum;\n    if (cref->geodesic) *out = M_PI-anglesum;\n    if (cref->integrandonly) *out /= (areasum/3.0);\n    success=true;\n\ngausscurv_cleanup:\n    varray_elementidclear(&nbrs);\n    varray_elementidclear(&synid);\n\n    return success;\n}\n\nFUNCTIONAL_INIT(GaussCurvature, MESH_GRADE_VERTEX)\nFUNCTIONAL_METHOD(GaussCurvature, integrand, MESH_GRADE_VERTEX, areacurvatureref, areacurvature_prepareref, functional_mapintegrand, gausscurvature_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(GaussCurvature, total, MESH_GRADE_VERTEX, areacurvatureref, areacurvature_prepareref, functional_sumintegrand, gausscurvature_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE)\nFUNCTIONAL_METHOD(GaussCurvature, gradient, MESH_GRADE_VERTEX, areacurvatureref, areacurvature_prepareref, functional_mapnumericalgradient, gausscurvature_integrand, meancurvaturesq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD)\n\nMORPHO_BEGINCLASS(GaussCurvature)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, GaussCurvature_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, GaussCurvature_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, GaussCurvature_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, GaussCurvature_total, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Fields\n * ********************************************************************** */\n\ntypedef struct {\n    objectfield *field;\n    grade grade;\n} fieldref;\n\n/* ----------------------------------------------\n * GradSq\n * ---------------------------------------------- */\n\nbool gradsq_computeperpendicular(unsigned int n, double *s1, double *s2, double *out) {\n    double s1s2, s2s2, sout;\n\n    /* Compute s1 - (s1.s2) s2 / (s2.2) */\n    s1s2 = functional_vecdot(n, s1, s2);\n    s2s2 = functional_vecdot(n, s2, s2);\n    if (fabs(s2s2)<MORPHO_EPS) return false; // Check for side of zero weight\n\n    double temp[n];\n    functional_vecscale(n, s1s2/s2s2, s2, temp);\n    functional_vecsub(n, s1, temp, out);\n\n    /* Scale by 1/|t|^2 */\n    sout = functional_vecnorm(n, out);\n    if (fabs(sout)<MORPHO_EPS) return false; // Check for side of zero weight\n\n    functional_vecscale(n, 1/(sout*sout), out, out);\n    return true;\n}\n\n/** Evaluates the gradient of a field quantity\n @param[in] mesh - object to use\n @param[in] field - field to compute gradient of\n @param[in] nv - number of vertices\n @param[in] vid - vertex ids\n @param[out] out - should be field->psize * mesh->dim units of storage */\nbool gradsq_evaluategradient(objectmesh *mesh, objectfield *field, int nv, int *vid, double *out) {    double *f[nv]; // Field value lists\n    double *x[nv]; // Vertex coordinates\n    unsigned int nentries=0;\n\n    // Get field values and vertex coordinates\n    for (unsigned int i=0; i<nv; i++) {\n        if (!mesh_getvertexcoordinatesaslist(mesh, vid[i], &x[i])) return false;\n        if (!field_getelementaslist(field, MESH_GRADE_VERTEX, vid[i], 0, &nentries, &f[i])) return false;\n    }\n\n    double s[3][mesh->dim], t[3][mesh->dim];\n\n    /* Vector sides */\n    functional_vecsub(mesh->dim, x[1], x[0], s[0]);\n    functional_vecsub(mesh->dim, x[2], x[1], s[1]);\n    functional_vecsub(mesh->dim, x[0], x[2], s[2]);\n\n    /* Perpendicular vectors */\n    gradsq_computeperpendicular(mesh->dim, s[2], s[1], t[0]);\n    gradsq_computeperpendicular(mesh->dim, s[0], s[2], t[1]);\n    gradsq_computeperpendicular(mesh->dim, s[1], s[0], t[2]);\n\n    /* Compute the gradient */\n    for (unsigned int i=0; i<mesh->dim*nentries; i++) out[i]=0;\n    for (unsigned int j=0; j<nv; j++) {\n        for (unsigned int i=0; i<nentries; i++) {\n            functional_vecaddscale(mesh->dim, &out[i*mesh->dim], f[j][i], t[j], &out[i*mesh->dim]);\n        }\n    }\n\n    return true;\n}\n\n/** Evaluates the gradient of a field quantity in 1D\n @param[in] mesh - object to use\n @param[in] field - field to compute gradient of\n @param[in] nv - number of vertices\n @param[in] vid - vertex ids\n @param[out] out - should be field->psize * mesh->dim units of storage */\nbool gradsq_evaluategradient1d(objectmesh *mesh, objectfield *field, int nv, int *vid, double *out) {\n    UNREACHABLE(\"GradSq in 1D not implemented.\");\n    double *f[nv]; // Field value lists\n    double *x[nv]; // Vertex coordinates\n    unsigned int nentries=0;\n\n    // Get field values and vertex coordinates\n    for (unsigned int i=0; i<nv; i++) {\n        if (!mesh_getvertexcoordinatesaslist(mesh, vid[i], &x[i])) return false;\n        if (!field_getelementaslist(field, MESH_GRADE_VERTEX, vid[i], 0, &nentries, &f[i])) return false;\n    }\n\n    double s[mesh->dim];\n\n    /* Vector sides */\n    functional_vecsub(mesh->dim, x[1], x[0], s);\n\n    /* Compute the gradient */\n    for (unsigned int i=0; i<mesh->dim*nentries; i++) out[i]=0;\n    for (unsigned int j=0; j<nv; j++) {\n        for (unsigned int i=0; i<nentries; i++) {\n//            functional_vecaddscale(mesh->dim, &out[i*mesh->dim], f[j][i], t[j], &out[i*mesh->dim]);\n        }\n    }\n\n    return true;\n}\n\n/** Evaluates the gradient of a field quantity in 3D\n @param[in] mesh - object to use\n @param[in] field - field to compute gradient of\n @param[in] nv - number of vertices\n @param[in] vid - vertex ids\n @param[out] out - should be field->psize * mesh->dim units of storage */\nbool gradsq_evaluategradient3d(objectmesh *mesh, objectfield *field, int nv, int *vid, double *out) {\n    double *f[nv]; // Field value lists\n    double *x[nv]; // Vertex coordinates\n    double xarray[nv*mesh->dim]; // Vertex coordinates\n    double xtarray[nv*mesh->dim]; // Vertex coordinates\n    unsigned int nentries=0;\n\n    // Get field values and vertex coordinates\n    for (unsigned int i=0; i<nv; i++) {\n        if (!mesh_getvertexcoordinatesaslist(mesh, vid[i], &x[i])) return false;\n        if (!field_getelementaslist(field, MESH_GRADE_VERTEX, vid[i], 0, &nentries, &f[i])) return false;\n    }\n\n    // Build a matrix such that the columns are x_i - x_0\n    for (unsigned int i=1; i<nv; i++) {\n        functional_vecsub(mesh->dim, x[i], x[0], &xarray[(i-1)*mesh->dim]);\n    }\n\n    for (unsigned int i=0; i<mesh->dim*nentries; i++) out[i]=0;\n\n    objectmatrix M = MORPHO_STATICMATRIX(xarray, mesh->dim, mesh->dim);\n    objectmatrix Mt = MORPHO_STATICMATRIX(xtarray, mesh->dim, mesh->dim);\n    matrix_transpose(&M, &Mt);\n\n    double farray[nentries*mesh->dim]; // Field elements\n    objectmatrix frhs = MORPHO_STATICMATRIX(farray, mesh->dim, nentries);\n    objectmatrix grad = MORPHO_STATICMATRIX(out, mesh->dim, nentries);\n\n    // Loop over elements of the field\n    for (unsigned int i=0; i<nentries; i++) {\n        // Copy across the field values to form the rhs\n        for (unsigned int j=0; j<mesh->dim; j++) farray[i*mesh->dim+j] = f[j+1][i]-f[0][i];\n    }\n\n    // Solve to obtain the gradient of each element\n    matrix_divs(&Mt, &frhs, &grad);\n\n    return true;\n}\n\n/** Prepares the gradsq reference */\nbool gradsq_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, fieldref *ref) {\n    bool success=false, grdset=false;\n    value field=MORPHO_NIL, grd=MORPHO_NIL;\n\n    if (objectinstance_getpropertyinterned(self, functional_fieldproperty, &field) &&\n        MORPHO_ISFIELD(field)) {\n        ref->field=MORPHO_GETFIELD(field);\n        success=true;\n    }\n\n    if (objectinstance_getpropertyinterned(self, functional_gradeproperty, &grd) &&\n        MORPHO_ISINTEGER(grd)) {\n        ref->grade=MORPHO_GETINTEGERVALUE(grd);\n        if (ref->grade>0) grdset=true;\n    }\n    if (!grdset) ref->grade=mesh_maxgrade(mesh);\n\n    return success;\n}\n\n/** Clones the nematic reference with a given substitute field */\nvoid *gradsq_cloneref(void *ref, objectfield *field, objectfield *sub) {\n    fieldref *nref = (fieldref *) ref;\n    fieldref *clone = MORPHO_MALLOC(sizeof(fieldref));\n    \n    if (clone) {\n        *clone = *nref;\n        if (clone->field==field) clone->field=sub;\n    }\n    \n    return clone;\n}\n\n/** Calculate the |grad q|^2 energy */\nbool gradsq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    fieldref *eref = ref;\n    double size=0; // Length area or volume of the element\n    double grad[eref->field->psize*mesh->dim];\n\n    if (!functional_elementsize(v, mesh, eref->grade, id, nv, vid, &size)) return false;\n\n    if (eref->grade==2) {\n        if (!gradsq_evaluategradient(mesh, eref->field, nv, vid, grad)) return false;\n    } else if (eref->grade==3) {\n        if (!gradsq_evaluategradient3d(mesh, eref->field, nv, vid, grad)) return false;\n    } else {\n        return false;\n    }\n\n    double gradnrm=functional_vecnorm(eref->field->psize*mesh->dim, grad);\n    *out = gradnrm*gradnrm*size;\n\n    return true;\n}\n\n/** Initialize a GradSq object */\nvalue GradSq_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n\n    if (nargs>0 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectinstance_setproperty(self, functional_fieldproperty, MORPHO_GETARG(args, 0));\n    } else {\n        morpho_runtimeerror(v, VM_INVALIDARGS);\n        return MORPHO_FALSE;\n    }\n\n    /* Second (optional) argument is the grade to act on */\n    if (nargs>1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) {\n            objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), functional_gradeproperty, MORPHO_GETARG(args, 1));\n        }\n    }\n\n    return MORPHO_NIL;\n}\n\nFUNCTIONAL_METHOD(GradSq, integrand, (ref.grade), fieldref, gradsq_prepareref, functional_mapintegrand, gradsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(GradSq, total, (ref.grade), fieldref, gradsq_prepareref, functional_sumintegrand, gradsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(GradSq, gradient, (ref.grade), fieldref, gradsq_prepareref, functional_mapnumericalgradient, gradsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_ADD);\n\nvalue GradSq_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    fieldref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (gradsq_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_AREA, info.sel, &ref)) {\n            info.g = ref.grade;\n            info.field = ref.field;\n            info.integrand = gradsq_integrand;\n            info.cloneref = gradsq_cloneref;\n            info.ref = &ref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n            //functional_mapfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(GradSq)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, GradSq_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, GradSq_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, GradSq_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, GradSq_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, GradSq_fieldgradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * Nematic\n * ---------------------------------------------- */\n\nstatic value nematic_ksplayproperty;\nstatic value nematic_ktwistproperty;\nstatic value nematic_kbendproperty;\nstatic value nematic_pitchproperty;\n\ntypedef struct {\n    double ksplay,ktwist,kbend,pitch;\n    bool haspitch;\n    objectfield *field;\n    grade grade;\n} nematicref;\n\n/** Prepares the nematic reference */\nbool nematic_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, nematicref *ref) {\n    bool success=false, grdset=false;\n    value field=MORPHO_NIL, grd=MORPHO_NIL;\n    value val=MORPHO_NIL;\n    ref->ksplay=1.0; ref->ktwist=1.0; ref->kbend=1.0; ref->pitch=0.0;\n    ref->haspitch=false;\n\n    if (objectinstance_getpropertyinterned(self, functional_fieldproperty, &field) &&\n        MORPHO_ISFIELD(field)) {\n        ref->field=MORPHO_GETFIELD(field);\n        success=true;\n    }\n    if (objectinstance_getpropertyinterned(self, nematic_ksplayproperty, &val) && MORPHO_ISNUMBER(val)) {\n        morpho_valuetofloat(val, &ref->ksplay);\n    }\n    if (objectinstance_getpropertyinterned(self, nematic_ktwistproperty, &val) && MORPHO_ISNUMBER(val)) {\n        morpho_valuetofloat(val, &ref->ktwist);\n    }\n    if (objectinstance_getpropertyinterned(self, nematic_kbendproperty, &val) && MORPHO_ISNUMBER(val)) {\n        morpho_valuetofloat(val, &ref->kbend);\n    }\n    if (objectinstance_getpropertyinterned(self, nematic_pitchproperty, &val) && MORPHO_ISNUMBER(val)) {\n        morpho_valuetofloat(val, &ref->pitch);\n        ref->haspitch=true;\n    }\n\n    if (objectinstance_getpropertyinterned(self, functional_gradeproperty, &grd) &&\n        MORPHO_ISINTEGER(grd)) {\n        ref->grade=MORPHO_GETINTEGERVALUE(grd);\n        if (ref->grade>0) grdset=true;\n    }\n    if (!grdset) ref->grade=mesh_maxgrade(mesh);\n\n    return success;\n}\n\n/** Clones the nematic reference with a given substitute field */\nvoid *nematic_cloneref(void *ref, objectfield *field, objectfield *sub) {\n    nematicref *nref = (nematicref *) ref;\n    nematicref *clone = MORPHO_MALLOC(sizeof(nematicref));\n    \n    if (clone) {\n        *clone = *nref;\n        if (clone->field==field) clone->field=sub;\n    }\n    \n    return clone;\n}\n\n/* Integrates two linear functions with values at vertices f[0]...f[2] and g[0]...g[2] */\ndouble nematic_bcint(double *f, double *g) {\n    return (f[0]*(2*g[0]+g[1]+g[2]) + f[1]*(g[0]+2*g[1]+g[2]) + f[2]*(g[0]+g[1]+2*g[2]))/12;\n}\n\n/* Integrates a linear vector function with values at vertices f[0]...f[2] */\ndouble nematic_bcint1(double *f) {\n    return (f[0] + f[1] + f[2])/3;\n}\n\n/* Integrates a linear vector function with values at vertices f[0]...f[n]\n   Works for dimensions 1-3 at least */\ndouble nematic_bcintf(unsigned int n, double *f) {\n    double sum = 0;\n    for (unsigned int i=0; i<n; i++) sum+=f[i];\n    return sum/n;\n}\n\n/* Integrates a product of two linear functions with values at vertices\n   f[0]...f[n] and g[0]...g[n].\n   Works for dimensions 1-3 at least */\ndouble nematic_bcintfg(unsigned int n, double *f, double *g) {\n    double sum = 0;\n    for (unsigned int i=0; i<n; i++) {\n        for (unsigned int j=0; j<n; j++) sum+=f[i]*g[j];\n        sum+=f[i]*g[i];\n    }\n    return sum/(n*(n+1));\n}\n\n/** Calculate the nematic energy */\nbool nematic_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    nematicref *eref = ref;\n    double size=0; // Length area or volume of the element\n    double gradnnraw[eref->field->psize*3];\n    double gradnn[eref->field->psize*3];\n    double divnn, curlnn[3] = { 0.0, 0.0, 0.0 };\n    \n    for (int i=0; i<eref->field->psize*3; i++) { gradnn[i]=0.0; gradnnraw[i]=0.0; }\n\n    if (!functional_elementsize(v, mesh, eref->grade, id, nv, vid, &size)) return false;\n\n    // Get nematic director components\n    double *nn[nv]; // Field value lists\n    unsigned int nentries=0;\n    for (unsigned int i=0; i<nv; i++) {\n        if (!field_getelementaslist(eref->field, MESH_GRADE_VERTEX, vid[i], 0, &nentries, &nn[i])) return false;\n    }\n\n    // Evaluate gradients of the director\n    if (eref->grade==2) {\n        if (!gradsq_evaluategradient(mesh, eref->field, nv, vid, gradnnraw)) return\n            false;\n    } else if (eref->grade==3) {\n        if (!gradsq_evaluategradient3d(mesh, eref->field, nv, vid, gradnnraw)) return\n            false;\n    }\n    \n    // Copy into 3x3 matrix\n    for (int j=0; j<3; j++) for (int i=0; i<mesh->dim; i++) gradnn[3*j+i] = gradnnraw[mesh->dim*j+i];\n    \n    // Output of this is the matrix:\n    // [ nx,x ny,x nz,x ] [ 0 3 6 ] <- indices\n    // [ nx,y ny,y nz,y ] [ 1 4 7 ]\n    // [ nx,z ny,z nz,z ] [ 2 5 8 ]\n    objectmatrix gradnnmat = MORPHO_STATICMATRIX(gradnn, 3, 3);\n\n    matrix_trace(&gradnnmat, &divnn);\n    curlnn[0]=gradnn[7]-gradnn[5]; // nz,y - ny,z\n    curlnn[1]=gradnn[2]-gradnn[6]; // nx,z - nz,x\n    curlnn[2]=gradnn[3]-gradnn[1]; // ny,x - nx,y\n\n    /* From components of the curl, construct the coefficients that go in front of integrals of\n           nx^2, ny^2, nz^2, nx*ny, ny*nz, and nz*nx over the element. */\n    double ctwst[6] = { curlnn[0]*curlnn[0], curlnn[1]*curlnn[1], curlnn[2]*curlnn[2],\n                        2*curlnn[0]*curlnn[1], 2*curlnn[1]*curlnn[2], 2*curlnn[2]*curlnn[0]};\n\n    double cbnd[6] = { ctwst[1] + ctwst[2], ctwst[0] + ctwst[2], ctwst[0] + ctwst[1],\n                       -ctwst[3], -ctwst[4], -ctwst[5] };\n\n    /* Calculate integrals of nx^2, ny^2, nz^2, nx*ny, ny*nz, and nz*nx over the element */\n    double nnt[3][nv]; // The transpose of nn\n    for (unsigned int i=0; i<nv; i++)\n        for (unsigned int j=0; j<3; j++) nnt[j][i]=nn[i][j];\n\n    double integrals[] = {  nematic_bcintfg(nv, nnt[0], nnt[0]),\n                            nematic_bcintfg(nv, nnt[1], nnt[1]),\n                            nematic_bcintfg(nv, nnt[2], nnt[2]),\n                            nematic_bcintfg(nv, nnt[0], nnt[1]),\n                            nematic_bcintfg(nv, nnt[1], nnt[2]),\n                            nematic_bcintfg(nv, nnt[2], nnt[0])\n    };\n\n    /* Now we can calculate the components of splay, twist and bend */\n    double splay=0.0, twist=0.0, bend=0.0, chol=0.0;\n\n    /* Evaluate the three contributions to the integral */\n    splay = 0.5*eref->ksplay*size*divnn*divnn;\n    for (unsigned int i=0; i<6; i++) {\n        twist += ctwst[i]*integrals[i];\n        bend += cbnd[i]*integrals[i];\n    }\n    twist *= 0.5*eref->ktwist*size;\n    bend *= 0.5*eref->kbend*size;\n\n    if (eref->haspitch) {\n        /* Cholesteric terms: 0.5 * k22 * [- 2 q (cx <nx> + cy <ny> + cz <nz>) + q^2] */\n        for (unsigned i=0; i<3; i++) {\n            chol += -2*curlnn[i]*nematic_bcintf(nv, nnt[i])*eref->pitch;\n        }\n        chol += (eref->pitch*eref->pitch);\n        chol *= 0.5*eref->ktwist*size;\n    }\n\n    *out = splay+twist+bend+chol;\n\n    return true;\n}\n\n/** Initialize a Nematic object */\nvalue Nematic_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n\n    int nfixed=nargs;\n    value ksplay=MORPHO_FLOAT(1.0),\n          ktwist=MORPHO_FLOAT(1.0),\n          kbend=MORPHO_FLOAT(1.0);\n    value pitch=MORPHO_NIL;\n\n    if (builtin_options(v, nargs, args, &nfixed, 4,\n                        nematic_ksplayproperty, &ksplay,\n                        nematic_ktwistproperty, &ktwist,\n                        nematic_kbendproperty, &kbend,\n                        nematic_pitchproperty, &pitch)) {\n        objectinstance_setproperty(self, nematic_ksplayproperty, ksplay);\n        objectinstance_setproperty(self, nematic_ktwistproperty, ktwist);\n        objectinstance_setproperty(self, nematic_kbendproperty, kbend);\n        objectinstance_setproperty(self, nematic_pitchproperty, pitch);\n    } else morpho_runtimeerror(v, NEMATIC_ARGS);\n\n    if (nfixed==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) {\n        objectinstance_setproperty(self, functional_fieldproperty, MORPHO_GETARG(args, 0));\n    } else morpho_runtimeerror(v, NEMATIC_ARGS);\n\n    return MORPHO_NIL;\n}\n\nFUNCTIONAL_METHOD(Nematic, integrand, (ref.grade), nematicref, nematic_prepareref, functional_mapintegrand, nematic_integrand, NULL, NEMATIC_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(Nematic, total, (ref.grade), nematicref, nematic_prepareref, functional_sumintegrand, nematic_integrand, NULL, NEMATIC_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(Nematic, gradient, (ref.grade), nematicref, nematic_prepareref, functional_mapnumericalgradient, nematic_integrand, NULL, NEMATIC_ARGS, SYMMETRY_NONE);\n\nvalue Nematic_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    nematicref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (nematic_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_AREA, info.sel, &ref)) {\n            info.g=ref.grade;\n            info.integrand=nematic_integrand;\n            info.ref=&ref;\n            info.cloneref=nematic_cloneref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(Nematic)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, Nematic_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, Nematic_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, Nematic_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, Nematic_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, Nematic_fieldgradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * NematicElectric\n * ---------------------------------------------- */\n\ntypedef struct {\n    objectfield *director;\n    value field;\n    grade grade;\n} nematicelectricref;\n\n/** Prepares the nematicelectric reference */\nbool nematicelectric_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, nematicelectricref *ref) {\n    bool success=false, grdset=false;\n    ref->field=MORPHO_NIL;\n    value fieldlist=MORPHO_NIL, grd=MORPHO_NIL;\n\n    if (objectinstance_getpropertyinterned(self, functional_fieldproperty, &fieldlist) &&\n        MORPHO_ISLIST(fieldlist)) {\n        objectlist *lst = MORPHO_GETLIST(fieldlist);\n        value director = MORPHO_NIL;\n        list_getelement(lst, 0, &director);\n        list_getelement(lst, 1, &ref->field);\n\n        if (MORPHO_ISFIELD(director)) ref->director=MORPHO_GETFIELD(director);\n\n        if (MORPHO_ISFIELD(ref->field) || MORPHO_ISMATRIX(ref->field)) success=true;\n    }\n\n    if (objectinstance_getpropertyinterned(self, functional_gradeproperty, &grd) &&\n        MORPHO_ISINTEGER(grd)) {\n        ref->grade=MORPHO_GETINTEGERVALUE(grd);\n        if (ref->grade>0) grdset=true;\n    }\n    if (!grdset) ref->grade=mesh_maxgrade(mesh);\n\n    return success;\n}\n\n/** Clones the nematic reference with a given substitute field */\nvoid *nematicelectric_cloneref(void *ref, objectfield *field, objectfield *sub) {\n    nematicelectricref *nref = (nematicelectricref *) ref;\n    nematicelectricref *clone = MORPHO_MALLOC(sizeof(nematicelectricref));\n    \n    if (clone) {\n        *clone = *nref;\n        if (clone->director==field) clone->director=sub;\n        if (MORPHO_ISFIELD(clone->field) &&\n            MORPHO_GETFIELD(clone->field)==field) {\n            clone->field=MORPHO_OBJECT(sub);\n        }\n    }\n    \n    return clone;\n}\n\n/** Calculate the integral (n.E)^2 energy, where E is calculated from the electric potential */\nbool nematicelectric_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    nematicelectricref *eref = ref;\n    double size=0; // Length area or volume of the element\n\n    if (!functional_elementsize(v, mesh, eref->grade, id, nv, vid, &size)) return false;\n\n    // Get nematic director components\n    double *nn[nv]; // Field value lists\n    unsigned int nentries=0;\n    for (unsigned int i=0; i<nv; i++) {\n        if (!field_getelementaslist(eref->director, MESH_GRADE_VERTEX, vid[i], 0, &nentries, &nn[i])) return false;\n    }\n\n    // The electric field ends up being constant over the element\n    double ee[mesh->dim];\n    if (MORPHO_ISFIELD(eref->field)) {\n        if (eref->grade==2) {\n            if (!gradsq_evaluategradient(mesh, MORPHO_GETFIELD(eref->field), nv, vid, ee)) return false;\n        } else if (eref->grade==3) {\n            if (!gradsq_evaluategradient3d(mesh, MORPHO_GETFIELD(eref->field), nv, vid, ee)) return false;\n        }\n    }\n\n    /* Calculate integrals of nx^2, ny^2, nz^2, nx*ny, ny*nz, and nz*nx over the element */\n    double nnt[mesh->dim][nv]; // The transpose of nn\n    for (unsigned int i=0; i<nv; i++)\n        for (unsigned int j=0; j<mesh->dim; j++) nnt[j][i]=nn[i][j];\n\n    /* Calculate integral (n.e)^2 using the above results */\n    double total = ee[0]*ee[0]*nematic_bcintfg(nv, nnt[0], nnt[0])+\n                   ee[1]*ee[1]*nematic_bcintfg(nv, nnt[1], nnt[1])+\n                   ee[2]*ee[2]*nematic_bcintfg(nv, nnt[2], nnt[2])+\n                   2*ee[0]*ee[1]*nematic_bcintfg(nv, nnt[0], nnt[1])+\n                   2*ee[1]*ee[2]*nematic_bcintfg(nv, nnt[1], nnt[2])+\n                   2*ee[2]*ee[0]*nematic_bcintfg(nv, nnt[2], nnt[0]);\n\n    *out = size*total;\n\n    return true;\n}\n\n/** Initialize a NematicElectric object */\nvalue NematicElectric_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n\n    if (nargs==2 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISFIELD(MORPHO_GETARG(args, 1))) {\n        objectlist *new = object_newlist(2, &MORPHO_GETARG(args, 0));\n        if (new) {\n            value lst = MORPHO_OBJECT(new);\n            objectinstance_setproperty(self, functional_fieldproperty, lst);\n            morpho_bindobjects(v, 1, &lst);\n        }\n    } else morpho_runtimeerror(v, NEMATICELECTRIC_ARGS);\n\n    return MORPHO_NIL;\n}\n\nFUNCTIONAL_METHOD(NematicElectric, integrand, (ref.grade), nematicelectricref, nematicelectric_prepareref, functional_mapintegrand, nematicelectric_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(NematicElectric, total, (ref.grade), nematicelectricref, nematicelectric_prepareref, functional_sumintegrand, nematicelectric_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(NematicElectric, gradient, (ref.grade), nematicelectricref, nematicelectric_prepareref, functional_mapnumericalgradient, nematicelectric_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE);\n\nvalue NematicElectric_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    nematicelectricref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (nematicelectric_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_AREA, info.sel, &ref)) {\n            info.g=ref.grade;\n            info.integrand=nematicelectric_integrand;\n            info.cloneref=nematicelectric_cloneref;\n            info.ref=&ref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(NematicElectric)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, NematicElectric_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, NematicElectric_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, NematicElectric_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, NematicElectric_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, NematicElectric_fieldgradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * NormSq\n * ---------------------------------------------- */\n\n/** Calculate the norm squared of a field quantity */\nbool normsq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    fieldref *eref = ref;\n    unsigned int nentries;\n    double *entries;\n\n    if (field_getelementaslist(eref->field, MESH_GRADE_VERTEX, id, 0, &nentries, &entries)) {\n        *out = functional_vecdot(nentries, entries, entries);\n        return true;\n    }\n\n    return false;\n}\n\nFUNCTIONAL_METHOD(NormSq, integrand, MESH_GRADE_VERTEX, fieldref, gradsq_prepareref, functional_mapintegrand, normsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(NormSq, total, MESH_GRADE_VERTEX, fieldref, gradsq_prepareref, functional_sumintegrand, normsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(NormSq, gradient, MESH_GRADE_VERTEX, fieldref, gradsq_prepareref, functional_mapnumericalgradient, normsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nvalue NormSq_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    fieldref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        if (gradsq_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_VERTEX, info.sel, &ref)) {\n            info.g=MESH_GRADE_VERTEX;\n            info.ref=&ref;\n            info.field=ref.field;\n            info.integrand=normsq_integrand;\n            info.cloneref=gradsq_cloneref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(NormSq)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, GradSq_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, NormSq_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, NormSq_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, NormSq_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, NormSq_fieldgradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Integrals\n * ********************************************************************** */\n\n/** Integral references\n @brief Used to pass through the functional element mapping system.\n A thread local copy is made with cloned fields */\n\ntypedef struct {\n    value integrand;\n    \n    int nfields;\n    value *fields;\n    value *originalfields; // Original fields\n    value method; // Method dictionary\n    objectmesh *mref; // Reference mesh\n    vm *v;\n    bool weightbyref; // Use reference mesh for the element\n} integralref;\n\n/* ----------------------------------------------\n * Integrand functions\n * ---------------------------------------------- */\n\n/** Integral element references\n @brief used to store information about the current element in thread-local storage. We wrap them in an object so that they can be safely stored in a value.\n Guaranteed to be thread local */\n\ntypedef struct {\n    object obj;\n    objectmesh *mesh;    // The current mesh object\n    \n    integralref *iref;   // The current integral ref structure\n    \n// Information about the element\n    grade g;             // Current grade\n    elementid id;        // Current element\n    int nv;              // Number of vertices\n    int *vid;            // Vertex ids\n    double **vertexposn; // List of vertex positions\n    double elementsize;  // Size of the element\n    \n// Interpolated quantities:\n    double *lambda;      // Barycentric coordinates\n    double *posn;        // Position in physical space\n    \n    quantity *quantities; // Original quantities obtained for the element\n    objectmatrix *invj;   // Inverse jacobian for the element\n    \n    value *qgrad;        // Gradients\n    value *qinterpolated; // List of interpolated quantities (this allows us to identify operators on fields\n} objectintegralelementref;\n\nsize_t objectintegralelementref_sizefn(object *obj) {\n    return sizeof(objectintegralelementref);\n}\n\nvoid objectintegralelementref_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Elementref>\");\n}\n\nobjecttypedefn objectintegralelementrefdefn = {\n    .printfn=objectintegralelementref_printfn,\n    .markfn=NULL,\n    .freefn=NULL,\n    .sizefn=objectintegralelementref_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\nobjecttype objectintegralelementreftype;\n#define OBJECT_INTEGRALELEMENTREF objectintegralelementreftype\n\n/** Tests whether an object is an element ref */\n#define MORPHO_ISINTEGRALELEMENTREF(val) object_istype(val, OBJECT_INTEGRALELEMENTREF)\n\n/** Gets the object as an element ref */\n#define MORPHO_GETINTEGRALELEMENTREF(val) ((objectintegralelementref *) MORPHO_GETOBJECT(val))\n\n/** Static element ref */\n#define MORPHO_STATICINTEGRALELEMENTREF(mesh, grade, id, nv, vid)      { .obj.type=OBJECT_INTEGRALELEMENTREF, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .g=grade, .mesh=mesh, .id=id, .nv=nv, .vid=vid, .qinterpolated=NULL }\n\nint elementhandle;\n\n/** Get the current element ref from thread-local storage in the VM */\nobjectintegralelementref *integral_getelementref(vm *v) {\n    value elref=MORPHO_NIL;\n    vm_gettlvar(v, elementhandle, &elref);\n    if (MORPHO_ISINTEGRALELEMENTREF(elref)) return MORPHO_GETINTEGRALELEMENTREF(elref);\n    \n    return NULL;\n}\n\n/* --------\n * Tangent\n * -------- */\n\nint tangenthandle; // TL storage handle for tangent vectors\n\n/** Evaluate the tangent vector */\nvoid integral_evaluatetangent(vm *v, value *out) {\n    objectintegralelementref *elref = integral_getelementref(v);\n    if (!elref) { morpho_runtimeerror(v, INTEGRAL_SPCLFN, TANGENT_FUNCTION); return; }\n    \n    int dim = elref->mesh->dim;\n    \n    objectmatrix *mtangent = object_newmatrix(dim, 1, false);\n    if (!mtangent) {\n        morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        return;\n    }\n    \n    functional_vecsub(dim, elref->vertexposn[1], elref->vertexposn[0], mtangent->elements);\n\n    double tnorm=functional_vecnorm(dim, mtangent->elements);\n    if (fabs(tnorm)>MORPHO_EPS) functional_vecscale(dim, 1.0/tnorm, mtangent->elements, mtangent->elements);\n    \n    vm_settlvar(v, tangenthandle, MORPHO_OBJECT(mtangent));\n    *out = MORPHO_OBJECT(mtangent);\n}\n\nstatic value integral_tangent(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    \n    vm_gettlvar(v, tangenthandle, &out);\n    if (MORPHO_ISNIL(out)) integral_evaluatetangent(v, &out);\n    \n    return out;\n}\n\n/* --------\n * Normal\n * -------- */\n\nint normlhandle; // TL storage handle for normal vectors\n\n/** Evaluates the normal vector */\nvoid integral_evaluatenormal(vm *v, value *out) {\n    objectintegralelementref *elref = integral_getelementref(v);\n    if (!elref) { morpho_runtimeerror(v, INTEGRAL_SPCLFN, NORMAL_FUNCTION); return; }\n    \n    int dim = elref->mesh->dim;\n    double s0[dim], s1[dim];\n    objectmatrix *mnormal = object_newmatrix(dim, 1, false);\n    if (!mnormal) {\n        morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        return;\n    }\n    \n    functional_vecsub(dim, elref->vertexposn[1], elref->vertexposn[0], s0);\n    functional_vecsub(dim, elref->vertexposn[2], elref->vertexposn[1], s1);\n    functional_veccross(s0, s1, mnormal->elements);\n    \n    double nnorm=functional_vecnorm(dim, mnormal->elements);\n    if (fabs(nnorm)>MORPHO_EPS) functional_vecscale(dim, 1.0/nnorm, mnormal->elements, mnormal->elements);\n    \n    vm_settlvar(v, normlhandle, MORPHO_OBJECT(mnormal));\n    *out = MORPHO_OBJECT(mnormal);\n}\n\nstatic value integral_normal(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n\n    vm_gettlvar(v, normlhandle, &out);\n    if (MORPHO_ISNIL(out)) integral_evaluatenormal(v, &out);\n    \n    return out;\n}\n\n/* --------\n * Gradient\n * -------- */\n\nbool integrator_sumquantityweighted(int n, double *wts, value *q, value *out);\n\n/** @brief Prepares an inverse jacobian matrix.\n    @param[in] dim - dimension of physical space\n    @param[in] g - grade of the object\n    @param[in] x - list of vertex positions (grade+1 entries, each of length dim)\n    @param[out] invj - inverse jacobian for the transformation (dim*g entries) */\nbool integral_prepareinvjacobian(unsigned int dim, grade g, double **x, objectmatrix *invj) {\n    bool success=false;\n    \n    // Construct the (dim x g) matrix of edge vectors\n    double s[dim*g];\n    for (int i=0; i<g; i++) functional_vecsub(dim, x[i+1], x[0], s + i*dim);\n    \n    if (g==dim) {\n        objectmatrix smat = MORPHO_STATICMATRIX(s, dim, dim);\n        success=(matrix_inverse(&smat, invj)==MATRIX_OK);\n    } else if (g==1) {\n        double s01norm = functional_vecdot(dim, s, s);\n        if (s01norm>0) {\n            functional_vecscale(dim, 1.0/s01norm, s, invj->elements);\n            success=true;\n        }\n    } else if (g==2 && dim==3) {\n        double *s0 = s, *s1 = s+dim, s0xs1[dim], u[dim], v[dim*g];\n        functional_veccross(s0, s1, s0xs1);\n        double s0xs1norm = functional_vecnorm(dim, s0xs1);\n        if (s0xs1norm>0) {\n            double invs0xs1norm = 1/(s0xs1norm*s0xs1norm);\n            functional_veccross(s1, s0xs1, u);\n            functional_vecscale(dim, invs0xs1norm, u, v);\n            functional_veccross(s0xs1, s0, u);\n            functional_vecscale(dim, invs0xs1norm, u, v+dim);\n            \n            objectmatrix invjt = MORPHO_STATICMATRIX(v, dim, g);\n            \n            matrix_transpose(&invjt, invj);\n            \n            success=true;\n        }\n    }\n    return success;\n}\n\n/** Allocate suitable storage for the gradient */\nbool integral_gradalloc(int dim, value prototype, value *out) {\n    if (MORPHO_ISNIL(prototype)) { // Scalar\n        objectmatrix *mgrad=object_newmatrix(dim, 1, false);\n        if (mgrad) *out = MORPHO_OBJECT(mgrad);\n        return mgrad;\n    } else if (MORPHO_ISMATRIX(prototype)) {\n        objectlist *mlst = object_newlist(0, NULL);\n        if (mlst) *out = MORPHO_OBJECT(mlst);\n        return mlst;\n    } else UNREACHABLE(\"Field type not supported in grad\");\n    return false;\n}\n\n/** Prepares the gradient sum to hold the component of the gradient */\nbool integral_gradsuminit(int i, value prototype, value dest, value *sum) {\n    if (MORPHO_ISLIST(dest)) {\n        objectlist *lst = MORPHO_GETLIST(dest);\n        \n        if (i>=list_length(lst)) {\n            objectmatrix *prmat = MORPHO_GETMATRIX(prototype);\n            objectmatrix *new = object_newmatrix(prmat->nrows, prmat->ncols, true);\n            if (!new) return false;\n            *sum = MORPHO_OBJECT(new);\n            list_append(lst, *sum);\n        } else {\n            matrix_zero(MORPHO_GETMATRIX(lst->val.data[i]));\n            *sum = lst->val.data[i];\n        }\n    }\n    return true;\n}\n \n/** Copies the component of the gradient into the relevant destination if needed */\nbool integral_gradsumcopy(int i, value sum, value dest) {\n    if (MORPHO_ISMATRIX(dest)) {\n        return morpho_valuetofloat(sum, &MORPHO_GETMATRIX(dest)->elements[i]);\n    } else return true;\n}\n\n/** Copies the component of the gradient into the relevant destination */\nbool integral_oldgradcopy(int dim, int ndof, double *grad, value prototype, value dest) {\n    bool success=false;\n    if (MORPHO_ISMATRIX(dest)) {\n        objectmatrix *mdest = MORPHO_GETMATRIX(dest);\n        memcpy(mdest->elements, grad, sizeof(double)*dim);\n        success=true;\n    } else if (MORPHO_ISLIST(dest)) {\n        objectlist *lst = MORPHO_GETLIST(dest);\n        objectmatrix *proto = MORPHO_GETMATRIX(prototype);\n        for (int i=0; i<dim; i++) {\n            objectmatrix *mgrad=NULL;\n            value el;\n            \n            if (i>=list_length(lst)) {\n                mgrad=object_newmatrix(proto->nrows, proto->ncols, false); // Should copy prototype dimensions!\n                if (mgrad) {\n                    for (int k=0; k<ndof; k++) mgrad->elements[k]=grad[k*dim+i];\n                    list_append(lst, MORPHO_OBJECT(mgrad));\n                    success=true;\n                }\n            }\n        }\n    }\n    return success;\n}\n\n/** Evaluates the gradient of a field */\nbool integral_evaluategradient(vm *v, value q, value *out) {\n    objectintegralelementref *elref = integral_getelementref(v);\n    if (!elref) { morpho_runtimeerror(v, INTEGRAL_SPCLFN, GRAD_FUNCTION); return false; }\n    \n    /* Identify the field being referred to */\n    int ifld, xfld=-1;\n    for (ifld=0; ifld<elref->iref->nfields; ifld++) {\n        if (MORPHO_ISFIELD(q) && MORPHO_ISSAME(elref->iref->originalfields[ifld], q)) break;\n        else if (MORPHO_ISSAME(elref->qinterpolated[ifld], q)) {\n            if (xfld>=0) { morpho_runtimeerror(v, INTEGRAL_AMBGSFLD); return false; }\n            // @warning: This will fail if two fields happen to have the same value(!)\n            xfld=ifld;\n        }\n    }\n    if (xfld>=0) ifld = xfld;\n    \n    // Raise an error if we couldn't find it\n    if (ifld>=elref->iref->nfields) {\n        morpho_runtimeerror(v, INTEGRAL_FLD); return false;\n    }\n    \n    // Extract information from the field\n    objectfield *fld = MORPHO_GETFIELD(elref->iref->fields[ifld]);\n    int dim = elref->mesh->dim;\n    \n    // Allocate objects if need be. Don't bind these; these will be freed when the elref is cleared.\n    if (!MORPHO_ISOBJECT(elref->qgrad[ifld])) {\n        if (!integral_gradalloc(dim, fld->prototype, &elref->qgrad[ifld])) {\n            morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false;\n        }\n    }\n    \n    bool success=false;\n    \n    // Evaluate gradient\n    if (MORPHO_ISFESPACE(fld->fnspc)) {\n        if (!elref->invj) {\n            elref->invj=object_newmatrix(elref->g, elref->mesh->dim, false);\n            \n            if (elref->invj) {\n                integral_prepareinvjacobian(elref->mesh->dim, elref->g, elref->vertexposn, elref->invj);\n            } else {\n                morpho_runtimeerror(v, INTEGRAL_GRDEVL);\n                return false;\n            }\n        }\n        \n        int nnodes = MORPHO_GETFESPACE(fld->fnspc)->fespace->nnodes;\n        double gdata[nnodes * elref->g];\n        objectmatrix gmat = MORPHO_STATICMATRIX(gdata, nnodes, elref->g);\n        \n        // Compute gradient in reference frame\n        fespace_gradient(MORPHO_GETFESPACE(fld->fnspc)->fespace,\n                                elref->lambda, &gmat);\n        \n        // Compute matrix\n        double fmatdata[nnodes * dim];\n        objectmatrix fmat = MORPHO_STATICMATRIX(fmatdata, nnodes, dim);\n        \n        if (matrix_mul(&gmat, elref->invj, &fmat)!=MATRIX_OK) {\n            morpho_runtimeerror(v, INTEGRAL_GRDEVL);\n            return false;\n        }\n        \n        for (int i=0; i<dim; i++) {\n            value sum;\n            \n            if (integral_gradsuminit(i, fld->prototype, elref->qgrad[ifld], &sum) &&\n                integrator_sumquantityweighted(nnodes, fmat.elements+i*nnodes, elref->quantities[ifld].vals, &sum)) {\n                integral_gradsumcopy(i, sum, elref->qgrad[ifld]);\n            } else {\n                morpho_runtimeerror(v, INTEGRAL_GRDEVL);\n                return false;\n            }\n        }\n        \n        success=true;\n    } else { // Old gradient calculation\n        int ndof = fld->psize; // Number of degrees of freedom per element\n        double grad[ndof*dim]; // Storage for gradient\n        \n        // Evaluate correct gradient\n        if (elref->g==2) success=gradsq_evaluategradient(elref->mesh, fld, elref->nv, elref->vid, grad);\n        else if (elref->g==3) success=gradsq_evaluategradient3d(elref->mesh, fld, elref->nv, elref->vid, grad);\n        \n        integral_oldgradcopy(dim, ndof, grad, fld->prototype, elref->qgrad[ifld]);\n        success=true;\n    }\n    \n    // Store for further use\n    if (success) *out=elref->qgrad[ifld];\n    \n    return success;\n}\n\nstatic value integral_gradfn(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    if (nargs==1) {\n        integral_evaluategradient(v, MORPHO_GETARG(args, 0), &out);\n    } else morpho_runtimeerror(v, INTEGRAL_FLD);\n    \n    return out;\n}\n\n/* -------------------\n * Cauchy green strain\n * ------------------- */\n\nint cauchygreenhandle; // TL storage handle for CG tensor\n\n/** Evaluates the cg strain tensor */\nvoid integral_evaluatecg(vm *v, value *out) {\n    objectintegralelementref *elref = integral_getelementref(v);\n    \n    if (!elref || !elref->iref->mref) {\n        morpho_runtimeerror(v, INTEGRAL_SPCLFN, CGTENSOR_FUNCTION); return;\n    }\n    \n    int gdim=elref->nv-1; // Dimension of Gram matrix\n    \n    objectmatrix *cg=object_newmatrix(gdim, gdim, true);\n    if (!cg) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return; }\n    \n    double gramrefel[gdim*gdim], gramdefel[gdim*gdim], qel[gdim*gdim], rel[gdim*gdim];\n    objectmatrix gramref = MORPHO_STATICMATRIX(gramrefel, gdim, gdim); // Gram matrices\n    objectmatrix gramdef = MORPHO_STATICMATRIX(gramdefel, gdim, gdim); //\n    objectmatrix q = MORPHO_STATICMATRIX(qel, gdim, gdim); // Inverse of Gram in source domain\n    objectmatrix r = MORPHO_STATICMATRIX(rel, gdim, gdim); // Intermediate calculations\n    \n    linearelasticity_calculategram(elref->iref->mref->vert, elref->mesh->dim, elref->nv, elref->vid, &gramref);\n    linearelasticity_calculategram(elref->mesh->vert, elref->mesh->dim, elref->nv, elref->vid, &gramdef);\n    \n    if (matrix_inverse(&gramref, &q)!=MATRIX_OK) return;\n    if (matrix_mul(&gramdef, &q, &r)!=MATRIX_OK) return;\n\n    matrix_identity(cg);\n    matrix_scale(cg, -0.5);\n    matrix_accumulate(cg, 0.5, &r);\n    \n    vm_settlvar(v, cauchygreenhandle, MORPHO_OBJECT(cg));\n    *out = MORPHO_OBJECT(cg);\n}\n\nstatic value integral_cgfn(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n\n    vm_gettlvar(v, cauchygreenhandle, &out);\n    if (MORPHO_ISNIL(out)) integral_evaluatecg(v, &out);\n    \n    return out;\n}\n\n/* ----------------------\n * General initialization\n * ---------------------- */\n\n/** Clears threadlocal storage */\nvoid integral_cleartlvars(vm *v) {\n    int handles[] = { elementhandle, normlhandle, tangenthandle, cauchygreenhandle, -1 };\n    \n    for (int i=0; handles[i]>=0; i++) {\n        vm_settlvar(v, handles[i], MORPHO_NIL);\n    }\n}\n\nvoid integral_freetlvars(vm *v) {\n    int handles[] = { normlhandle, tangenthandle, cauchygreenhandle, -1 };\n    \n    for (int i=0; handles[i]>=0; i++) {\n        value val;\n        vm_gettlvar(v, handles[i], &val);\n        if (MORPHO_ISOBJECT(val)) morpho_freeobject(val);\n    }\n    \n    integral_cleartlvars(v);\n}\n\n/* ----------------------------------------------\n * Generic integral support functions\n * ---------------------------------------------- */\n\nvalue functional_methodproperty;\n\n/** Prepares an integral reference */\nbool integral_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, integralref *ref) {\n    bool success=false;\n    value func=MORPHO_NIL;\n    value mref=MORPHO_NIL;\n    value wtbyref=MORPHO_NIL;\n    value field=MORPHO_NIL;\n    value method=MORPHO_NIL;\n    ref->v=NULL;\n    ref->nfields=0;\n    ref->method=MORPHO_NIL;\n    ref->mref=NULL;\n    ref->weightbyref=false;\n\n    if (objectinstance_getpropertyinterned(self, scalarpotential_functionproperty, &func) &&\n        MORPHO_ISCALLABLE(func)) {\n        ref->integrand=func;\n        success=true;\n    }\n    if (objectinstance_getpropertyinterned(self, linearelasticity_referenceproperty, &mref) &&\n        MORPHO_ISMESH(mref)) {\n        ref->mref=MORPHO_GETMESH(mref);\n    }\n    if (objectinstance_getpropertyinterned(self, linearelasticity_weightbyreferenceproperty, &wtbyref)) {\n        ref->weightbyref=!morpho_isfalse(wtbyref);\n    }\n    if (objectinstance_getpropertyinterned(self, functional_methodproperty, &method)) {\n        ref->method=method;\n    }\n    if (objectinstance_getpropertyinterned(self, functional_fieldproperty, &field) &&\n        MORPHO_ISLIST(field)) {\n        objectlist *list = MORPHO_GETLIST(field);\n        ref->nfields=list->val.count;\n        ref->fields=list->val.data;\n        ref->originalfields=list->val.data;\n        \n        for (int i=0; i<ref->nfields; i++) {\n            if (MORPHO_ISFIELD(ref->fields[i])) {\n                objectfield *fld = MORPHO_GETFIELD(ref->fields[i]);\n                field_addpool(fld);\n            }\n        }\n    }\n    return success;\n}\n\n/** Clones the integral reference with a given substitute field */\nvoid *integral_cloneref(void *ref, objectfield *field, objectfield *sub) {\n    integralref *nref = (integralref *) ref;\n    integralref *clone = MORPHO_MALLOC(sizeof(integralref));\n    \n    if (clone) {\n        *clone = *nref;\n        clone->originalfields=nref->originalfields;\n        clone->fields=MORPHO_MALLOC(sizeof(value)*clone->nfields);\n        if (!clone->fields) { MORPHO_FREE(clone); return NULL; }\n        \n        for (int i=0; i<clone->nfields; i++) {\n            clone->fields[i]=nref->fields[i];\n            if (MORPHO_ISFIELD(nref->fields[i]) &&\n                MORPHO_GETFIELD(nref->fields[i])==field) {\n                clone->fields[i]=MORPHO_OBJECT(sub);\n            }\n        }\n    }\n    \n    return clone;\n}\n\n/** Frees a reference */\nvoid integral_freeref(void *ref) {\n    integralref *nref = (integralref *) ref;\n    MORPHO_FREE(nref->fields);\n    MORPHO_FREE(ref);\n}\n\n/** Clears any data in an element ref */\nvoid integral_clearelref(objectintegralelementref *elref) {\n    if (elref->invj) object_free((object *) elref->invj);\n}\n\n/** Prepares quantity list */\nbool integral_preparequantities(integralref *iref, int nv, int *vid, quantity *quantities) {\n    bool success=false;\n    for (int k=0; k<iref->nfields; k++) {\n        objectfield *f=MORPHO_GETFIELD(iref->fields[k]);\n        \n        if (MORPHO_ISFESPACE(f->fnspc)) {\n            fespace *disc=MORPHO_GETFESPACE(f->fnspc)->fespace;\n            if (nv-1<disc->grade) {\n                if (!fespace_lower(disc, nv-1, &disc)) return false;\n            }\n            \n            quantities[k].nnodes=disc->nnodes;\n            quantities[k].ifn=disc->ifn;\n            \n            fieldindx findx[disc->nnodes];\n            fespace_doftofieldindx(f, disc, nv, vid, findx);\n            \n            quantities[k].vals=MORPHO_MALLOC(sizeof(value)*disc->nnodes);\n            for (int i=0; i<disc->nnodes; i++) {\n                int dof;\n                field_getindex(f, findx[i].g, findx[i].id, findx[i].indx, &dof);\n                field_getelementwithindex(f, dof, &quantities[k].vals[i]);\n            }\n            success=true;\n        } else {\n            quantities[k].nnodes=nv;\n            quantities[k].ifn=NULL;\n            quantities[k].vals=MORPHO_MALLOC(sizeof(value)*nv);\n            for (unsigned int i=0; i<nv; i++) {\n                field_getelement(f, MESH_GRADE_VERTEX, vid[i], 0, &quantities[k].vals[i]);\n            }\n            success=true; \n        }\n    }\n    return success;\n}\n\n/** Clears a list of quantities */\nvoid integral_clearquantities(int nq, quantity *quantities) {\n    for (int k=0; k<nq; k++) {\n        if (quantities[k].vals) MORPHO_FREE(quantities[k].vals);\n    }\n}\n\nbool integral_integrandfn(unsigned int dim, double *t, double *x, unsigned int nquantity, value *quantity, void *ref, double *fout) {\n    integralref *iref = ref;\n    objectmatrix posn = MORPHO_STATICMATRIX(x, dim, 1);\n    value args[nquantity+1], out;\n\n    // The integrand function is called with the position and then interpolated quantities.\n    args[0]=MORPHO_OBJECT(&posn);\n    for (unsigned int i=0; i<nquantity; i++) args[i+1]=quantity[i];\n    \n    objectintegralelementref *elref = integral_getelementref(iref->v);\n    if (elref) {\n        elref->lambda=t;\n        elref->posn=x;\n        elref->qinterpolated=quantity;\n    }\n    \n    if (morpho_call(iref->v, iref->integrand, nquantity+1, args, &out)) {\n        morpho_valuetofloat(out, fout);\n        return true;\n    }\n\n    return false;\n}\n\n/* ----------------------------------------------\n * LineIntegral\n * ---------------------------------------------- */\n\n/** Integrate a function over a line */\nbool lineintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    integralref iref = *(integralref *) ref;\n    double *x[nv];\n    bool success;\n    value qgrad[iref.nfields+1];\n    for (int i=0; i<iref.nfields; i++) qgrad[i] = MORPHO_NIL;\n    \n    objectintegralelementref elref = MORPHO_STATICINTEGRALELEMENTREF(mesh, MESH_GRADE_LINE, id, nv, vid);\n    elref.iref = &iref;\n    elref.vertexposn = x;\n    elref.qgrad=qgrad;\n    elref.invj=NULL;\n    \n    if (!functional_elementsize(v, mesh, MESH_GRADE_LINE, id, nv, vid, &elref.elementsize)) return false;\n\n    iref.v=v;\n    for (unsigned int i=0; i<nv; i++) {\n        mesh_getvertexcoordinatesaslist(mesh, vid[i], &x[i]);\n    }\n\n    /* Set up quantities */\n    integral_cleartlvars(v);\n    vm_settlvar(v, elementhandle, MORPHO_OBJECT(&elref));\n\n    if (MORPHO_ISDICTIONARY(iref.method)) {\n        double err;\n        quantity quantities[iref.nfields+1];\n        integral_preparequantities(&iref, nv, vid, quantities);\n        elref.quantities=quantities;\n        \n        success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), morpho_geterror(v), mesh->dim, MESH_GRADE_LINE, x, iref.nfields, quantities, &iref, out, &err);\n        \n        integral_clearquantities(iref.nfields, quantities);\n        integral_clearelref(&elref);\n    } else { // Old integrator\n        value q0[iref.nfields+1], q1[iref.nfields+1];\n        value *q[2] = { q0, q1 };\n        for (unsigned int k=0; k<iref.nfields; k++) {\n            for (unsigned int i=0; i<nv; i++) {\n                field_getelement(MORPHO_GETFIELD(iref.fields[k]), MESH_GRADE_VERTEX, vid[i], 0, &q[i][k]);\n            }\n        }\n        \n        success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out);\n    }\n    \n    if (success) *out *=elref.elementsize;\n\n    integral_freetlvars(v);\n    \n    // Free gradient information\n    for (int i=0; i<iref.nfields; i++) {\n        if (MORPHO_ISLIST(qgrad[i])) {\n            objectlist *l = MORPHO_GETLIST(qgrad[i]);\n            for (int j=0; j<l->val.count; j++) morpho_freeobject(l->val.data[j]);\n        }\n        morpho_freeobject(qgrad[i]);\n    }\n    \n    return success;\n}\n\nFUNCTIONAL_METHOD(LineIntegral, integrand, MESH_GRADE_LINE, integralref, integral_prepareref, functional_mapintegrand, lineintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(LineIntegral, total, MESH_GRADE_LINE, integralref, integral_prepareref, functional_sumintegrand, lineintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(LineIntegral, gradient, MESH_GRADE_LINE, integralref, integral_prepareref, functional_mapnumericalgradient, lineintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(LineIntegral, hessian, MESH_GRADE_LINE, integralref, integral_prepareref, functional_mapnumericalhessian, lineintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE)\n\n/** Initialize a LineIntegral object */\nvalue LineIntegral_init(vm *v, int nargs, value *args) {\n    objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args));\n    int nparams = -1;\n    int nfixed;\n    value method=MORPHO_NIL;\n    value mref=MORPHO_NIL;\n    value wtbyref=MORPHO_NIL;\n\n    if (builtin_options(v, nargs, args, &nfixed, 3,\n                        functional_methodproperty, &method,\n                        linearelasticity_referenceproperty, &mref,\n                        linearelasticity_weightbyreferenceproperty, &wtbyref)) {\n        if (MORPHO_ISDICTIONARY(method)) {\n            objectinstance_setproperty(self, functional_methodproperty, method);\n        } else if (!MORPHO_ISNIL(method)) {\n            morpho_runtimeerror(v, INTEGRAL_MTHDDCT);\n        }\n\n        if (MORPHO_ISMESH(mref)) objectinstance_setproperty(self, linearelasticity_referenceproperty, mref);\n        if (MORPHO_ISBOOL(wtbyref)) objectinstance_setproperty(self, linearelasticity_weightbyreferenceproperty, wtbyref);\n    } else {\n        morpho_runtimeerror(v, INTEGRAL_ARGS);\n        return MORPHO_NIL;\n    }\n    \n    if (nfixed>0) {\n        value f = MORPHO_GETARG(args, 0);\n\n        if (morpho_countparameters(f, &nparams)) {\n            objectinstance_setproperty(self, scalarpotential_functionproperty, MORPHO_GETARG(args, 0));\n        } else {\n            morpho_runtimeerror(v, INTEGRAL_ARGS);\n            return MORPHO_NIL;\n        }\n    }\n\n    if (nparams!=nfixed) {\n        morpho_runtimeerror(v, INTEGRAL_NFLDS);\n        return MORPHO_NIL;\n    }\n\n    if (nfixed>1) {\n        /* Remaining arguments should be fields */\n        objectlist *list = object_newlist(nfixed-1, & MORPHO_GETARG(args, 1));\n        if (!list) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return MORPHO_NIL; }\n\n        for (unsigned int i=1; i<nfixed; i++) {\n            if (!MORPHO_ISFIELD(MORPHO_GETARG(args, i))) {\n                morpho_runtimeerror(v, INTEGRAL_ARGS);\n                object_free((object *) list);\n                return MORPHO_NIL;\n            }\n        }\n\n        value field = MORPHO_OBJECT(list);\n        objectinstance_setproperty(self, functional_fieldproperty, field);\n        morpho_bindobjects(v, 1, &field);\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Field gradients for Line Integrals */\nvalue LineIntegral_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    integralref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        // Should check whether the field is known about here...\n        if (integral_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_LINE, info.sel, &ref)) {\n            info.g=MESH_GRADE_LINE;\n            info.integrand=lineintegral_integrand;\n            info.cloneref=integral_cloneref;\n            info.freeref=integral_freeref;\n            info.ref=&ref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(LineIntegral)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineIntegral_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, LineIntegral_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LineIntegral_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, LineIntegral_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, LineIntegral_fieldgradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, LineIntegral_hessian, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * AreaIntegral\n * ---------------------------------------------- */\n\n/** Integrate a function over an area */\nbool areaintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    integralref iref = *(integralref *) ref;\n    double *x[nv];\n    bool success;\n    \n    value qgrad[iref.nfields+1];\n    for (int i=0; i<iref.nfields; i++) qgrad[i] = MORPHO_NIL;\n    \n    objectintegralelementref elref = MORPHO_STATICINTEGRALELEMENTREF(mesh, MESH_GRADE_AREA, id, nv, vid);\n    elref.iref = &iref;\n    elref.vertexposn=x;\n    elref.qgrad=qgrad;\n    elref.invj=NULL;  \n  \n    if (iref.weightbyref) {\n        if (!functional_elementsize(v, iref.mref, MESH_GRADE_AREA, id, nv, vid, &elref.elementsize)) return false;\n    } else {\n        if (!functional_elementsize(v, mesh, MESH_GRADE_AREA, id, nv, vid, &elref.elementsize)) return false;\n    }\n\n    iref.v=v;\n    for (unsigned int i=0; i<nv; i++) {\n        mesh_getvertexcoordinatesaslist(mesh, vid[i], &x[i]);\n    }\n    \n    /* Set up quantities */\n    integral_cleartlvars(v);\n    vm_settlvar(v, elementhandle, MORPHO_OBJECT(&elref));\n    \n    if (MORPHO_ISDICTIONARY(iref.method)) {\n        double err;\n        quantity quantities[iref.nfields+1];\n        integral_preparequantities(&iref, nv, vid, quantities);\n        elref.quantities=quantities;\n        \n        success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), morpho_geterror(v), mesh->dim, MESH_GRADE_AREA, x, iref.nfields, quantities, &iref, out, &err);\n        \n        integral_clearquantities(iref.nfields, quantities);\n        integral_clearelref(&elref);\n    } else {\n        value q0[iref.nfields+1], q1[iref.nfields+1], q2[iref.nfields+1];\n        value *q[3] = { q0, q1, q2 };\n        for (unsigned int k=0; k<iref.nfields; k++) {\n            for (unsigned int i=0; i<nv; i++) {\n                field_getelement(MORPHO_GETFIELD(iref.fields[k]), MESH_GRADE_VERTEX, vid[i], 0, &q[i][k]);\n            }\n        }\n        \n        success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_AREA, x, iref.nfields, q, &iref, out);\n    }\n    \n    if (success) *out *= elref.elementsize;\n\n    integral_freetlvars(v);\n    \n    // Free gradient information\n    for (int i=0; i<iref.nfields; i++) {\n        if (MORPHO_ISLIST(qgrad[i])) {\n            objectlist *l = MORPHO_GETLIST(qgrad[i]);\n            for (int j=0; j<l->val.count; j++) morpho_freeobject(l->val.data[j]);\n        }\n        morpho_freeobject(qgrad[i]);\n    }\n    \n    return success;\n}\n\nFUNCTIONAL_METHOD(AreaIntegral, integrand, MESH_GRADE_AREA, integralref, integral_prepareref, functional_mapintegrand, areaintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(AreaIntegral, total, MESH_GRADE_AREA, integralref, integral_prepareref, functional_sumintegrand, areaintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(AreaIntegral, gradient, MESH_GRADE_AREA, integralref, integral_prepareref, functional_mapnumericalgradient, areaintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\n/** Field gradients for Area Integrals */\nvalue AreaIntegral_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    integralref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        // Should check whether the field is known about here...\n        if (integral_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_AREA, info.sel, &ref)) {\n            info.g=MESH_GRADE_AREA;\n            info.integrand=areaintegral_integrand;\n            info.cloneref=integral_cloneref;\n            info.freeref=integral_freeref;\n            info.ref=&ref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(AreaIntegral)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineIntegral_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, AreaIntegral_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, AreaIntegral_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, AreaIntegral_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, AreaIntegral_fieldgradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ----------------------------------------------\n * VolumeIntegral\n * ---------------------------------------------- */\n\n/** Integrate a function over a volume */\nbool volumeintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) {\n    integralref iref = *(integralref *) ref;\n    double *x[nv];\n    bool success;\n    \n    value qgrad[iref.nfields+1];\n    for (int i=0; i<iref.nfields; i++) qgrad[i] = MORPHO_NIL;\n    \n    objectintegralelementref elref = MORPHO_STATICINTEGRALELEMENTREF(mesh, MESH_GRADE_VOLUME, id, nv, vid);\n    elref.iref = &iref;\n    elref.vertexposn = x;\n    elref.qgrad=qgrad;\n    elref.invj=NULL;\n\n    if (!functional_elementsize(v, mesh, MESH_GRADE_VOLUME, id, nv, vid, &elref.elementsize)) return false;\n\n    iref.v=v;\n    for (unsigned int i=0; i<nv; i++) {\n        mesh_getvertexcoordinatesaslist(mesh, vid[i], &x[i]);\n    }\n    \n    /* Set up quantities */\n    integral_cleartlvars(v);\n    vm_settlvar(v, elementhandle, MORPHO_OBJECT(&elref));\n    \n    if (MORPHO_ISDICTIONARY(iref.method)) {\n        double err;\n        quantity quantities[iref.nfields+1];\n        integral_preparequantities(&iref, nv, vid, quantities);\n        elref.quantities=quantities;\n        \n        success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), morpho_geterror(v), mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, quantities, &iref, out, &err);\n        \n        integral_clearquantities(iref.nfields, quantities);\n        integral_clearelref(&elref);\n    } else {\n        value q0[iref.nfields+1], q1[iref.nfields+1], q2[iref.nfields+1], q3[iref.nfields+1];\n        value *q[4] = { q0, q1, q2, q3 };\n        for (unsigned int k=0; k<iref.nfields; k++) {\n            for (unsigned int i=0; i<nv; i++) {\n                field_getelement(MORPHO_GETFIELD(iref.fields[k]), MESH_GRADE_VERTEX, vid[i], 0, &q[i][k]);\n            }\n        }\n        \n        success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, q, &iref, out);\n    }\n    \n    if (success) *out *=elref.elementsize;\n\n    integral_freetlvars(v);\n    \n    for (int i=0; i<iref.nfields; i++) {\n        if (MORPHO_ISLIST(qgrad[i])) {\n            objectlist *l = MORPHO_GETLIST(qgrad[i]);\n            for (int j=0; j<l->val.count; j++) morpho_freeobject(l->val.data[j]);\n        }\n        morpho_freeobject(qgrad[i]);\n    }\n    \n    return success;\n}\n\nFUNCTIONAL_METHOD(VolumeIntegral, integrand, MESH_GRADE_VOLUME, integralref, integral_prepareref, functional_mapintegrand, volumeintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(VolumeIntegral, total, MESH_GRADE_VOLUME, integralref, integral_prepareref, functional_sumintegrand, volumeintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\nFUNCTIONAL_METHOD(VolumeIntegral, gradient, MESH_GRADE_VOLUME, integralref, integral_prepareref, functional_mapnumericalgradient, volumeintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE);\n\n/** Field gradients for Volume Integrals */\nvalue VolumeIntegral_fieldgradient(vm *v, int nargs, value *args) {\n    functional_mapinfo info;\n    integralref ref;\n    value out=MORPHO_NIL;\n\n    if (functional_validateargs(v, nargs, args, &info)) {\n        // Should check whether the field is known about here...\n        if (integral_prepareref(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, MESH_GRADE_VOLUME, info.sel, &ref)) {\n            info.g=MESH_GRADE_VOLUME;\n            info.integrand=volumeintegral_integrand;\n            info.cloneref=integral_cloneref;\n            info.freeref=integral_freeref;\n            info.ref=&ref;\n            functional_mapnumericalfieldgradient(v, &info, &out);\n        } else morpho_runtimeerror(v, GRADSQ_ARGS);\n    }\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    return out;\n}\n\nMORPHO_BEGINCLASS(VolumeIntegral)\nMORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineIntegral_init, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, VolumeIntegral_integrand, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, VolumeIntegral_total, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, VolumeIntegral_gradient, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, VolumeIntegral_fieldgradient, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nvoid functional_initialize(void) {\n    fddelta1 = pow(MORPHO_EPS, 1.0/3.0);\n    fddelta2 = pow(MORPHO_EPS, 1.0/4.0);\n    \n    functional_gradeproperty=builtin_internsymbolascstring(FUNCTIONAL_GRADE_PROPERTY);\n    functional_fieldproperty=builtin_internsymbolascstring(FUNCTIONAL_FIELD_PROPERTY);\n    scalarpotential_functionproperty=builtin_internsymbolascstring(SCALARPOTENTIAL_FUNCTION_PROPERTY);\n    scalarpotential_gradfunctionproperty=builtin_internsymbolascstring(SCALARPOTENTIAL_GRADFUNCTION_PROPERTY);\n    linearelasticity_referenceproperty=builtin_internsymbolascstring(LINEARELASTICITY_REFERENCE_PROPERTY);\n    linearelasticity_weightbyreferenceproperty=builtin_internsymbolascstring(LINEARELASTICITY_WTBYREF_PROPERTY);\n    linearelasticity_poissonproperty=builtin_internsymbolascstring(LINEARELASTICITY_POISSON_PROPERTY);\n    hydrogel_aproperty=builtin_internsymbolascstring(HYDROGEL_A_PROPERTY);\n    hydrogel_bproperty=builtin_internsymbolascstring(HYDROGEL_B_PROPERTY);\n    hydrogel_cproperty=builtin_internsymbolascstring(HYDROGEL_C_PROPERTY);\n    hydrogel_dproperty=builtin_internsymbolascstring(HYDROGEL_D_PROPERTY);\n    hydrogel_phirefproperty=builtin_internsymbolascstring(HYDROGEL_PHIREF_PROPERTY);\n    hydrogel_phi0property=builtin_internsymbolascstring(HYDROGEL_PHI0_PROPERTY);\n    equielement_weightproperty=builtin_internsymbolascstring(EQUIELEMENT_WEIGHT_PROPERTY);\n    nematic_ksplayproperty=builtin_internsymbolascstring(NEMATIC_KSPLAY_PROPERTY);\n    nematic_ktwistproperty=builtin_internsymbolascstring(NEMATIC_KTWIST_PROPERTY);\n    nematic_kbendproperty=builtin_internsymbolascstring(NEMATIC_KBEND_PROPERTY);\n    nematic_pitchproperty=builtin_internsymbolascstring(NEMATIC_PITCH_PROPERTY);\n    \n    functional_methodproperty=builtin_internsymbolascstring(INTEGRAL_METHOD_PROPERTY);\n\n    curvature_integrandonlyproperty=builtin_internsymbolascstring(CURVATURE_INTEGRANDONLY_PROPERTY);\n    curvature_geodesicproperty=builtin_internsymbolascstring(CURVATURE_GEODESIC_PROPERTY);\n\n    objectstring objclassname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objclassname));\n\n    builtin_addclass(LENGTH_CLASSNAME, MORPHO_GETCLASSDEFINITION(Length), objclass);\n    builtin_addclass(AREA_CLASSNAME, MORPHO_GETCLASSDEFINITION(Area), objclass);\n    builtin_addclass(AREAENCLOSED_CLASSNAME, MORPHO_GETCLASSDEFINITION(AreaEnclosed), objclass);\n    builtin_addclass(VOLUMEENCLOSED_CLASSNAME, MORPHO_GETCLASSDEFINITION(VolumeEnclosed), objclass);\n    builtin_addclass(VOLUME_CLASSNAME, MORPHO_GETCLASSDEFINITION(Volume), objclass);\n    builtin_addclass(SCALARPOTENTIAL_CLASSNAME, MORPHO_GETCLASSDEFINITION(ScalarPotential), objclass);\n    builtin_addclass(LINEARELASTICITY_CLASSNAME, MORPHO_GETCLASSDEFINITION(LinearElasticity), objclass);\n    builtin_addclass(HYDROGEL_CLASSNAME, MORPHO_GETCLASSDEFINITION(Hydrogel), objclass);\n    builtin_addclass(EQUIELEMENT_CLASSNAME, MORPHO_GETCLASSDEFINITION(EquiElement), objclass);\n    builtin_addclass(LINECURVATURESQ_CLASSNAME, MORPHO_GETCLASSDEFINITION(LineCurvatureSq), objclass);\n    builtin_addclass(LINETORSIONSQ_CLASSNAME, MORPHO_GETCLASSDEFINITION(LineTorsionSq), objclass);\n    builtin_addclass(MEANCURVATURESQ_CLASSNAME, MORPHO_GETCLASSDEFINITION(MeanCurvatureSq), objclass);\n    builtin_addclass(GAUSSCURVATURE_CLASSNAME, MORPHO_GETCLASSDEFINITION(GaussCurvature), objclass);\n    builtin_addclass(GRADSQ_CLASSNAME, MORPHO_GETCLASSDEFINITION(GradSq), objclass);\n    builtin_addclass(NORMSQ_CLASSNAME, MORPHO_GETCLASSDEFINITION(NormSq), objclass);\n    builtin_addclass(LINEINTEGRAL_CLASSNAME, MORPHO_GETCLASSDEFINITION(LineIntegral), objclass);\n    builtin_addclass(AREAINTEGRAL_CLASSNAME, MORPHO_GETCLASSDEFINITION(AreaIntegral), objclass);\n    builtin_addclass(VOLUMEINTEGRAL_CLASSNAME, MORPHO_GETCLASSDEFINITION(VolumeIntegral), objclass);\n    builtin_addclass(NEMATIC_CLASSNAME, MORPHO_GETCLASSDEFINITION(Nematic), objclass);\n    builtin_addclass(NEMATICELECTRIC_CLASSNAME, MORPHO_GETCLASSDEFINITION(NematicElectric), objclass);\n\n    builtin_addfunction(TANGENT_FUNCTION, integral_tangent, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(NORMAL_FUNCTION, integral_normal, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(GRAD_FUNCTION, integral_gradfn, BUILTIN_FLAGSEMPTY);\n    builtin_addfunction(CGTENSOR_FUNCTION, integral_cgfn, BUILTIN_FLAGSEMPTY);\n\n    morpho_defineerror(VOLUMEENCLOSED_ZERO, ERROR_HALT, VOLUMEENCLOSED_ZERO_MSG);\n    morpho_defineerror(FUNC_INTEGRAND_MESH, ERROR_HALT, FUNC_INTEGRAND_MESH_MSG);\n    morpho_defineerror(FUNC_ELNTFND, ERROR_HALT, FUNC_ELNTFND_MSG);\n\n    morpho_defineerror(SCALARPOTENTIAL_FNCLLBL, ERROR_HALT, SCALARPOTENTIAL_FNCLLBL_MSG);\n\n    morpho_defineerror(LINEARELASTICITY_REF, ERROR_HALT, LINEARELASTICITY_REF_MSG);\n    morpho_defineerror(LINEARELASTICITY_PRP, ERROR_HALT, LINEARELASTICITY_PRP_MSG);\n\n    morpho_defineerror(HYDROGEL_ARGS, ERROR_HALT, HYDROGEL_ARGS_MSG);\n    morpho_defineerror(HYDROGEL_PRP, ERROR_HALT, HYDROGEL_PRP_MSG);\n    morpho_defineerror(HYDROGEL_FLDGRD, ERROR_HALT, HYDROGEL_FLDGRD_MSG);\n    morpho_defineerror(HYDROGEL_ZEEROREFELEMENT, ERROR_WARNING, HYDROGEL_ZEEROREFELEMENT_MSG);\n    morpho_defineerror(HYDROGEL_BNDS, ERROR_WARNING, HYDROGEL_BNDS_MSG);\n\n    morpho_defineerror(EQUIELEMENT_ARGS, ERROR_HALT, EQUIELEMENT_ARGS_MSG);\n    morpho_defineerror(GRADSQ_ARGS, ERROR_HALT, GRADSQ_ARGS_MSG);\n    morpho_defineerror(NEMATIC_ARGS, ERROR_HALT, NEMATIC_ARGS_MSG);\n    morpho_defineerror(NEMATICELECTRIC_ARGS, ERROR_HALT, NEMATICELECTRIC_ARGS_MSG);\n\n    morpho_defineerror(FUNCTIONAL_ARGS, ERROR_HALT, FUNCTIONAL_ARGS_MSG);\n\n    morpho_defineerror(INTEGRAL_ARGS, ERROR_HALT, INTEGRAL_ARGS_MSG);\n    morpho_defineerror(INTEGRAL_NFLDS, ERROR_HALT, INTEGRAL_NFLDS_MSG);\n    morpho_defineerror(INTEGRAL_MTHDDCT, ERROR_HALT, INTEGRAL_MTHDDCT_MSG);\n    morpho_defineerror(INTEGRAL_FLD, ERROR_HALT, INTEGRAL_FLD_MSG);\n    morpho_defineerror(INTEGRAL_AMBGSFLD, ERROR_HALT, INTEGRAL_AMBGSFLD_MSG);\n    morpho_defineerror(INTEGRAL_SPCLFN, ERROR_HALT, INTEGRAL_SPCLFN_MSG);\n    morpho_defineerror(INTEGRAL_GRDEVL, ERROR_HALT, INTEGRAL_GRDEVL_MSG);\n    \n    functional_poolinitialized = false;\n    \n    objectintegralelementreftype=object_addtype(&objectintegralelementrefdefn);\n    elementhandle=vm_addtlvar();\n    tangenthandle=vm_addtlvar();\n    normlhandle=vm_addtlvar();\n    cauchygreenhandle=vm_addtlvar();\n    \n    morpho_addfinalizefn(functional_finalize);\n}\n\nvoid functional_finalize(void) {\n    if (functional_poolinitialized) threadpool_clear(&functional_pool);\n}\n\n#endif\n"
  },
  {
    "path": "src/geometry/functional.h",
    "content": "/** @file functional.h\n *  @author T J Atherton\n *\n *  @brief Functionals\n */\n\n#ifndef functional_h\n#define functional_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include <stdio.h>\n#include \"morpho.h\"\n#include \"mesh.h\"\n#include \"field.h\"\n#include \"selection.h\"\n\n/* -------------------------------------------------------\n * Functionals\n * ------------------------------------------------------- */\n\n/* Functional properties */\n#define FUNCTIONAL_GRADE_PROPERTY             \"grade\"\n#define FUNCTIONAL_FIELD_PROPERTY             \"field\"\n#define SCALARPOTENTIAL_FUNCTION_PROPERTY     \"function\"\n#define SCALARPOTENTIAL_GRADFUNCTION_PROPERTY \"gradfunction\"\n#define LINEARELASTICITY_REFERENCE_PROPERTY   \"reference\"\n#define LINEARELASTICITY_WTBYREF_PROPERTY     \"weightByReference\"\n#define LINEARELASTICITY_POISSON_PROPERTY     \"poissonratio\"\n#define HYDROGEL_A_PROPERTY                   \"a\"\n#define HYDROGEL_B_PROPERTY                   \"b\"\n#define HYDROGEL_C_PROPERTY                   \"c\"\n#define HYDROGEL_D_PROPERTY                   \"d\"\n#define HYDROGEL_PHIREF_PROPERTY              \"phiref\"\n#define HYDROGEL_PHI0_PROPERTY                \"phi0\"\n#define EQUIELEMENT_WEIGHT_PROPERTY           \"weight\"\n\n#define NEMATIC_KSPLAY_PROPERTY               \"ksplay\"\n#define NEMATIC_KTWIST_PROPERTY               \"ktwist\"\n#define NEMATIC_KBEND_PROPERTY                \"kbend\"\n#define NEMATIC_PITCH_PROPERTY                \"pitch\"\n#define NEMATIC_DIRECTOR_PROPERTY             \"director\"\n\n#define CURVATURE_INTEGRANDONLY_PROPERTY      \"integrandonly\"\n#define CURVATURE_GEODESIC_PROPERTY           \"geodesic\"\n\n#define INTEGRAL_METHOD_PROPERTY              \"method\"\n\n/* Functional methods */\n#define FUNCTIONAL_INTEGRAND_METHOD    \"integrand\"\n#define FUNCTIONAL_TOTAL_METHOD        \"total\"\n#define FUNCTIONAL_GRADIENT_METHOD     \"gradient\"\n#define FUNCTIONAL_FIELDGRADIENT_METHOD     \"fieldgradient\"\n#define FUNCTIONAL_HESSIAN_METHOD      \"hessian\"\n#define FUNCTIONAL_INTEGRANDFORELEMENT_METHOD      \"integrandForElement\"\n\n/* Special functions that can be used in integrands */\n#define TANGENT_FUNCTION               \"tangent\"\n#define NORMAL_FUNCTION                \"normal\"\n#define GRAD_FUNCTION                  \"grad\"\n#define CGTENSOR_FUNCTION              \"cgtensor\"\n\n/* Functional names */\n#define LENGTH_CLASSNAME               \"Length\"\n#define AREA_CLASSNAME                 \"Area\"\n#define AREAENCLOSED_CLASSNAME         \"AreaEnclosed\"\n#define VOLUME_CLASSNAME               \"Volume\"\n#define VOLUMEENCLOSED_CLASSNAME       \"VolumeEnclosed\"\n#define SCALARPOTENTIAL_CLASSNAME      \"ScalarPotential\"\n#define LINEARELASTICITY_CLASSNAME     \"LinearElasticity\"\n#define HYDROGEL_CLASSNAME             \"Hydrogel\"\n#define EQUIELEMENT_CLASSNAME          \"EquiElement\"\n#define LINECURVATURESQ_CLASSNAME      \"LineCurvatureSq\"\n#define LINETORSIONSQ_CLASSNAME        \"LineTorsionSq\"\n#define MEANCURVATURESQ_CLASSNAME      \"MeanCurvatureSq\"\n#define GAUSSCURVATURE_CLASSNAME       \"GaussCurvature\"\n#define GRADSQ_CLASSNAME               \"GradSq\"\n#define NORMSQ_CLASSNAME               \"NormSq\"\n#define LINEINTEGRAL_CLASSNAME         \"LineIntegral\"\n#define AREAINTEGRAL_CLASSNAME         \"AreaIntegral\"\n#define VOLUMEINTEGRAL_CLASSNAME       \"VolumeIntegral\"\n#define NEMATIC_CLASSNAME              \"Nematic\"\n#define NEMATICELECTRIC_CLASSNAME      \"NematicElectric\"\n\n/* Errors */\n#define FUNC_INTEGRAND_MESH            \"FnctlIntMsh\"\n#define FUNC_INTEGRAND_MESH_MSG        \"Method 'integrand' requires a mesh as the argument.\"\n\n#define FUNC_INTEGRAND_MESH            \"FnctlIntMsh\"\n#define FUNC_INTEGRAND_MESH_MSG        \"Method 'integrand' requires a mesh as the argument.\"\n\n#define FUNC_ELNTFND                   \"FnctlELNtFnd\"\n#define FUNC_ELNTFND_MSG               \"Mesh does not provide elements of grade %u.\"\n\n#define SCALARPOTENTIAL_FNCLLBL        \"SclrPtFnCllbl\"\n#define SCALARPOTENTIAL_FNCLLBL_MSG    \"ScalarPotential function is not callable.\"\n\n#define INTEGRAL_ARGS                  \"IntgrlArgs\"\n#define INTEGRAL_ARGS_MSG              \"Integral functionals require a callable argument, followed by zero or more Fields.\"\n\n#define INTEGRAL_MTHDDCT               \"IntgrlMthdDct\"\n#define INTEGRAL_MTHDDCT_MSG           \"The method argument requires a Dictionary containing configuration settings.\"\n\n#define INTEGRAL_FLD                   \"IntgrlFld\"\n#define INTEGRAL_FLD_MSG               \"Can't identify field.\"\n\n#define INTEGRAL_GRDEVL                \"IntgrlGrdEvl\"\n#define INTEGRAL_GRDEVL_MSG            \"Gradient evaluation failed.\"\n\n#define INTEGRAL_AMBGSFLD              \"IntgrlAmbgsFld\"\n#define INTEGRAL_AMBGSFLD_MSG          \"Field reference is ambigious: call with a Field object.\"\n\n#define INTEGRAL_SPCLFN                \"IntgrlSpclFn\"\n#define INTEGRAL_SPCLFN_MSG            \"Special function '%s' must not be called outside of an Integral.\"\n\n#define INTEGRAL_NFLDS                 \"IntgrlNFlds\"\n#define INTEGRAL_NFLDS_MSG             \"Incorrect number of Fields provided for integrand function.\"\n\n#define VOLUMEENCLOSED_ZERO            \"VolEnclZero\"\n#define VOLUMEENCLOSED_ZERO_MSG        \"VolumeEnclosed detected an element of zero size. Check that a mesh point is not coincident with the origin.\"\n\n#define LINEARELASTICITY_REF           \"LnElstctyRef\"\n#define LINEARELASTICITY_REF_MSG       \"LinearElasticity requires a mesh as the argument.\"\n\n#define LINEARELASTICITY_PRP           \"LnElstctyPrp\"\n#define LINEARELASTICITY_PRP_MSG       \"LinearElasticity requires properties 'reference' to be a mesh, 'grade' to be an integer grade and 'poissonratio' to be a number.\"\n\n#define EQUIELEMENT_ARGS               \"EquiElArgs\"\n#define EQUIELEMENT_ARGS_MSG           \"EquiElement allows 'grade' and 'weight' as optional arguments.\"\n\n#define HYDROGEL_ARGS                  \"HydrglArgs\"\n#define HYDROGEL_ARGS_MSG              \"Hydrogel requires a reference mesh and allows 'grade', 'a', 'b', 'c', 'd', 'phi0' and 'phiref' as optional arguments.\"\n\n#define HYDROGEL_PRP                   \"HydrglPrp\"\n#define HYDROGEL_PRP_MSG               \"Hydrogel requires the first argument to be a mesh, 'grade' to be an integer grade, 'a', 'b', 'c' 'd', 'phiref' to be numbers and 'phi0' to be a number or a Field.\"\n\n#define HYDROGEL_FLDGRD                \"HydrglFldGrd\"\n#define HYDROGEL_FLDGRD_MSG            \"Hydrogel has been given phi0 as a Field that lacks scalar elements in grade %u.\"\n\n#define HYDROGEL_ZEEROREFELEMENT       \"HydrglZrRfVl\"\n#define HYDROGEL_ZEEROREFELEMENT_MSG   \"Reference element %u has tiny volume V=%g, V0=%g\\n\"\n\n#define HYDROGEL_BNDS                  \"HydrglBnds\"\n#define HYDROGEL_BNDS_MSG              \"Phi outside bounds at element %u V=%g, V0=%g, phi=%g, 1-phi=%g\\n\"\n\n#define GRADSQ_ARGS                    \"GradSqArgs\"\n#define GRADSQ_ARGS_MSG                \"GradSq requires a field as the argument.\"\n\n#define NEMATIC_ARGS                   \"NmtcArgs\"\n#define NEMATIC_ARGS_MSG               \"Nematic requires a field as the argument.\"\n\n#define NEMATICELECTRIC_ARGS           \"NmtcElArgs\"\n#define NEMATICELECTRIC_ARGS_MSG       \"NematicElectric requires the director and electric field or potential as arguments (in that order).\"\n\n#define FUNCTIONAL_ARGS                \"FnctlArgs\"\n#define FUNCTIONAL_ARGS_MSG            \"Invalid args passed to method.\"\n\n/* -------------------------------------------------------\n * Functional types\n * ------------------------------------------------------- */\n\nextern value functional_gradeproperty;\nextern value functional_fieldproperty;\n\n/** Symmetry behaviors */\ntypedef enum {\n    SYMMETRY_NONE,\n    SYMMETRY_ADD\n} symmetrybhvr;\n\n/** Integrand function */\ntypedef bool (functional_integrand) (vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out);\n\n/** Gradient function */\ntypedef bool (functional_gradient) (vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc);\n\n/** Field gradient function */\ntypedef bool (functional_fieldgradient) (vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectfield *frc);\n\nstruct s_functional_mapinfo; // Resolve circular typedef dependency\n\n/** Clone reference function */\ntypedef void * (functional_cloneref) (void *ref, objectfield *field, objectfield *sub);\n\n/** Free reference function */\ntypedef void (functional_freeref) (void *ref);\n\n/** Dependencies function */\ntypedef bool (functional_dependencies) (struct s_functional_mapinfo *info, elementid id, varray_elementid *out);\n\ntypedef struct s_functional_mapinfo {\n    objectmesh *mesh; // Mesh to use\n    objectselection *sel; // Selection, if any\n    objectfield *field; // Field, if any\n    grade g; // Grade to use\n    elementid id; // Element id at which to evaluate the integrand\n    functional_integrand *integrand; // Integrand function\n    functional_gradient *grad; // Gradient\n    functional_fieldgradient *fieldgrad; // Field gradient\n    functional_dependencies *dependencies; // Dependencies\n    functional_cloneref *cloneref; // Clone a reference with a given field substituted\n    functional_freeref *freeref; // Free a reference\n    symmetrybhvr sym; // Symmetry behavior\n    void *ref; // Reference to pass on\n} functional_mapinfo;\n\nbool functional_validateargs(vm *v, int nargs, value *args, functional_mapinfo *info);\nvoid functional_symmetryimagelist(objectmesh *mesh, grade g, bool sort, varray_elementid *ids);\nbool functional_symmetrysumforces(objectmesh *mesh, objectmatrix *frc);\nbool functional_inlist(varray_elementid *list, elementid id);\nbool functional_containsvertex(int nv, int *vid, elementid id);\n\nbool functional_sumintegrand(vm *v, functional_mapinfo *info, value *out);\nbool functional_mapintegrand(vm *v, functional_mapinfo *info, value *out);\nbool functional_mapintegrandat(vm *v, functional_mapinfo *info, value *out);\nbool functional_mapgradient(vm *v, functional_mapinfo *info, value *out);\nbool functional_mapfieldgradient(vm *v, functional_mapinfo *info, value *out);\nbool functional_mapnumericalgradient(vm *v, functional_mapinfo *info, value *out);\nbool functional_mapnumericalfieldgradient(vm *v, functional_mapinfo *info, value *out);\n\nvoid functional_vecadd(unsigned int n, double *a, double *b, double *out);\nvoid functional_vecaddscale(unsigned int n, double *a, double lambda, double *b, double *out);\nvoid functional_vecsub(unsigned int n, double *a, double *b, double *out);\nvoid functional_vecscale(unsigned int n, double lambda, double *a, double *out);\ndouble functional_vecnorm(unsigned int n, double *a);\ndouble functional_vecdot(unsigned int n, double *a, double *b);\nvoid functional_veccross(double *a, double *b, double *out);\nvoid functional_veccross2d(double *a, double *b, double *out);\n\nbool functional_elementsize(vm *v, objectmesh *mesh, grade g, elementid id, int nv, int *vid, double *out);\nbool functional_elementgradient_scale(vm *v, objectmesh *mesh, grade g, elementid id, int nv, int *vid, objectmatrix *frc, double scale);\nbool functional_elementgradient(vm *v, objectmesh *mesh, grade g, elementid id, int nv, int *vid, objectmatrix *frc);\n\n/* -------------------------------------------------------\n * Functional method macros\n * ------------------------------------------------------- */\n\n/** Initialize a functional */\n#define FUNCTIONAL_INIT(name, grade) value name##_init(vm *v, int nargs, value *args) { \\\n    objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), functional_gradeproperty, MORPHO_INTEGER(grade)); \\\n    return MORPHO_NIL; \\\n}\n\n/** Evaluate an integrand */\n#define FUNCTIONAL_INTEGRAND(name, grade, integrandfn) value name##_integrand(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        info.g = grade; info.integrand = integrandfn; \\\n        functional_mapintegrand(v, &info, &out); \\\n    } \\\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); \\\n    return out; \\\n}\n\n/** Evaluate an integrand at an element */\n#define FUNCTIONAL_INTEGRANDFORELEMENT(name, grade, integrandfn) value name##_integrandForElement(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        info.g = grade; info.integrand = integrandfn; \\\n        functional_mapintegrandforelement(v, &info, &out); \\\n    } \\\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); \\\n    return out; \\\n}\n\n/** Evaluate a gradient */\n#define FUNCTIONAL_GRADIENT(name, grade, gradientfn, symbhvr) \\\nvalue name##_gradient(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        info.g = grade; info.grad = gradientfn; info.sym = symbhvr; \\\n        functional_mapgradient(v, &info, &out); \\\n    } \\\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); \\\n    \\\n    return out; \\\n}\n\n/** Evaluate a gradient */\n#define FUNCTIONAL_NUMERICALGRADIENT(name, grade, integrandfn, symbhvr) \\\nvalue name##_gradient(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        info.g = grade; info.integrand = integrandfn; info.sym = symbhvr; \\\n        functional_mapnumericalgradient(v, &info, &out); \\\n    } \\\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); \\\n    \\\n    return out; \\\n}\n\n/** Total an integrand */\n#define FUNCTIONAL_TOTAL(name, grade, totalfn) \\\nvalue name##_total(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        info.g = grade; info.integrand = totalfn; \\\n        functional_sumintegrand(v, &info, &out); \\\n    } \\\n    \\\n    return out; \\\n}\n\n/** Hessian */\n#define FUNCTIONAL_HESSIAN(name, grade, totalfn) \\\nvalue name##_hessian(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        info.g = grade; info.integrand = totalfn; \\\n        functional_mapnumericalhessian(v, &info, &out); \\\n    } \\\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); \\\n    \\\n    return out; \\\n}\n\n/* Alternative way of defining methods that use a reference */\n#define FUNCTIONAL_METHOD(class, name, grade, reftype, prepare, integrandfn, integrandmapfn, deps, err, symbhvr) value class##_##name(vm *v, int nargs, value *args) { \\\n    functional_mapinfo info; \\\n    reftype ref; \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (functional_validateargs(v, nargs, args, &info)) { \\\n        if (prepare(MORPHO_GETINSTANCE(MORPHO_SELF(args)), info.mesh, grade, info.sel, &ref)) { \\\n            info.integrand = integrandmapfn; \\\n            info.dependencies = deps, \\\n            info.sym = symbhvr; \\\n            info.g = grade; \\\n            info.ref = &ref; \\\n            integrandfn(v, &info, &out); \\\n        } else morpho_runtimeerror(v, err); \\\n    } \\\n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); \\\n    return out; \\\n}\n\n/* -------------------------------------------------------\n * Initialization\n * ------------------------------------------------------- */\n\nvoid functional_initialize(void);\nvoid functional_finalize(void);\n\n#endif\n\n#endif /* functional_h */\n"
  },
  {
    "path": "src/geometry/geometry.c",
    "content": "/** @file geometry.c\n *  @author T J Atherton\n *\n *  @brief Geometry wrapper\n */\n\n#include \"geometry.h\"\n\nvoid geometry_initialize(void) {\n    mesh_initialize();\n    integrate_initialize();\n    field_initialize();\n    functional_initialize();\n    fespace_initialize();\n    selection_initialize();\n}\n"
  },
  {
    "path": "src/geometry/geometry.h",
    "content": "/** @file geometry.h\n *  @author T J Atherton\n *\n *  @brief Geometry wrapper\n */\n\n#ifndef geometry_h\n#define geometry_h\n\n#include \"mesh.h\"\n#include \"field.h\"\n#include \"selection.h\"\n#include \"functional.h\"\n#include \"fespace.h\"\n#include \"integrate.h\"\n\nvoid geometry_initialize(void);\n\n#endif\n"
  },
  {
    "path": "src/geometry/integrate.c",
    "content": "/** @file integrate.c\n *  @author T J Atherton\n *\n *  @brief Numerical integration\n*/\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include <limits.h>\n#include <float.h>\n\n#include \"integrate.h\"\n#include \"morpho.h\"\n#include \"classes.h\"\n\n#include \"matrix.h\"\n#include \"sparse.h\"\n#include \"geometry.h\"\n\nbool integrate_recognizequantities(unsigned int nquantity, value *quantity, value *out) {\n    if (nquantity>0) {\n        for (unsigned int i=0; i<nquantity; i++) {\n            if (MORPHO_ISFLOAT(quantity[i])) {\n                out[i]=MORPHO_FLOAT(0);\n            } else if (MORPHO_ISMATRIX(quantity[i])) {\n                out[i]=MORPHO_OBJECT(object_clonematrix(MORPHO_GETMATRIX(quantity[i])));\n            } else return false;\n        }\n    }\n    return true;\n}\n\n/* **********************************************************************\n * Line integrals\n * ********************************************************************** */\n\nstatic double gk[] = {\n    /* Gauss 7 pt nodes [pt, Gauss wt, Kronrod wt] */\n    -0.949107912342759,  0.129484966168870,  0.063092092629979,\n    0.949107912342759,  0.129484966168870,  0.063092092629979,\n    -0.741531185599394,  0.279705391489277,  0.140653259715525,\n    0.741531185599394,  0.279705391489277,  0.140653259715525,\n    -0.405845151377397,  0.381830050505119,  0.190350578064785,\n    0.405845151377397,  0.381830050505119,  0.190350578064785,\n    0.000000000000000,  0.417959183673469,  0.209482141084728,\n    \n    /* Kronrod extension [pt, Gauss wt, Kronrod wt] */\n    -0.991455371120813,  0.0, 0.022935322010529,\n    0.991455371120813,  0.0, 0.022935322010529,\n    -0.864864423359769,  0.0, 0.104790010322250,\n    0.864864423359769,  0.0, 0.104790010322250,\n    -0.586087235467691,  0.0, 0.169004726639267,\n    0.586087235467691,  0.0, 0.169004726639267,\n    -0.207784955007898,  0.0, 0.204432940075298,\n    0.207784955007898,  0.0, 0.204432940075298\n};\n\nunsigned int gknpts=15;\nunsigned int gk1=7;\nunsigned int gk2=15;\n\n/* Linearly interpolate the position. t goes from [0,1] */\nvoid integrate_interpolatepositionline(unsigned int dim, double *x[3], double t, double *xout) {\n    double lambda[2] = {1-t,t};\n    for (unsigned int j=0; j<dim; j++) {\n        xout[j]=0;\n        for (unsigned int k=0; k<2; k++) xout[j]+=lambda[k]*x[k][j];\n    }\n}\n\n/* Interpolate any quantities. t goes from [0,1] */\nvoid integrate_interpolatequantitiesline(unsigned int dim, double t, unsigned int nquantity, value *quantity[2], value *qout) {\n    double lambda[2] = {1-t,t};\n    \n    for (unsigned int i=0; i<nquantity; i++) {\n        if (MORPHO_ISFLOAT(quantity[0][i])) {\n            double val = lambda[0]*MORPHO_GETFLOATVALUE(quantity[0][i])+\n                         lambda[1]*MORPHO_GETFLOATVALUE(quantity[1][i]);\n            qout[i]=MORPHO_FLOAT(val);\n        } else if (MORPHO_ISMATRIX(quantity[0][i]) && MORPHO_ISMATRIX(quantity[1][i])) {\n            objectmatrix *m0=MORPHO_GETMATRIX(quantity[0][i]),\n                         *m1=MORPHO_GETMATRIX(quantity[1][i]),\n                         *out=(MORPHO_ISMATRIX(qout[i]) ? MORPHO_GETMATRIX(qout[i]): NULL);\n            \n            if (!out) {\n                out = object_clonematrix(m0);\n                qout[i]=MORPHO_OBJECT(out);\n            }\n            \n            for (unsigned int i=0; i<m0->ncols*m0->nrows; i++) {\n                out->elements[i] = lambda[0]*m0->elements[i]+lambda[1]*m1->elements[i];\n            }\n        }\n    }\n}\n\n/** Integrate over a line element\n * @param[in] function     - function to integrate\n * @param[in] dim                - Dimension of the vertices\n * @param[in] x                     - vertices of the line x[0] = {x,y,z} etc.\n * @param[in] nquantity   - number of quantities per vertex\n * @param[in] quantity     - List of quantities for each vertex.\n * @param[in] ref                 - a pointer to any data required by the function\n * @param[in] ge                   - Global estimate of the integral (used for recursion).\n * @param[out] out               - estimate of the integral\n * @returns True on success */\nbool integrate_lineint(integrandfunction *function, unsigned int dim, double *x[2], unsigned int nquantity, value *quantity[2], value *q, void *ref, unsigned int recursiondepth, double ge, double *out) {\n    double r[gknpts], r1=0.0, r2=0.0, eps;\n    double xx[dim], gest=ge;\n    double af=pow(0.5, (double) recursiondepth); // Length of whole line from recursion depth\n    unsigned int i;\n    bool success=false;\n    double fout = 0;\n    \n    /* Try low order method for rapid results on low order functions */\n    for (unsigned int i=0; i<gknpts; i++) {\n        double tt=0.5*(1.0+gk[3*i]); // Convert [-1,1] to [0,1]\n        integrate_interpolatepositionline(dim, x, tt, xx);\n        if (nquantity)  integrate_interpolatequantitiesline(dim, tt, nquantity, quantity, q);\n        if ((*function) (dim, &tt, xx, nquantity, q, ref,&fout)){\n            r[i] = fout;\n        }\n        else {\n            return false;\n        }\n    }\n    \n    for (i=0; i<gk1; i++) {\n        r1+=r[i]*gk[3*i+1];\n        r2+=r[i]*gk[3*i+2];\n    }\n    for (; i<gk2; i++) {\n        r2+=r[i]*gk[3*i+2];\n    }\n    r1*=0.5; r2*=0.5;\n    \n    if (recursiondepth==0) gest=fabs(r2); // If at top level construct a global estimate of the integral\n    \n    eps=r2-r1;\n    eps*=af;\n    if (gest>MORPHO_EPS) eps/=gest; // Globally relative estimate using area factor\n    \n    //printf(\"Recursion depth %u: %g %g - %g\\n\",recursiondepth, r1, r2, eps);\n    \n    if (fabs(eps)<INTEGRATE_ACCURACYGOAL)  {\n        *out=r2;\n        return true;\n    }\n    \n    if (recursiondepth>INTEGRATE_MAXRECURSION) {\n        *out=r2;\n        return false;\n    }\n    \n    /* Bisect: */\n    double *xn[2]; /* Will hold the vertices. */\n    double xm[dim];\n    double est;\n    value qm[nquantity+1], *qn[2];\n    \n    /* New vertices s*/\n    for (unsigned int i=0; i<dim; i++) {\n        xm[i] = 0.5*(x[0][i]+x[1][i]);\n    }\n    /* Quantities */\n    if (nquantity) {\n        for (unsigned int i=0; i<nquantity; i++) qm[i]=MORPHO_NIL;\n        integrate_interpolatequantitiesline(dim, 0.5, nquantity, quantity, qm);\n    }\n    \n    r2=0.0;\n    xn[0]=x[0]; xn[1]=xm;\n    if (nquantity) { qn[0] = quantity[0]; qn[1] = qm; }\n    if (!integrate_lineint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &est)) goto integrate_lineint_cleanup;\n        \n    r2+=est;\n    \n    xn[0]=xm; xn[1]=x[1];\n    if (nquantity) { qn[0] = qm; qn[1] = quantity[1]; }\n    if (!integrate_lineint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &est)) goto integrate_lineint_cleanup;\n    \n    r2+=est;\n    r2*=0.5;\n    \n    *out = r2;\n    success=true;\n    \nintegrate_lineint_cleanup:\n    /* Free interpolated quantities */\n    for (unsigned int i=0; i<nquantity; i++) {\n        if (MORPHO_ISOBJECT(qm[i])) object_free(MORPHO_GETOBJECT(qm[i]));\n    }\n    \n    return success;\n}\n\n\n/* **********************************************************************\n * Area integrals\n * ********************************************************************** */\n\n/* Points to evaluate the function at in Barycentric coordinates */\n/* Adaptive rules based on Walkington, \"Quadrature on Simplices of arbitrary dimension\" */\nstatic double pts[] = {\n    0.3333333333333333, 0.3333333333333333, 0.3333333333333333,\n    0.6000000000000000, 0.2000000000000000, 0.2000000000000000,\n    0.2000000000000000, 0.6000000000000000, 0.2000000000000000,\n    0.2000000000000000, 0.2000000000000000, 0.6000000000000000,\n    0.7142857142857143, 0.1428571428571429, 0.1428571428571429,\n    0.1428571428571429, 0.7142857142857143, 0.1428571428571429,\n    0.1428571428571429, 0.1428571428571429, 0.7142857142857143,\n    0.4285714285714286, 0.4285714285714286, 0.1428571428571429,\n    0.4285714285714286, 0.1428571428571429, 0.4285714285714286,\n    0.1428571428571429, 0.4285714285714286, 0.4285714285714286,\n    \n    0.7777777777777778, 0.1111111111111111, 0.1111111111111111,\n    0.1111111111111111, 0.7777777777777778, 0.1111111111111111,\n    0.1111111111111111, 0.1111111111111111, 0.7777777777777778,\n    0.3333333333333333, 0.5555555555555556, 0.1111111111111111,\n    0.3333333333333333, 0.1111111111111111, 0.5555555555555556,\n    0.5555555555555556, 0.3333333333333333, 0.1111111111111111,\n    0.5555555555555556, 0.1111111111111111, 0.3333333333333333,\n    0.1111111111111111, 0.3333333333333333, 0.5555555555555556,\n    0.1111111111111111, 0.5555555555555556, 0.3333333333333333,\n    0.3333333333333333, 0.3333333333333333, 0.3333333333333333\n};\n\ndouble w[] = {\n    -0.5625, 0.5208333333333332, 0.5208333333333332, 0.5208333333333332,\n    \n    0.1265625, -0.5425347222222222, -0.5425347222222222, -0.5425347222222222,\n    0.4168402777777778, 0.4168402777777778, 0.4168402777777778, 0.4168402777777778,\n    0.4168402777777778, 0.4168402777777778\n};\n\nstatic double wts1[] = {-0.2812500000000000, 0.2604166666666667};\nstatic double wts2[] = {0.06328125000000000, -0.2712673611111111, 0.2084201388888889};\nstatic double wts3[] = {-0.007910156250000000, 0.1211015004960317, -0.3191433376736111,\n    0.2059465680803571};\nstatic unsigned int npts1 = 10;\nstatic unsigned int npts2 = 20;\n\n/* Linearly interpolate the position depending on the triangle */\nvoid integrate_interpolatepositiontri(unsigned int dim, double *x[3], double *lambda, double *xout) {\n    for (unsigned int j=0; j<dim; j++) {\n        xout[j]=0;\n        for (unsigned int k=0; k<3; k++) xout[j]+=lambda[k]*x[k][j];\n    }\n}\n\n/* Interpolate any quantities. t goes from [0,1] */\nvoid integrate_interpolatequantitiestri(unsigned int dim, double *lambda, unsigned int nquantity, value *quantity[3], value *qout) {\n    \n    for (unsigned int i=0; i<nquantity; i++) {\n        if (MORPHO_ISFLOAT(quantity[0][i])) {\n            double val = lambda[0]*MORPHO_GETFLOATVALUE(quantity[0][i])+\n                         lambda[1]*MORPHO_GETFLOATVALUE(quantity[1][i])+\n                         lambda[2]*MORPHO_GETFLOATVALUE(quantity[2][i]);\n            qout[i]=MORPHO_FLOAT(val);\n        } else if (MORPHO_ISMATRIX(quantity[0][i]) && MORPHO_ISMATRIX(quantity[1][i]) && MORPHO_ISMATRIX(quantity[2][i])) {\n            objectmatrix *m0=MORPHO_GETMATRIX(quantity[0][i]),\n                         *m1=MORPHO_GETMATRIX(quantity[1][i]),\n                         *m2=MORPHO_GETMATRIX(quantity[2][i]),\n                         *out=(MORPHO_ISMATRIX(qout[i]) ? MORPHO_GETMATRIX(qout[i]): NULL);\n            \n            if (!out) {\n                out = object_clonematrix(m0);\n                qout[i]=MORPHO_OBJECT(out);\n            }\n            \n            for (unsigned int i=0; i<m0->ncols*m0->nrows; i++) {\n                out->elements[i] = lambda[0]*m0->elements[i]+lambda[1]*m1->elements[i]+lambda[2]*m2->elements[i];\n            }\n        }\n    }\n}\n\n\n/** Integrate over an area element\n * @param[in] function     - function to integrate\n * @param[in] dim                - Dimension of the vertices\n * @param[in] x                     - vertices of the line x[0] = {x,y,z} etc.\n * @param[in] nquantity   - number of quantities per vertex\n * @param[in] quantity     - List of quantities for each vertex.\n * @param[in] ref                 - a pointer to any data required by the function\n * @param[in] ge                   - Global estimate of the integral (used for recursion).\n * @param[out] out               - estimate of the integral\n * @returns True on success */\nbool integrate_areaint(integrandfunction *function, unsigned int dim, double *x[3], unsigned int nquantity, value *quantity[3], value *q, void *ref, unsigned int recursiondepth, double ge, double *out) {\n    double r[npts2], r1, rr, r2, rr2, r3, rr3, eps;\n    double xx[dim], gest=ge;\n    double af=pow(0.25, (double) recursiondepth); // Area of total triangle covered from recursion depth\n    bool success=false;\n    double fout = 0;\n    /* Try low order method for rapid results on low order functions */\n    for (unsigned int i=0; i<npts1; i++) {\n        double *lambda=pts+3*i;\n        integrate_interpolatepositiontri(dim, x, lambda, xx);\n        if (nquantity)  integrate_interpolatequantitiestri(dim, lambda, nquantity, quantity, q);\n        if ((*function) (dim, lambda, xx, nquantity, q, ref, &fout)) {\n            r[i] = fout;\n        } else{\n            return false;\n        }\n        \n    }\n    rr=(r[1]+r[2]+r[3]);\n    rr2=(r[4]+r[5]+r[6]+r[7]+r[8]+r[9]);\n    r1 = wts1[0]*r[0] + wts1[1]*rr;\n    r2 = wts2[0]*r[0] + wts2[1]*rr + wts2[2]*rr2;\n    \n    if (recursiondepth==0) gest=fabs(r2); // If at top level construct a global estimate of the integral\n\n    eps=r2-r1;\n    eps*=af;\n    if (gest>MORPHO_EPS) eps/=gest; // Globally relative estimate using area factor\n\n    if (fabs(eps)<INTEGRATE_ACCURACYGOAL)  { // Low order worked\n        *out=2*r2;\n        return true;\n    }\n    \n    /* Extend order */\n    for (unsigned int i=npts1; i<npts2; i++) {\n        double *lambda=pts+3*i;\n        integrate_interpolatepositiontri(dim, x, lambda, xx);\n        if (nquantity)  integrate_interpolatequantitiestri(dim, lambda, nquantity, quantity, q);\n        if ((*function) (dim, lambda, xx, nquantity, q, ref, &fout)){\n            r[i] = fout;\n        } else{\n            return false;\n        }\n    }\n    rr3=(r[10]+r[11]+r[12]+r[13]+r[14]+r[15]+r[16]+r[17]+r[18]+r[19]);\n    r3 = wts3[0]*r[0] + wts3[1]*rr + wts3[2]*rr2 + wts3[3]*rr3;\n    \n    if (recursiondepth==0) gest=fabs(2*r3); // Use an improved estimate of the integral\n    \n    eps=r2-r3;\n    eps*=af;\n    if (gest>MORPHO_EPS) eps/=gest; // Globally relative estimate\n    //printf(\"Estimates %lg %lg %lg, err=%g af=%g\\n\", r1,r2,r3, eps, af);\n    if (fabs(eps)<INTEGRATE_ACCURACYGOAL) {\n        *out=2*r3;\n        return true;\n    }\n    \n    if (recursiondepth>INTEGRATE_MAXRECURSION) {\n        *out=2*r3;\n        return false;\n    }\n    \n    /* Quadrasect:\n         *       2\n         *      / \\\n         *   x20 - x12\n         *    / \\  / \\\n         *   0 - x01 - 1\n         */\n    double *xn[3]; /* Will hold the vertices. */\n    double x01[dim], x12[dim], x20[dim]; /* Vertices from midpoints */\n    double sub;\n    value q01[nquantity+1], q12[nquantity+1], q20[nquantity+1], *qn[3];\n    \n    r3=0.0;\n    /* New vertices s*/\n    for (unsigned int i=0; i<dim; i++) {\n        x01[i] = 0.5*(x[0][i]+x[1][i]);\n        x12[i] = 0.5*(x[1][i]+x[2][i]);\n        x20[i] = 0.5*(x[2][i]+x[0][i]);\n    }\n    /* Quantities */\n    if (nquantity) {\n        double ll[3];\n        for (unsigned int i=0; i<nquantity; i++) { q01[i]=MORPHO_NIL; q12[i]=MORPHO_NIL; q20[i]=MORPHO_NIL; }\n        ll[0]=0.5; ll[1]=0.5; ll[2]=0.0;\n        integrate_interpolatequantitiestri(dim, ll, nquantity, quantity, q01);\n        ll[0]=0.0; ll[1]=0.5; ll[2]=0.5;\n        integrate_interpolatequantitiestri(dim, ll, nquantity, quantity, q12);\n        ll[0]=0.5; ll[1]=0.0; ll[2]=0.5;\n        integrate_interpolatequantitiestri(dim, ll, nquantity, quantity, q20);\n    }\n    \n    xn[0]=x[0]; xn[1]=x01; xn[2]=x20;\n    if (nquantity) { qn[0] = quantity[0]; qn[1] = q01; qn[2] = q20; }\n    if (!integrate_areaint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &sub)) goto integrate_areaint_cleanup;\n    r3+=sub;\n    \n    xn[0]=x01; xn[1]=x[1]; xn[2]=x12;\n    if (nquantity) { qn[0] = q01; qn[1] = quantity[1]; qn[2] = q12; }\n    if (!integrate_areaint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &sub)) goto integrate_areaint_cleanup;\n    r3+=sub;\n    \n    xn[0]=x20; xn[1]=x12; xn[2]=x[2];\n    if (nquantity) { qn[0] = q20; qn[1] = q12; qn[2] = quantity[2]; }\n    if (!integrate_areaint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &sub)) goto integrate_areaint_cleanup;\n    r3+=sub;\n    \n    xn[0]=x01; xn[1]=x12; xn[2]=x20;\n    if (nquantity) { qn[0] = q01; qn[1] = q12; qn[2] = q20; }\n    if (!integrate_areaint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &sub)) goto integrate_areaint_cleanup;\n    r3+=sub;\n    \n    *out=0.25*r3;\n    success=true;\n    \nintegrate_areaint_cleanup:\n    /* Free interpolated quantities */\n    for (int j=0; j<3; j++) for (unsigned int i=0; i<nquantity; i++) {\n        if (MORPHO_ISOBJECT(qn[j][i])) object_free(MORPHO_GETOBJECT(qn[j][i]));\n    }\n    \n    return success;\n}\n\n/* **********************************************************************\n * Volume integrals\n * ********************************************************************** */\n\n// Nodes and weights from Journal of Computational and Applied Mathematics, 236, 17, 4348-4364 (2012)\n\n/*\nstatic double v1[] = {\n    0.2500000000000000,    0.2500000000000000,    0.2500000000000000,    0.2500000000000000,    1.0000000000000000\n};\n\nstatic unsigned int nv1 = 1;\n\nstatic double v2[] = {\n    0.5854101966249680,    0.1381966011250110,    0.1381966011250110,    0.1381966011250110,    0.2500000000000000,\n    0.1381966011250110,    0.5854101966249680,    0.1381966011250110,    0.1381966011250110,    0.2500000000000000,\n    0.1381966011250110,    0.1381966011250110,    0.5854101966249680,    0.1381966011250110,    0.2500000000000000,\n    0.1381966011250110,    0.1381966011250110,    0.1381966011250110,    0.5854101966249680,    0.2500000000000000\n};\n\nstatic unsigned int nv2 = 4;\n\nstatic double v3[] = {\n    0.7784952948213300,    0.0738349017262234,    0.0738349017262234,    0.0738349017262234,    0.0476331348432089,\n    0.0738349017262234,    0.7784952948213300,    0.0738349017262234,    0.0738349017262234,    0.0476331348432089,\n    0.0738349017262234,    0.0738349017262234,    0.7784952948213300,    0.0738349017262234,    0.0476331348432089,\n    0.0738349017262234,    0.0738349017262234,    0.0738349017262234,    0.7784952948213300,    0.0476331348432089,\n    0.4062443438840510,    0.4062443438840510,    0.0937556561159491,    0.0937556561159491,    0.1349112434378610,\n    0.4062443438840510,    0.0937556561159491,    0.4062443438840510,    0.0937556561159491,    0.1349112434378610,\n    0.4062443438840510,    0.0937556561159491,    0.0937556561159491,    0.4062443438840510,    0.1349112434378610,\n    0.0937556561159491,    0.4062443438840510,    0.4062443438840510,    0.0937556561159491,    0.1349112434378610,\n    0.0937556561159491,    0.4062443438840510,    0.0937556561159491,    0.4062443438840510,    0.1349112434378610,\n    0.0937556561159491,    0.0937556561159491,    0.4062443438840510,    0.4062443438840510,    0.1349112434378610\n};\n\nstatic unsigned int nv3 = 10;\n\nstatic double v4[] = {\n     0.9029422158182680,    0.0323525947272439,    0.0323525947272439,    0.0323525947272439,    0.0070670747944695,\n     0.0323525947272439,    0.9029422158182680,    0.0323525947272439,    0.0323525947272439,    0.0070670747944695,\n     0.0323525947272439,    0.0323525947272439,    0.9029422158182680,    0.0323525947272439,    0.0070670747944695,\n     0.0323525947272439,    0.0323525947272439,    0.0323525947272439,    0.9029422158182680,    0.0070670747944695,\n     0.2626825838877790,    0.6165965330619370,    0.0603604415251421,    0.0603604415251421,    0.0469986689718877,\n     0.6165965330619370,    0.2626825838877790,    0.0603604415251421,    0.0603604415251421,    0.0469986689718877,\n     0.2626825838877790,    0.0603604415251421,    0.6165965330619370,    0.0603604415251421,    0.0469986689718877,\n     0.6165965330619370,    0.0603604415251421,    0.2626825838877790,    0.0603604415251421,    0.0469986689718877,\n     0.2626825838877790,    0.0603604415251421,    0.0603604415251421,    0.6165965330619370,    0.0469986689718877,\n     0.6165965330619370,    0.0603604415251421,    0.0603604415251421,    0.2626825838877790,    0.0469986689718877,\n     0.0603604415251421,    0.2626825838877790,    0.6165965330619370,    0.0603604415251421,    0.0469986689718877,\n     0.0603604415251421,    0.6165965330619370,    0.2626825838877790,    0.0603604415251421,    0.0469986689718877,\n     0.0603604415251421,    0.2626825838877790,    0.0603604415251421,    0.6165965330619370,    0.0469986689718877,\n     0.0603604415251421,    0.6165965330619370,    0.0603604415251421,    0.2626825838877790,    0.0469986689718877,\n     0.0603604415251421,    0.0603604415251421,    0.2626825838877790,    0.6165965330619370,    0.0469986689718877,\n     0.0603604415251421,    0.0603604415251421,    0.6165965330619370,    0.2626825838877790,    0.0469986689718877,\n     0.3097693042728620,    0.3097693042728620,    0.3097693042728620,    0.0706920871814129,    0.1019369182898680,\n     0.3097693042728620,    0.3097693042728620,    0.0706920871814129,    0.3097693042728620,    0.1019369182898680,\n     0.3097693042728620,    0.0706920871814129,    0.3097693042728620,    0.3097693042728620,    0.1019369182898680,\n     0.0706920871814129,    0.3097693042728620,    0.3097693042728620,    0.3097693042728620,    0.1019369182898680\n};\n\nstatic unsigned int nv4 = 20;\n*/\n \nstatic double v5[] = {\n    0.9197896733368800,    0.0267367755543735,    0.0267367755543735,    0.0267367755543735,    0.0021900463965388,\n    0.0267367755543735,    0.9197896733368800,    0.0267367755543735,    0.0267367755543735,    0.0021900463965388,\n    0.0267367755543735,    0.0267367755543735,    0.9197896733368800,    0.0267367755543735,    0.0021900463965388,\n    0.0267367755543735,    0.0267367755543735,    0.0267367755543735,    0.9197896733368800,    0.0021900463965388,\n    0.1740356302468940,    0.7477598884818090,    0.0391022406356488,    0.0391022406356488,    0.0143395670177665,\n    0.7477598884818090,    0.1740356302468940,    0.0391022406356488,    0.0391022406356488,    0.0143395670177665,\n    0.1740356302468940,    0.0391022406356488,    0.7477598884818090,    0.0391022406356488,    0.0143395670177665,\n    0.7477598884818090,    0.0391022406356488,    0.1740356302468940,    0.0391022406356488,    0.0143395670177665,\n    0.1740356302468940,    0.0391022406356488,    0.0391022406356488,    0.7477598884818090,    0.0143395670177665,\n    0.7477598884818090,    0.0391022406356488,    0.0391022406356488,    0.1740356302468940,    0.0143395670177665,\n    0.0391022406356488,    0.1740356302468940,    0.7477598884818090,    0.0391022406356488,    0.0143395670177665,\n    0.0391022406356488,    0.7477598884818090,    0.1740356302468940,    0.0391022406356488,    0.0143395670177665,\n    0.0391022406356488,    0.1740356302468940,    0.0391022406356488,    0.7477598884818090,    0.0143395670177665,\n    0.0391022406356488,    0.7477598884818090,    0.0391022406356488,    0.1740356302468940,    0.0143395670177665,\n    0.0391022406356488,    0.0391022406356488,    0.1740356302468940,    0.7477598884818090,    0.0143395670177665,\n    0.0391022406356488,    0.0391022406356488,    0.7477598884818090,    0.1740356302468940,    0.0143395670177665,\n    0.4547545999844830,    0.4547545999844830,    0.0452454000155172,    0.0452454000155172,    0.0250305395686746,\n    0.4547545999844830,    0.0452454000155172,    0.4547545999844830,    0.0452454000155172,    0.0250305395686746,\n    0.4547545999844830,    0.0452454000155172,    0.0452454000155172,    0.4547545999844830,    0.0250305395686746,\n    0.0452454000155172,    0.4547545999844830,    0.4547545999844830,    0.0452454000155172,    0.0250305395686746,\n    0.0452454000155172,    0.4547545999844830,    0.0452454000155172,    0.4547545999844830,    0.0250305395686746,\n    0.0452454000155172,    0.0452454000155172,    0.4547545999844830,    0.4547545999844830,    0.0250305395686746,\n    0.5031186450145980,    0.2232010379623150,    0.2232010379623150,    0.0504792790607720,    0.0479839333057554,\n    0.2232010379623150,    0.5031186450145980,    0.2232010379623150,    0.0504792790607720,    0.0479839333057554,\n    0.2232010379623150,    0.2232010379623150,    0.5031186450145980,    0.0504792790607720,    0.0479839333057554,\n    0.5031186450145980,    0.2232010379623150,    0.0504792790607720,    0.2232010379623150,    0.0479839333057554,\n    0.2232010379623150,    0.5031186450145980,    0.0504792790607720,    0.2232010379623150,    0.0479839333057554,\n    0.2232010379623150,    0.2232010379623150,    0.0504792790607720,    0.5031186450145980,    0.0479839333057554,\n    0.5031186450145980,    0.0504792790607720,    0.2232010379623150,    0.2232010379623150,    0.0479839333057554,\n    0.2232010379623150,    0.0504792790607720,    0.5031186450145980,    0.2232010379623150,    0.0479839333057554,\n    0.2232010379623150,    0.0504792790607720,    0.2232010379623150,    0.5031186450145980,    0.0479839333057554,\n    0.0504792790607720,    0.5031186450145980,    0.2232010379623150,    0.2232010379623150,    0.0479839333057554,\n    0.0504792790607720,    0.2232010379623150,    0.5031186450145980,    0.2232010379623150,    0.0479839333057554,\n    0.0504792790607720,    0.2232010379623150,    0.2232010379623150,    0.5031186450145980,    0.0479839333057554,\n    0.2500000000000000,    0.2500000000000000,    0.2500000000000000,    0.2500000000000000,    0.0931745731195340\n};\n\nstatic unsigned int nv5 = 35;\n\nstatic double v6[] = {\n    0.9551438045408220,    0.0149520651530592,    0.0149520651530592,    0.0149520651530592,    0.0010373112336140,\n    0.0149520651530592,    0.9551438045408220,    0.0149520651530592,    0.0149520651530592,    0.0010373112336140,\n    0.0149520651530592,    0.0149520651530592,    0.9551438045408220,    0.0149520651530592,    0.0010373112336140,\n    0.0149520651530592,    0.0149520651530592,    0.0149520651530592,    0.9551438045408220,    0.0010373112336140,\n    0.7799760084415400,    0.1518319491659370,    0.0340960211962615,    0.0340960211962615,    0.0096016645399480,\n    0.1518319491659370,    0.7799760084415400,    0.0340960211962615,    0.0340960211962615,    0.0096016645399480,\n    0.7799760084415400,    0.0340960211962615,    0.1518319491659370,    0.0340960211962615,    0.0096016645399480,\n    0.1518319491659370,    0.0340960211962615,    0.7799760084415400,    0.0340960211962615,    0.0096016645399480,\n    0.7799760084415400,    0.0340960211962615,    0.0340960211962615,    0.1518319491659370,    0.0096016645399480,\n    0.1518319491659370,    0.0340960211962615,    0.0340960211962615,    0.7799760084415400,    0.0096016645399480,\n    0.0340960211962615,    0.7799760084415400,    0.1518319491659370,    0.0340960211962615,    0.0096016645399480,\n    0.0340960211962615,    0.1518319491659370,    0.7799760084415400,    0.0340960211962615,    0.0096016645399480,\n    0.0340960211962615,    0.7799760084415400,    0.0340960211962615,    0.1518319491659370,    0.0096016645399480,\n    0.0340960211962615,    0.1518319491659370,    0.0340960211962615,    0.7799760084415400,    0.0096016645399480,\n    0.0340960211962615,    0.0340960211962615,    0.7799760084415400,    0.1518319491659370,    0.0096016645399480,\n    0.0340960211962615,    0.0340960211962615,    0.1518319491659370,    0.7799760084415400,    0.0096016645399480,\n    0.3549340560639790,    0.5526556431060170,    0.0462051504150017,    0.0462051504150017,    0.0164493976798232,\n    0.5526556431060170,    0.3549340560639790,    0.0462051504150017,    0.0462051504150017,    0.0164493976798232,\n    0.3549340560639790,    0.0462051504150017,    0.5526556431060170,    0.0462051504150017,    0.0164493976798232,\n    0.5526556431060170,    0.0462051504150017,    0.3549340560639790,    0.0462051504150017,    0.0164493976798232,\n    0.3549340560639790,    0.0462051504150017,    0.0462051504150017,    0.5526556431060170,    0.0164493976798232,\n    0.5526556431060170,    0.0462051504150017,    0.0462051504150017,    0.3549340560639790,    0.0164493976798232,\n    0.0462051504150017,    0.3549340560639790,    0.5526556431060170,    0.0462051504150017,    0.0164493976798232,\n    0.0462051504150017,    0.5526556431060170,    0.3549340560639790,    0.0462051504150017,    0.0164493976798232,\n    0.0462051504150017,    0.3549340560639790,    0.0462051504150017,    0.5526556431060170,    0.0164493976798232,\n    0.0462051504150017,    0.5526556431060170,    0.0462051504150017,    0.3549340560639790,    0.0164493976798232,\n    0.0462051504150017,    0.0462051504150017,    0.3549340560639790,    0.5526556431060170,    0.0164493976798232,\n    0.0462051504150017,    0.0462051504150017,    0.5526556431060170,    0.3549340560639790,    0.0164493976798232,\n    0.5381043228880020,    0.2281904610687610,    0.2281904610687610,    0.0055147549744775,    0.0153747766513310,\n    0.2281904610687610,    0.5381043228880020,    0.2281904610687610,    0.0055147549744775,    0.0153747766513310,\n    0.2281904610687610,    0.2281904610687610,    0.5381043228880020,    0.0055147549744775,    0.0153747766513310,\n    0.5381043228880020,    0.2281904610687610,    0.0055147549744775,    0.2281904610687610,    0.0153747766513310,\n    0.2281904610687610,    0.5381043228880020,    0.0055147549744775,    0.2281904610687610,    0.0153747766513310,\n    0.2281904610687610,    0.2281904610687610,    0.0055147549744775,    0.5381043228880020,    0.0153747766513310,\n    0.5381043228880020,    0.0055147549744775,    0.2281904610687610,    0.2281904610687610,    0.0153747766513310,\n    0.2281904610687610,    0.0055147549744775,    0.5381043228880020,    0.2281904610687610,    0.0153747766513310,\n    0.2281904610687610,    0.0055147549744775,    0.2281904610687610,    0.5381043228880020,    0.0153747766513310,\n    0.0055147549744775,    0.5381043228880020,    0.2281904610687610,    0.2281904610687610,    0.0153747766513310,\n    0.0055147549744775,    0.2281904610687610,    0.5381043228880020,    0.2281904610687610,    0.0153747766513310,\n    0.0055147549744775,    0.2281904610687610,    0.2281904610687610,    0.5381043228880020,    0.0153747766513310,\n    0.1961837595745600,    0.3523052600879940,    0.3523052600879940,    0.0992057202494530,    0.0293520118375230,\n    0.3523052600879940,    0.1961837595745600,    0.3523052600879940,    0.0992057202494530,    0.0293520118375230,\n    0.3523052600879940,    0.3523052600879940,    0.1961837595745600,    0.0992057202494530,    0.0293520118375230,\n    0.1961837595745600,    0.3523052600879940,    0.0992057202494530,    0.3523052600879940,    0.0293520118375230,\n    0.3523052600879940,    0.1961837595745600,    0.0992057202494530,    0.3523052600879940,    0.0293520118375230,\n    0.3523052600879940,    0.3523052600879940,    0.0992057202494530,    0.1961837595745600,    0.0293520118375230,\n    0.1961837595745600,    0.0992057202494530,    0.3523052600879940,    0.3523052600879940,    0.0293520118375230,\n    0.3523052600879940,    0.0992057202494530,    0.1961837595745600,    0.3523052600879940,    0.0293520118375230,\n    0.3523052600879940,    0.0992057202494530,    0.3523052600879940,    0.1961837595745600,    0.0293520118375230,\n    0.0992057202494530,    0.1961837595745600,    0.3523052600879940,    0.3523052600879940,    0.0293520118375230,\n    0.0992057202494530,    0.3523052600879940,    0.1961837595745600,    0.3523052600879940,    0.0293520118375230,\n    0.0992057202494530,    0.3523052600879940,    0.3523052600879940,    0.1961837595745600,    0.0293520118375230,\n    0.5965649956210170,    0.1344783347929940,    0.1344783347929940,    0.1344783347929940,    0.0366291366405108,\n    0.1344783347929940,    0.5965649956210170,    0.1344783347929940,    0.1344783347929940,    0.0366291366405108,\n    0.1344783347929940,    0.1344783347929940,    0.5965649956210170,    0.1344783347929940,    0.0366291366405108,\n    0.1344783347929940,    0.1344783347929940,    0.1344783347929940,    0.5965649956210170,    0.0366291366405108\n};\n\nstatic unsigned int nv6 = 56;\n\n/* Linearly interpolate the position depending on the tetrahedron */\nvoid integrate_interpolatepositionvol(unsigned int dim, double *x[4], double *lambda, double *xout) {\n    for (unsigned int j=0; j<dim; j++) {\n        xout[j]=0;\n        for (unsigned int k=0; k<4; k++) xout[j]+=lambda[k]*x[k][j];\n    }\n}\n\n/* Interpolate any quantities. */\nvoid integrate_interpolatequantitiesvol(unsigned int dim, double *lambda, unsigned int nquantity, value *quantity[3], value *qout) {\n    for (unsigned int i=0; i<nquantity; i++) {\n        if (MORPHO_ISFLOAT(quantity[0][i])) {\n            double val = lambda[0]*MORPHO_GETFLOATVALUE(quantity[0][i])+\n                         lambda[1]*MORPHO_GETFLOATVALUE(quantity[1][i])+\n                         lambda[2]*MORPHO_GETFLOATVALUE(quantity[2][i])+\n                         lambda[3]*MORPHO_GETFLOATVALUE(quantity[3][i]);\n            qout[i]=MORPHO_FLOAT(val);\n        } else if (MORPHO_ISMATRIX(quantity[0][i]) && MORPHO_ISMATRIX(quantity[1][i]) && MORPHO_ISMATRIX(quantity[2][i]) && MORPHO_ISMATRIX(quantity[3][i])) {\n            objectmatrix *m0=MORPHO_GETMATRIX(quantity[0][i]),\n                         *m1=MORPHO_GETMATRIX(quantity[1][i]),\n                         *m2=MORPHO_GETMATRIX(quantity[2][i]),\n                         *m3=MORPHO_GETMATRIX(quantity[3][i]),\n                         *out=(MORPHO_ISMATRIX(qout[i]) ? MORPHO_GETMATRIX(qout[i]): NULL);\n            \n            if (!out) {\n                out = object_clonematrix(m0);\n                qout[i]=MORPHO_OBJECT(out);\n            }\n            \n            for (unsigned int i=0; i<m0->ncols*m0->nrows; i++) {\n                out->elements[i] = lambda[0]*m0->elements[i]+lambda[1]*m1->elements[i]+lambda[2]*m2->elements[i]+lambda[3]*m3->elements[i];\n            }\n        }\n    }\n}\n\nint nf = 0;\n\n/** Integrate over an volume element given a specified integration rule\n * @param[in] function     - function to integrate\n * @param[in] nsamples     - number of sampling pts\n * @param[in] integrationrule - integration rule data\n * @param[in] dim                - Dimension of the vertices\n * @param[in] x                     - vertices of the line x[0] = {x,y,z} etc.\n * @param[in] nquantity   - number of quantities per vertex\n * @param[in] quantity     - List of quantities for each vertex.\n * @param[in] ref                 - a pointer to any data required by the function\n * @param[out] out               - estimate of the integral\n * @returns True on success */\nbool integrate_integratevol(integrandfunction *function, unsigned int nsamples, double *integrationrule, unsigned int dim, double *x[4], unsigned int nquantity, value *quantity[3], value *q, void *ref, double *out) {\n    double xx[dim];\n    double r[nsamples], rout=0;\n    double fout = 0;\n    \n    for (unsigned int i=0; i<nsamples; i++) {\n        double *lambda=integrationrule+5*i;\n        double w = integrationrule[5*i+4];\n        \n        integrate_interpolatepositionvol(dim, x, lambda, xx);\n        if (nquantity) integrate_interpolatequantitiesvol(dim, lambda, nquantity, quantity, q);\n        nf++;\n        if ((*function) (dim, lambda, xx, nquantity, q, ref, &fout)) {\n            r[i] = fout;\n            rout+=w*r[i];\n        } else{\n            return false;\n        }\n        \n    }\n    \n    *out = rout;\n    return true;\n}\n\n/* Subdivision */\nstatic unsigned int vsub[] =  { 1, 4, 7, 8,\n                                0, 4, 7, 9,\n                                0, 4, 8, 9,\n                                4, 7, 8, 9,\n                                0, 5, 7, 9,\n                                0, 6, 8, 9,\n                                2, 5, 7, 9,\n                                3, 6, 8, 9 };\n\nstatic unsigned int nvsub = 8;\n\n/** Integrate over an volume element\n * @param[in] function     - function to integrate\n * @param[in] dim                - Dimension of the vertices\n * @param[in] x                     - vertices of the line x[0] = {x,y,z} etc.\n * @param[in] nquantity   - number of quantities per vertex\n * @param[in] quantity     - List of quantities for each vertex.\n * @param[in] ref                 - a pointer to any data required by the function\n * @param[in] ge                   - Global estimate of the integral (used for recursion).\n * @param[out] out               - estimate of the integral\n * @returns True on success */\nbool integrate_volint(integrandfunction *function, unsigned int dim, double *x[4], unsigned int nquantity, value *quantity[4], value *q, void *ref, unsigned int recursiondepth, double ge, double *out) {\n    double r1, r2, r3;\n    double gest=ge;\n    double af=pow(1.0/nvsub, (double) recursiondepth); // Volume of total tetrahedron calculated from recursion depth\n    \n    if (!integrate_integratevol(function, nv5, v5, dim, x, nquantity, quantity, q, ref, &r1)) return false;\n    if (!integrate_integratevol(function, nv6, v6, dim, x, nquantity, quantity, q, ref, &r2)) return false;\n\n    if (recursiondepth==0) gest=fabs(r2); // If at top level construct a global estimate of the integral\n\n    double eps=r2-r1;\n    eps*=af;\n    if (gest>MORPHO_EPS) eps/=gest; // Globally relative estimate using volume factor\n    \n    if (fabs(eps)<INTEGRATE_ACCURACYGOAL)  { // We converged\n        *out=r2;\n        return true;\n    }\n    \n    // Subdivision strategy\n    double *xn[4]; /* Will hold the vertices. */\n    double x01[dim], x02[dim], x03[dim], x12[dim], x13[dim], x23[dim]; /* New ertices from midpoints */\n    double *xx[] = { x[0], x[1], x[2], x[3], x01, x02, x03, x12, x13, x23 }; // All vertices\n    value q01[nquantity+1], q02[nquantity+1], q03[nquantity+1], q12[nquantity+1], q13[nquantity+1], q23[nquantity+1];\n    value *qq[] = { quantity[0], quantity[1], quantity[2], quantity[3], q01, q02, q03, q12, q13, q23 }; // All vertices\n    value *qn[4];\n    \n    r3=0.0;\n    /* New vertices s*/\n    for (unsigned int i=0; i<dim; i++) {\n        x01[i] = 0.5*(x[0][i]+x[1][i]);\n        x02[i] = 0.5*(x[0][i]+x[2][i]);\n        x03[i] = 0.5*(x[0][i]+x[3][i]);\n        x12[i] = 0.5*(x[1][i]+x[2][i]);\n        x13[i] = 0.5*(x[1][i]+x[3][i]);\n        x23[i] = 0.5*(x[2][i]+x[3][i]);\n    }\n    \n    /* Quantities */\n    if (nquantity) {\n        double ll[4];\n        for (unsigned int i=0; i<nquantity; i++) { q01[i]=MORPHO_NIL; q02[i]=MORPHO_NIL; q03[i]=MORPHO_NIL; q12[i]=MORPHO_NIL; q13[i]=MORPHO_NIL; q23[i]=MORPHO_NIL; }\n        \n        ll[0]=0.5; ll[1]=0.5; ll[2]=0.0; ll[3]=0.0;\n        integrate_interpolatequantitiesvol(dim, ll, nquantity, quantity, q01);\n        ll[0]=0.5; ll[1]=0.0; ll[2]=0.5; ll[3]=0.0;\n        integrate_interpolatequantitiesvol(dim, ll, nquantity, quantity, q02);\n        ll[0]=0.5; ll[1]=0.0; ll[2]=0.0; ll[3]=0.5;\n        integrate_interpolatequantitiesvol(dim, ll, nquantity, quantity, q03);\n        ll[0]=0.0; ll[1]=0.5; ll[2]=0.5; ll[3]=0.0;\n        integrate_interpolatequantitiesvol(dim, ll, nquantity, quantity, q12);\n        ll[0]=0.0; ll[1]=0.5; ll[2]=0.0; ll[3]=0.5;\n        integrate_interpolatequantitiesvol(dim, ll, nquantity, quantity, q13);\n        ll[0]=0.0; ll[1]=0.0; ll[2]=0.5; ll[3]=0.5;\n        integrate_interpolatequantitiesvol(dim, ll, nquantity, quantity, q23);\n    }\n    \n    double rr = 0.0;\n    \n    for (unsigned int i=0; i<nvsub; i++) {\n        double sub;\n        for (unsigned int j=0; j<4; j++) xn[j]=xx[vsub[4*i+j]];\n        if (nquantity) for (unsigned int j=0; j<4; j++) qn[j]=qq[vsub[4*i+j]];\n        \n        if (!integrate_volint(function, dim, xn, nquantity, qn, q, ref, recursiondepth+1, gest, &sub)) goto integrate_volint_cleanup;\n        \n        rr+=sub;\n    }\n    \n    *out=rr/nvsub;\n    \nintegrate_volint_cleanup:\n    \n    return true;\n    \n}\n\n/* **********************************************************************\n * Public interface\n * ********************************************************************** */\n\n/** Integrate over an element - public interface.\n * @param[in] integrand   - integrand\n * @param[in] dim                - Dimension of the vertices\n * @param[in] grade            - Grade to integrate over\n * @param[in] x                     - vertices of the triangle x[0] = {x,y,z} etc.\n * @param[in] nquantity   - number of quantities per vertex\n * @param[in] quantity     - List of quantities for each endpoint.\n * @param[in] ref                - a pointer to any data required by the function\n * @param[out] out              - value of the integral\n * @returns true on success.\n */\nbool integrate_integrate(integrandfunction *integrand, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out) {\n    double result=0.0;\n    value q[nquantity+1];\n    bool success=false;\n    \n    for (unsigned int i=0; i<nquantity; i++) q[i]=MORPHO_NIL;\n    if (quantity) integrate_recognizequantities(nquantity, quantity[0], q);\n    \n    /* Do the integration */\n    switch (grade) {\n        case 1:\n            success=integrate_lineint(integrand, dim, x, nquantity, quantity, q, ref, 0, 0.0, &result);\n            break;\n        case 2:\n            success=integrate_areaint(integrand, dim, x, nquantity, quantity, q, ref, 0, 0.0, &result);\n            break;\n        case 3:\n            success=integrate_volint(integrand, dim, x, nquantity, quantity, q, ref, 0, 0.0, &result);\n            break;\n    }\n    \n    /* Free any quantities allocated */\n    for (unsigned int i=0; i<nquantity; i++) {\n        if (MORPHO_ISOBJECT(q[i])) object_free(MORPHO_GETOBJECT(q[i]));\n    }\n    \n    *out = result;\n    \n    return success;\n}\n\n/* **********************************************************************\n * New integrator\n * ********************************************************************** */\n\n/* **********************************************\n * Quadrature rules\n * ********************************************** */\n\n/* --------------------------------\n * Simple midpoint-simpson rule\n * -------------------------------- */\n\ndouble midpointnodes[] = {\n    0.5, 0.5, // Midpoint\n    \n    0.0, 1.0, // } Simpsons extension\n    1.0, 0.0, // }\n};\n\ndouble midpointweights[] = {\n    1.0\n};\n\ndouble simpsonweights[] = {\n    0.66666666666666667, 0.16666666666666667, 0.16666666666666667\n};\n\nquadraturerule simpson = {\n    .name = \"simpson\",\n    .grade = 1,\n    .order = 3,\n    .nnodes = 1,\n    .nodes = midpointnodes,\n    .weights = simpsonweights,\n    .ext = NULL\n};\n\nquadraturerule midpoint = {\n    .name = \"midpoint\",\n    .grade = 1,\n    .order = 1,\n    .nnodes = 1,\n    .nodes = midpointnodes,\n    .weights = midpointweights,\n    .ext = &simpson\n};\n\n/* --------------------------------\n * Gauss-Kronrod 1-3 rule\n * -------------------------------- */\n\ndouble gk13nds[] = {\n    0.50000000000000000000, 0.50000000000000000000,\n    0.11270166537925831148, 0.88729833462074168852,\n    0.88729833462074168852, 0.11270166537925831148\n};\n\ndouble g1wts[] = {\n    1.0,\n};\n\ndouble k3wts[] = {\n    0.4444444444444444444445, 0.2777777777777777777778,\n    0.277777777777777777778\n};\n\nquadraturerule kronrod3 = {\n    .name = \"kronrod3\",\n    .grade = 1,\n    .order = 3,\n    .nnodes = 3,\n    .nodes = gk13nds,\n    .weights = k3wts,\n    .ext = NULL\n};\n\nquadraturerule gauss1 = {\n    .name = \"gauss1\",\n    .grade = 1,\n    .order = 1,\n    .nnodes = 1,\n    .nodes = gk13nds,\n    .weights = g1wts,\n    .ext = &kronrod3\n};\n\n/* --------------------------------\n * Gauss-Kronrod 2-5 rule\n * -------------------------------- */\n\ndouble gk25nds[] = {\n    0.21132486540518711775, 0.78867513459481288225,\n    0.78867513459481288225, 0.21132486540518711775,\n    \n    0.037089950113724269217, 0.96291004988627573078,\n    0.50000000000000000000, 0.50000000000000000000,\n    0.96291004988627573078, 0.037089950113724269217\n};\n\ndouble gauss2wts[] = {\n    0.5, 0.5, // Gauss weights\n};\n\ndouble kronrod5wts[] = {\n    0.2454545454545454545455, // Kronrod extension\n    0.245454545454545454546,\n    0.098989898989898989899,\n    0.3111111111111111111111,\n    0.098989898989898989899\n};\n\nquadraturerule kronrod5 = {\n    .name = \"kronrod5\",\n    .grade = 1,\n    .order = 7,\n    .nnodes = 5,\n    .nodes = gk25nds,\n    .weights = kronrod5wts,\n    .ext = NULL\n};\n\nquadraturerule gauss2 = {\n    .name = \"gauss2\",\n    .grade = 1,\n    .order = 3,\n    .nnodes = 2,\n    .nodes = gk25nds,\n    .weights = gauss2wts,\n    .ext = &kronrod5\n};\n\n/* --------------------------------\n * Gauss-Kronrod 5-11 rule\n * -------------------------------- */\n\n// Appears to be Mathematica's default integrator!\n\ndouble gk511nds[] = {\n    0.046910077030668003601,  0.9530899229693319964,\n    0.23076534494715845448,   0.76923465505284154552,\n    0.5,0.5,\n    0.76923465505284154552,   0.23076534494715845448,\n    0.9530899229693319964,    0.046910077030668003601,\n    \n    0.0079573199525787677519, 0.99204268004742123225,\n    0.12291663671457538978,   0.87708336328542461022,\n    0.36018479341910840329,   0.63981520658089159671,\n    0.63981520658089159671,   0.36018479341910840329,\n    0.87708336328542461022,   0.12291663671457538978,\n    0.99204268004742123225,   0.0079573199525787677519\n};\n\ndouble gauss5wts[] = {\n    0.118463442528094543757, 0.2393143352496832340206, 0.2844444444444444444444,\n    0.2393143352496832340206, 0.118463442528094543757\n};\n\ndouble kronrod11wts[] = {\n    0.0576166583112366970123, // Kronrod\n    0.12052016961432379335,\n    0.1414937089287456066021,\n    0.12052016961432379335,\n    0.0576166583112366970123,\n    0.02129101837554091643225,\n    0.093400398278246328734,\n    0.136424900956279461171,\n    0.136424900956279461171,\n    0.0934003982782463287339,\n    0.0212910183755409164322\n};\n\nquadraturerule kronrod11 = {\n    .name = \"kronrod11\",\n    .grade = 1,\n    .order = 16,\n    .nnodes = 11,\n    .nodes = gk511nds,\n    .weights = kronrod11wts,\n    .ext = NULL\n};\n\nquadraturerule gauss5 = {\n    .name = \"gauss5\",\n    .grade = 1,\n    .order = 9,\n    .nnodes = 5,\n    .nodes = gk511nds,\n    .weights = gauss5wts,\n    .ext = &kronrod11\n};\n\n/* --------------------------------\n * Gauss-Kronrod 7-15 rule\n * -------------------------------- */\n\ndouble gk715nds[] = {\n    0.0254460438286207377369, 0.9745539561713792622631, // Gauss nodes\n    0.1292344072003027800681, 0.8707655927996972199320,\n    0.2970774243113014165467, 0.7029225756886985834533,\n    0.5, 0.5,\n    0.7029225756886985834533, 0.2970774243113014165467,\n    0.8707655927996972199320, 0.1292344072003027800681,\n    0.9745539561713792622631, 0.0254460438286207377369,\n    \n    0.0042723144395936803966, 0.9957276855604063196035, // Kronrod extension\n    0.0675677883201154636052, 0.9324322116798845363949,\n    0.2069563822661544348530, 0.7930436177338455651471,\n    0.3961075224960507661997, 0.6038924775039492338004,\n    0.6038924775039492338004, 0.3961075224960507661997,\n    0.7930436177338455651471, 0.2069563822661544348530,\n    0.9324322116798845363949, 0.0675677883201154636052,\n    0.9957276855604063196035, 0.0042723144395936803966\n};\n\ndouble gauss7wts[] = {\n    0.0647424830844348466355, // Gauss weights\n    0.1398526957446383339505,\n    0.1909150252525594724752,\n    0.20897959183673469387755,\n    0.1909150252525594724752,\n    0.13985269574463833395075,\n    0.0647424830844348466353\n};\n    \ndouble kronrod15wts[] = {\n    0.0315460463149892766454,\n    0.070326629857762959373,\n    0.0951752890323927049567,\n    0.104741070542363914007,\n    0.095175289032392704957,\n    0.0703266298577629593726,\n    0.0315460463149892766454,\n    \n    0.0114676610052646124819,\n    0.0523950051611250919200,\n    0.0845023633196339514133,\n    0.1022164700376494462071,\n    0.1022164700376494462071,\n    0.0845023633196339514133,\n    0.05239500516112509192,\n    0.01146766100526461248187\n};\n\nquadraturerule kronrod15 = {\n    .name = \"kronrod15\",\n    .grade = 1,\n    .order = 22,\n    .nnodes = 15,\n    .nodes = gk715nds,\n    .weights = kronrod15wts,\n    .ext = NULL\n};\n\nquadraturerule gauss7 = {\n    .name = \"gauss7\",\n    .grade = 1,\n    .order = 13,\n    .nnodes = 7,\n    .nodes = gk715nds,\n    .weights = gauss7wts,\n    .ext = &kronrod15\n};\n\n/* --------------------------------\n * Triangle\n * -------------------------------- */\n\n/* Quadrature rules based on Walkington, \"Quadrature on Simplices of arbitrary dimension\" */\n\ndouble tripts[] = {\n    0.3333333333333333, 0.3333333333333333, 0.3333333333333333,\n    0.6000000000000000, 0.2000000000000000, 0.2000000000000000,\n    0.2000000000000000, 0.6000000000000000, 0.2000000000000000,\n    0.2000000000000000, 0.2000000000000000, 0.6000000000000000,\n    \n    0.7142857142857143, 0.1428571428571429, 0.1428571428571429,\n    0.1428571428571429, 0.7142857142857143, 0.1428571428571429,\n    0.1428571428571429, 0.1428571428571429, 0.7142857142857143,\n    0.4285714285714286, 0.4285714285714286, 0.1428571428571429,\n    0.4285714285714286, 0.1428571428571429, 0.4285714285714286,\n    0.1428571428571429, 0.4285714285714286, 0.4285714285714286,\n    \n    0.7777777777777778, 0.1111111111111111, 0.1111111111111111,\n    0.1111111111111111, 0.7777777777777778, 0.1111111111111111,\n    0.1111111111111111, 0.1111111111111111, 0.7777777777777778,\n    0.3333333333333333, 0.5555555555555556, 0.1111111111111111,\n    0.3333333333333333, 0.1111111111111111, 0.5555555555555556,\n    0.5555555555555556, 0.3333333333333333, 0.1111111111111111,\n    0.5555555555555556, 0.1111111111111111, 0.3333333333333333,\n    0.1111111111111111, 0.3333333333333333, 0.5555555555555556,\n    0.1111111111111111, 0.5555555555555556, 0.3333333333333333,\n    0.3333333333333333, 0.3333333333333333, 0.3333333333333333\n};\n\ndouble tri0wts[] = {\n    1.0\n};\n\ndouble tri4wts[] = {\n    -0.5625, 0.5208333333333332, 0.5208333333333332, 0.5208333333333332\n};\n    \ndouble tri10wts[] = {\n    0.1265625, -0.5425347222222222, -0.5425347222222222, -0.5425347222222222,\n    0.4168402777777778, 0.4168402777777778, 0.4168402777777778, 0.4168402777777778,\n    0.4168402777777778, 0.4168402777777778\n};\n\ndouble tri20wts[] = {\n    -0.0158203125, 0.2422030009920634, 0.2422030009920634, 0.2422030009920634,\n    -0.6382866753472222, -0.6382866753472222, -0.6382866753472222, -0.6382866753472222,\n    -0.6382866753472222, -0.6382866753472222,\n    \n    0.4118931361607142, 0.4118931361607142, 0.4118931361607142, 0.4118931361607142,\n    0.4118931361607142, 0.4118931361607142, 0.4118931361607142, 0.4118931361607142,\n    0.4118931361607142, 0.4118931361607142\n};\n\nquadraturerule tri20 = {\n    .name = \"tri20\",\n    .grade = 2,\n    .order = 8,\n    .nnodes = 20,\n    .nodes = tripts,\n    .weights = tri20wts,\n    .ext = NULL\n};\n\nquadraturerule tri10 = {\n    .name = \"tri10\",\n    .grade = 2,\n    .order = 5,\n    .nnodes = 10,\n    .nodes = tripts,\n    .weights = tri10wts,\n    .ext = &tri20\n};\n\nquadraturerule tri4 = {\n    .name = \"tri4\",\n    .grade = 2,\n    .order = 3, // 4pt rule is order 3,\n    .nnodes = 4,\n    .nodes = tripts,\n    .weights = tri4wts,\n    .ext = &tri10\n};\n\nquadraturerule tri0 = {\n    .name = \"tri0\",\n    .grade = 2,\n    .order = 1, // 1pt rule is order 1,\n    .nnodes = 1,\n    .nodes = tripts,\n    .weights = tri0wts,\n    .ext = &tri4\n};\n\n// CUBTRI rule from D. P. Laurie, ACM Transactions on Mathematical Software, Vol 8, No. 2, June 1982,Pages 210-218\n\ndouble cubtripts[] = {\n    0.333333333333333333,0.333333333333333333,0.333333333333333333,\n    0.797426985353087322,0.101286507323456339,0.101286507323456339,\n    0.101286507323456339,0.101286507323456339,0.797426985353087322,\n    0.101286507323456339,0.797426985353087322,0.101286507323456339,\n    0.0597158717897698205,0.47014206410511509,0.47014206410511509,\n    0.47014206410511509,0.47014206410511509,0.0597158717897698205,\n    0.47014206410511509,0.0597158717897698205,0.47014206410511509,\n    \n    0.941038278231120867,0.0294808608844395667,0.0294808608844395667,\n    0.0294808608844395667,0.0294808608844395667,0.941038278231120867,\n    0.0294808608844395667,0.941038278231120867,0.0294808608844395667,\n    0.535795346449899265,0.232102326775050368,0.232102326775050368,\n    0.232102326775050368,0.232102326775050368,0.535795346449899265,\n    0.232102326775050368,0.535795346449899265,0.232102326775050368,\n    0.0294808608844395667,0.232102326775050368,0.738416812340510066,\n    0.232102326775050368,0.0294808608844395667,0.738416812340510066,\n    0.738416812340510066,0.0294808608844395667,0.232102326775050368,\n    0.738416812340510066,0.232102326775050368,0.0294808608844395667,\n    0.0294808608844395667,0.738416812340510066,0.232102326775050368,\n    0.232102326775050368,0.738416812340510066,0.0294808608844395667\n};\n\ndouble cubtri0wts[] = {\n    1.0\n};\n\ndouble cubtri7wts[] = {\n    0.225000000000000000, 0.125939180544827153, 0.125939180544827153,\n    0.125939180544827153, 0.132394152788506181, 0.132394152788506181,\n    0.132394152788506181\n};\n\ndouble cubtri19wts[] = {\n    0.0378610912003146833, 0.0376204254131829721, 0.0376204254131829721,\n    0.0376204254131829721, 0.0783573522441173376, 0.0783573522441173376,\n    0.0783573522441173376, 0.0134442673751654019, 0.0134442673751654019,\n    0.0134442673751654019, 0.116271479656965896, 0.116271479656965896,\n    0.116271479656965896, 0.0375097224552317488, 0.0375097224552317488,\n    0.0375097224552317488, 0.0375097224552317488, 0.0375097224552317488,\n    0.0375097224552317488\n};\n\nquadraturerule cubtri19 = {\n    .name = \"cubtri19\",\n    .grade = 2,\n    .order = 8,\n    .nnodes = 19,\n    .nodes = cubtripts,\n    .weights = cubtri19wts,\n    .ext = NULL\n};\n\nquadraturerule cubtri7 = {\n    .name = \"cubtri7\",\n    .grade = 2,\n    .order = 5,\n    .nnodes = 7,\n    .nodes = cubtripts,\n    .weights = cubtri7wts,\n    .ext = &cubtri19\n};\n\nquadraturerule cubtri0 = {\n    .name = \"cubtri0\",\n    .grade = 2,\n    .order = 1,\n    .nnodes = 1,\n    .nodes = cubtripts,\n    .weights = cubtri0wts,\n    .ext = &cubtri7\n};\n\n// Grundmann-Möller embedded rules:\n//   SIAM Journal on Numerical Analysis , Apr., 1978, Vol. 15, No. 2 (Apr., 1978), pp. 282-290\n// Computed with simplex_gm_rule [https://people.sc.fsu.edu/~jburkardt/c_src/simplex_gm_rule/simplex_gm_rule.html]\n\ndouble grundmann2dpts[] = {\n    0.3333333333333333, 0.3333333333333333, 0.3333333333333334,\n    0.2, 0.6, 0.2,\n    0.6, 0.2, 0.2,\n    0.2, 0.2, 0.6,\n    \n    // Additional pts for rule 2 and above\n    0.1428571428571428, 0.7142857142857143, 0.1428571428571428,\n    0.4285714285714285, 0.4285714285714285, 0.1428571428571429,\n    0.1428571428571428, 0.4285714285714285, 0.4285714285714286,\n    0.7142857142857143, 0.1428571428571428, 0.1428571428571428,\n    0.4285714285714285, 0.1428571428571428, 0.4285714285714286,\n    0.1428571428571428, 0.1428571428571428, 0.7142857142857143,\n    \n    // Additional pts for rule 3 and above\n    0.1111111111111111, 0.7777777777777778, 0.1111111111111112,\n    0.3333333333333333, 0.5555555555555556, 0.1111111111111112,\n    0.1111111111111111, 0.5555555555555556, 0.3333333333333333,\n    0.5555555555555556, 0.3333333333333333, 0.1111111111111112,\n    0.3333333333333333, 0.3333333333333333, 0.3333333333333334,\n    0.1111111111111111, 0.3333333333333333, 0.5555555555555556,\n    0.7777777777777778, 0.1111111111111111, 0.1111111111111112,\n    0.5555555555555556, 0.1111111111111111, 0.3333333333333333,\n    0.3333333333333333, 0.1111111111111111, 0.5555555555555556,\n    0.1111111111111111, 0.1111111111111111, 0.7777777777777778,\n    \n    // Additional pts for rule 4 and above\n    0.09090909090909091, 0.8181818181818182, 0.09090909090909083,\n    0.2727272727272727, 0.6363636363636364, 0.09090909090909094,\n    0.09090909090909091, 0.6363636363636364, 0.2727272727272727,\n    0.4545454545454545, 0.4545454545454545, 0.09090909090909094,\n    0.2727272727272727, 0.4545454545454545, 0.2727272727272727,\n    0.09090909090909091, 0.4545454545454545, 0.4545454545454546,\n    0.6363636363636364, 0.2727272727272727, 0.09090909090909094,\n    0.4545454545454545, 0.2727272727272727, 0.2727272727272727,\n    0.2727272727272727, 0.2727272727272727, 0.4545454545454546,\n    0.09090909090909091, 0.2727272727272727, 0.6363636363636364,\n    0.8181818181818182, 0.09090909090909091, 0.09090909090909083,\n    0.6363636363636364, 0.09090909090909091, 0.2727272727272727,\n    0.4545454545454545, 0.09090909090909091, 0.4545454545454546,\n    0.2727272727272727, 0.09090909090909091, 0.6363636363636364,\n    0.09090909090909091, 0.09090909090909091, 0.8181818181818181,\n    \n    // Additional pts for rule 5 and above\n    0.07692307692307693, 0.8461538461538461, 0.07692307692307687,\n    0.2307692307692308, 0.6923076923076923, 0.07692307692307687,\n    0.07692307692307693, 0.6923076923076923, 0.2307692307692308,\n    0.3846153846153846, 0.5384615384615384, 0.07692307692307687,\n    0.2307692307692308, 0.5384615384615384, 0.2307692307692308,\n    0.07692307692307693, 0.5384615384615384, 0.3846153846153846,\n    0.5384615384615384, 0.3846153846153846, 0.07692307692307687,\n    0.3846153846153846, 0.3846153846153846, 0.2307692307692307,\n    0.2307692307692308, 0.3846153846153846, 0.3846153846153846,\n    0.07692307692307693, 0.3846153846153846, 0.5384615384615384,\n    0.6923076923076923, 0.2307692307692308, 0.07692307692307687,\n    0.5384615384615384, 0.2307692307692308, 0.2307692307692308,\n    0.3846153846153846, 0.2307692307692308, 0.3846153846153846,\n    0.2307692307692308, 0.2307692307692308, 0.5384615384615384,\n    0.07692307692307693, 0.2307692307692308, 0.6923076923076923,\n    0.8461538461538461, 0.07692307692307693, 0.07692307692307687,\n    0.6923076923076923, 0.07692307692307693, 0.2307692307692308,\n    0.5384615384615384, 0.07692307692307693, 0.3846153846153846,\n    0.3846153846153846, 0.07692307692307693, 0.5384615384615384,\n    0.2307692307692308, 0.07692307692307693, 0.6923076923076923,\n    0.07692307692307693, 0.07692307692307693, 0.8461538461538461,\n};\n\ndouble grundmann2d0wts[] = {\n    1.0\n};\n\ndouble grundmann2d1wts[] = {\n    -0.5625, 0.5208333333333333, 0.5208333333333333, 0.5208333333333333\n};\n\ndouble grundmann2d2wts[] = {\n    0.1265625, -0.5425347222222222, -0.5425347222222222, -0.5425347222222222,\n    0.4168402777777778, 0.4168402777777778, 0.4168402777777778, 0.4168402777777778,\n    0.4168402777777778, 0.4168402777777778\n};\n\ndouble grundmann2d3wts[] = {\n    -0.0158203125, 0.2422030009920635, 0.2422030009920635, 0.2422030009920635,\n    -0.6382866753472222, -0.6382866753472222, -0.6382866753472222, -0.6382866753472222,\n    -0.6382866753472222, -0.6382866753472222, 0.4118931361607143, 0.4118931361607143,\n    0.4118931361607143, 0.4118931361607143, 0.4118931361607143, 0.4118931361607143,\n    0.4118931361607143, 0.4118931361607143, 0.4118931361607143, 0.4118931361607143\n};\n\ndouble grundmann2d4wts[] = {\n    0.001271275111607143, -0.06307369817501654, -0.06307369817501654, -0.06307369817501654,\n    0.4343895429446373, 0.4343895429446373, 0.4343895429446373, 0.4343895429446373,\n    0.4343895429446373, 0.4343895429446373, -0.8340836007254465, -0.8340836007254465,\n    -0.8340836007254465, -0.8340836007254465, -0.8340836007254465, -0.8340836007254465,\n    -0.8340836007254465, -0.8340836007254465, -0.8340836007254465, -0.8340836007254465,\n    0.4614965712666722, 0.4614965712666722, 0.4614965712666722, 0.4614965712666722,\n    0.4614965712666722, 0.4614965712666722, 0.4614965712666722, 0.4614965712666722,\n    0.4614965712666722, 0.4614965712666722, 0.4614965712666722, 0.4614965712666722,\n    0.4614965712666722, 0.4614965712666722, 0.4614965712666722\n};\n\ndouble grundmann2d5wts[] = {\n    -7.150922502790179e-05, 0.01095029482205148, 0.01095029482205148, 0.01095029482205148,\n    -0.1773757300357269, -0.1773757300357269, -0.1773757300357269, -0.1773757300357269,\n    -0.1773757300357269, -0.1773757300357269, 0.7677360415768315, 0.7677360415768315,\n    0.7677360415768315, 0.7677360415768315, 0.7677360415768315, 0.7677360415768315,\n    0.7677360415768315, 0.7677360415768315, 0.7677360415768315, 0.7677360415768315,\n    -1.16335594006807, -1.16335594006807, -1.16335594006807, -1.16335594006807, \n    -1.16335594006807, -1.16335594006807, -1.16335594006807, -1.16335594006807,\n    -1.16335594006807, -1.16335594006807, -1.16335594006807, -1.16335594006807,\n    -1.16335594006807, -1.16335594006807, -1.16335594006807, 0.5621168423917127,\n    0.5621168423917127, 0.5621168423917127, 0.5621168423917127, 0.5621168423917127,\n    0.5621168423917127, 0.5621168423917127, 0.5621168423917127, 0.5621168423917127,\n    0.5621168423917127, 0.5621168423917127, 0.5621168423917127, 0.5621168423917127,\n    0.5621168423917127, 0.5621168423917127, 0.5621168423917127, 0.5621168423917127,\n    0.5621168423917127, 0.5621168423917127, 0.5621168423917127, 0.5621168423917127\n};\n\nquadraturerule grundmann2d5 = {\n    .name = \"grundmann2d5\",\n    .grade = 2,\n    .order = 11,\n    .nnodes = 56,\n    .nodes = grundmann2dpts,\n    .weights = grundmann2d5wts,\n    .ext = NULL\n};\n\nquadraturerule grundmann2d4 = {\n    .name = \"grundmann2d4\",\n    .grade = 2,\n    .order = 9,\n    .nnodes = 35,\n    .nodes = grundmann2dpts,\n    .weights = grundmann2d4wts,\n    .ext = &grundmann2d5\n};\n\nquadraturerule grundmann2d3 = {\n    .name = \"grundmann2d3\",\n    .grade = 2,\n    .order = 7,\n    .nnodes = 20,\n    .nodes = grundmann2dpts,\n    .weights = grundmann2d3wts,\n    .ext = &grundmann2d4\n};\n\nquadraturerule grundmann2d2 = {\n    .name = \"grundmann2d2\",\n    .grade = 2,\n    .order = 5,\n    .nnodes = 10,\n    .nodes = grundmann2dpts,\n    .weights = grundmann2d2wts,\n    .ext = &grundmann2d3\n};\n\nquadraturerule grundmann2d1 = {\n    .name = \"grundmann2d1\",\n    .grade = 2,\n    .order = 3,\n    .nnodes = 4,\n    .nodes = grundmann2dpts,\n    .weights = grundmann2d1wts,\n    .ext = &grundmann2d2\n};\n\nquadraturerule grundmann2d0 = {\n    .name = \"grundmann2d0\",\n    .grade = 2,\n    .order = 1,\n    .nnodes = 1,\n    .nodes = grundmann2dpts,\n    .weights = grundmann2d0wts,\n    .ext = &grundmann2d1\n};\n\n/* --------------------------------\n * Tetrahedron\n * -------------------------------- */\n\n// Nodes and weights from Keast, Computer Methods in Applied Mechanics and Engineering,\n//    Volume 55, Number 3, May 1986, pages 339-348.\n\ndouble keast4pts[] = {\n    0.25,0.25,0.25,0.25,\n    0.78571428571428571,  0.071428571428571428, 0.071428571428571428, 0.071428571428571428,\n    0.071428571428571428, 0.78571428571428571,  0.071428571428571428, 0.071428571428571428,\n    0.071428571428571428, 0.071428571428571428, 0.78571428571428571,  0.071428571428571428,\n    0.071428571428571428, 0.071428571428571428, 0.071428571428571428, 0.78571428571428571,\n    0.39940357616679922,  0.39940357616679922,  0.10059642383320078,  0.10059642383320078,\n    0.39940357616679922,  0.10059642383320078,  0.39940357616679922,  0.10059642383320078,\n    0.39940357616679922,  0.10059642383320078,  0.10059642383320078,  0.39940357616679922,\n    0.10059642383320078,  0.39940357616679922,  0.39940357616679922,  0.10059642383320078,\n    0.10059642383320078,  0.39940357616679922,  0.10059642383320078,  0.39940357616679922,\n    0.10059642383320078,  0.10059642383320078,  0.39940357616679922,  0.39940357616679922\n};\n\ndouble keast4wts[] = {\n    -0.07893333333333333,\n    0.04573333333333333333,0.04573333333333333333,\n    0.04573333333333333333,0.04573333333333333333,\n    0.149333333333333328,0.149333333333333328,0.149333333333333328,0.149333333333333328,0.149333333333333328,0.149333333333333328\n};\n\nquadraturerule keast4 = {\n    .name = \"keast4\",\n    .grade = 3,\n    .order = 4,\n    .nnodes = 11,\n    .nodes = keast4pts,\n    .weights = keast4wts,\n    .ext = NULL\n};\n\ndouble keast5pts[] = {\n    0.25,0.25,0.25,0.25,\n    0,0.3333333333333333,0.3333333333333333,0.3333333333333333,\n    0.3333333333333333,0,0.3333333333333333,0.3333333333333333,\n    0.3333333333333333,0.3333333333333333,0,0.3333333333333333,\n    0.3333333333333333,0.3333333333333333,0.3333333333333333,0,\n    0.72727272727272727,0.090909090909090909,0.090909090909090909,0.090909090909090909,\n    0.090909090909090909,0.72727272727272727,0.090909090909090909,0.090909090909090909,\n    0.090909090909090909,0.090909090909090909,0.72727272727272727,0.090909090909090909,\n    0.090909090909090909,0.090909090909090909,0.090909090909090909,0.72727272727272727,\n    0.066550153573664281,0.066550153573664281,0.43344984642633573,0.43344984642633573,\n    0.066550153573664281,0.43344984642633573,0.066550153573664281,0.43344984642633573,\n    0.066550153573664281,0.43344984642633573,0.43344984642633573,0.066550153573664281,\n    0.43344984642633573,0.066550153573664281,0.066550153573664281,0.43344984642633573,\n    0.43344984642633573,0.066550153573664281,0.43344984642633573,0.066550153573664281,\n    0.43344984642633573,0.43344984642633573,0.066550153573664281,0.066550153573664281\n};\n\ndouble keast5wts[] = {\n    0.181702068582535114,\n    0.0361607142857142958, 0.0361607142857142958, 0.0361607142857142958,\n    0.0361607142857142958, 0.069871494516173845,\n    \n    0.069871494516173845,0.069871494516173845,0.069871494516173845,0.06569484936831872,\n    0.06569484936831872,0.06569484936831872,0.06569484936831872,0.06569484936831872,\n    0.06569484936831872\n};\n\nquadraturerule keast5 = {\n    .name = \"keast5\",\n    .grade = 3,\n    .order = 5,\n    .nnodes = 15,\n    .nodes = keast5pts,\n    .weights = keast5wts,\n    .ext = NULL\n};\n\n// Nodes and weights from Journal of Computational and Applied Mathematics, 236, 17, 4348-4364 (2012)\ndouble tet5pts[] = {\n    0.91978967333688,0.0267367755543735,0.0267367755543735,0.0267367755543735,\n    0.0267367755543735,0.91978967333688,0.0267367755543735,0.0267367755543735,\n    0.0267367755543735,0.0267367755543735,0.91978967333688,0.0267367755543735,\n    0.0267367755543735,0.0267367755543735,0.0267367755543735,0.91978967333688,\n    0.174035630246894,0.747759888481809,0.0391022406356488,0.0391022406356488,\n    0.747759888481809,0.174035630246894,0.0391022406356488,0.0391022406356488,\n    0.174035630246894,0.0391022406356488,0.747759888481809,0.0391022406356488,\n    0.747759888481809,0.0391022406356488,0.174035630246894,0.0391022406356488,\n    0.174035630246894,0.0391022406356488,0.0391022406356488,0.747759888481809,\n    0.747759888481809,0.0391022406356488,0.0391022406356488,0.174035630246894,\n    0.0391022406356488,0.174035630246894,0.747759888481809,0.0391022406356488,\n    0.0391022406356488,0.747759888481809,0.174035630246894,0.0391022406356488,\n    0.0391022406356488,0.174035630246894,0.0391022406356488,0.747759888481809,\n    0.0391022406356488,0.747759888481809,0.0391022406356488,0.174035630246894,\n    0.0391022406356488,0.0391022406356488,0.174035630246894,0.747759888481809,\n    0.0391022406356488,0.0391022406356488,0.747759888481809,0.174035630246894,\n    0.454754599984483,0.454754599984483,0.0452454000155172,0.0452454000155172,\n    0.454754599984483,0.0452454000155172,0.454754599984483,0.0452454000155172,\n    0.454754599984483,0.0452454000155172,0.0452454000155172,0.454754599984483,\n    0.0452454000155172,0.454754599984483,0.454754599984483,0.0452454000155172,\n    0.0452454000155172,0.454754599984483,0.0452454000155172,0.454754599984483,\n    0.0452454000155172,0.0452454000155172,0.454754599984483,0.454754599984483,\n    0.503118645014598,0.223201037962315,0.223201037962315,0.050479279060772,\n    0.223201037962315,0.503118645014598,0.223201037962315,0.050479279060772,\n    0.223201037962315,0.223201037962315,0.503118645014598,0.050479279060772,\n    0.503118645014598,0.223201037962315,0.050479279060772,0.223201037962315,\n    0.223201037962315,0.503118645014598,0.050479279060772,0.223201037962315,\n    0.223201037962315,0.223201037962315,0.050479279060772,0.503118645014598,\n    0.503118645014598,0.050479279060772,0.223201037962315,0.223201037962315,\n    0.223201037962315,0.050479279060772,0.503118645014598,0.223201037962315,\n    0.223201037962315,0.050479279060772,0.223201037962315,0.503118645014598,\n    0.050479279060772,0.503118645014598,0.223201037962315,0.223201037962315,\n    0.050479279060772,0.223201037962315,0.503118645014598,0.223201037962315,\n    0.050479279060772,0.223201037962315,0.223201037962315,0.503118645014598,\n    0.25,0.25,0.25,0.25\n};\n\ndouble tet5wts[] = {\n    0.0021900463965388,0.0021900463965388,0.0021900463965388,0.0021900463965388,\n    0.0143395670177665,0.0143395670177665,0.0143395670177665,0.0143395670177665,\n    0.0143395670177665,0.0143395670177665,0.0143395670177665,0.0143395670177665,\n    0.0143395670177665,0.0143395670177665,0.0143395670177665,0.0143395670177665,\n    0.0250305395686746,0.0250305395686746,0.0250305395686746,0.0250305395686746,\n    0.0250305395686746,0.0250305395686746,0.0479839333057554,0.0479839333057554,\n    0.0479839333057554,0.0479839333057554,0.0479839333057554,0.0479839333057554,\n    0.0479839333057554,0.0479839333057554,0.0479839333057554,0.0479839333057554,\n    0.0479839333057554,0.0479839333057554,0.093174573119534 };\n\nquadraturerule tet5 = {\n    .name = \"tet5\",\n    .grade = 3,\n    .order = 7,\n    .nnodes = 35,\n    .nodes = tet5pts,\n    .weights = tet5wts,\n    .ext = NULL\n};\n\ndouble tet6pts[] = {\n    0.955143804540822,0.0149520651530592,0.0149520651530592,0.0149520651530592,\n    0.0149520651530592,0.955143804540822,0.0149520651530592,0.0149520651530592,\n    0.0149520651530592,0.0149520651530592,0.955143804540822,0.0149520651530592,\n    0.0149520651530592,0.0149520651530592,0.0149520651530592,0.955143804540822,\n    0.77997600844154,0.151831949165937,0.0340960211962615,0.0340960211962615,\n    0.151831949165937,0.77997600844154,0.0340960211962615,0.0340960211962615,\n    0.77997600844154,0.0340960211962615,0.151831949165937,0.0340960211962615,\n    0.151831949165937,0.0340960211962615,0.77997600844154,0.0340960211962615,\n    0.77997600844154,0.0340960211962615,0.0340960211962615,0.151831949165937,\n    0.151831949165937,0.0340960211962615,0.0340960211962615,0.77997600844154,\n    0.0340960211962615,0.77997600844154,0.151831949165937,0.0340960211962615,\n    0.0340960211962615,0.151831949165937,0.77997600844154,0.0340960211962615,\n    0.0340960211962615,0.77997600844154,0.0340960211962615,0.151831949165937,\n    0.0340960211962615,0.151831949165937,0.0340960211962615,0.77997600844154,\n    0.0340960211962615,0.0340960211962615,0.77997600844154,0.151831949165937,\n    0.0340960211962615,0.0340960211962615,0.151831949165937,0.77997600844154,\n    0.354934056063979,0.552655643106017,0.0462051504150017,0.0462051504150017,\n    0.552655643106017,0.354934056063979,0.0462051504150017,0.0462051504150017,\n    0.354934056063979,0.0462051504150017,0.552655643106017,0.0462051504150017,\n    0.552655643106017,0.0462051504150017,0.354934056063979,0.0462051504150017,\n    0.354934056063979,0.0462051504150017,0.0462051504150017,0.552655643106017,\n    0.552655643106017,0.0462051504150017,0.0462051504150017,0.354934056063979,\n    0.0462051504150017,0.354934056063979,0.552655643106017,0.0462051504150017,\n    0.0462051504150017,0.552655643106017,0.354934056063979,0.0462051504150017,\n    0.0462051504150017,0.354934056063979,0.0462051504150017,0.552655643106017,\n    0.0462051504150017,0.552655643106017,0.0462051504150017,0.354934056063979,\n    0.0462051504150017,0.0462051504150017,0.354934056063979,0.552655643106017,\n    0.0462051504150017,0.0462051504150017,0.552655643106017,0.354934056063979,\n    0.538104322888002,0.228190461068761,0.228190461068761,0.0055147549744775,\n    0.228190461068761,0.538104322888002,0.228190461068761,0.0055147549744775,\n    0.228190461068761,0.228190461068761,0.538104322888002,0.0055147549744775,\n    0.538104322888002,0.228190461068761,0.0055147549744775,0.228190461068761,\n    0.228190461068761,0.538104322888002,0.0055147549744775,0.228190461068761,\n    0.228190461068761,0.228190461068761,0.0055147549744775,0.538104322888002,\n    0.538104322888002,0.0055147549744775,0.228190461068761,0.228190461068761,\n    0.228190461068761,0.0055147549744775,0.538104322888002,0.228190461068761,\n    0.228190461068761,0.0055147549744775,0.228190461068761,0.538104322888002,\n    0.0055147549744775,0.538104322888002,0.228190461068761,0.228190461068761,\n    0.0055147549744775,0.228190461068761,0.538104322888002,0.228190461068761,\n    0.0055147549744775,0.228190461068761,0.228190461068761,0.538104322888002,\n    0.19618375957456,0.352305260087994,0.352305260087994,0.099205720249453,\n    0.352305260087994,0.19618375957456,0.352305260087994,0.099205720249453,\n    0.352305260087994,0.352305260087994,0.19618375957456,0.099205720249453,\n    0.19618375957456,0.352305260087994,0.099205720249453,0.352305260087994,\n    0.352305260087994,0.19618375957456,0.099205720249453,0.352305260087994,\n    0.352305260087994,0.352305260087994,0.099205720249453,0.19618375957456,\n    0.19618375957456,0.099205720249453,0.352305260087994,0.352305260087994,\n    0.352305260087994,0.099205720249453,0.19618375957456,0.352305260087994,\n    0.352305260087994,0.099205720249453,0.352305260087994,0.19618375957456,\n    0.099205720249453,0.19618375957456,0.352305260087994,0.352305260087994,\n    0.099205720249453,0.352305260087994,0.19618375957456,0.352305260087994,\n    0.099205720249453,0.352305260087994,0.352305260087994,0.19618375957456,\n    0.596564995621017,0.134478334792994,0.134478334792994,0.134478334792994,\n    0.134478334792994,0.596564995621017,0.134478334792994,0.134478334792994,\n    0.134478334792994,0.134478334792994,0.596564995621017,0.134478334792994,\n    0.134478334792994,0.134478334792994,0.134478334792994,0.596564995621017\n};\n\ndouble tet6wts[] = {\n    0.001037311233614,0.001037311233614,0.001037311233614,0.001037311233614,\n    0.009601664539948,0.009601664539948,0.009601664539948,0.009601664539948,\n    0.009601664539948,0.009601664539948,0.009601664539948,0.009601664539948,\n    0.009601664539948,0.009601664539948,0.009601664539948,0.009601664539948,\n    0.0164493976798232,0.0164493976798232,0.0164493976798232,0.0164493976798232,\n    0.0164493976798232,0.0164493976798232,0.0164493976798232,0.0164493976798232,\n    0.0164493976798232,0.0164493976798232,0.0164493976798232,0.0164493976798232,\n    0.015374776651331,0.015374776651331,0.015374776651331,0.015374776651331,\n    0.015374776651331,0.015374776651331,0.015374776651331,0.015374776651331,\n    0.015374776651331,0.015374776651331,0.015374776651331,0.015374776651331,\n    0.029352011837523,0.029352011837523,0.029352011837523,0.029352011837523,\n    0.029352011837523,0.029352011837523,0.029352011837523,0.029352011837523,\n    0.029352011837523,0.029352011837523,0.029352011837523,0.029352011837523,\n    0.0366291366405108,0.0366291366405108,0.0366291366405108,0.0366291366405108\n};\n\nquadraturerule tet6 = {\n    .name = \"tet6\",\n    .grade = 3,\n    .order = 9,\n    .nnodes = 56,\n    .nodes = tet6pts,\n    .weights = tet6wts,\n    .ext = NULL\n};\n\n// Grundmann-Möller embedded rules:\n//   SIAM Journal on Numerical Analysis , Apr., 1978, Vol. 15, No. 2 (Apr., 1978), pp. 282-290\n// See also a very clear example presented in\n//   ACM Transactions on Mathematical Software, Volume 29, Issue 3, pp 297–308 (2003) */\n\ndouble grundmann3dpts[] = {\n    // Rule 1, order 3\n    0.25,0.25,0.25,0.25,\n    0.16666666666666666667,0.16666666666666666667,0.16666666666666666667,0.5,\n    0.16666666666666666667,0.16666666666666666667,0.5,0.16666666666666666667,\n    0.16666666666666666667,0.5,0.16666666666666666667,0.16666666666666666667,\n    0.5,0.16666666666666666667,0.16666666666666666667,0.16666666666666666667,\n    \n    // Additional points for rule 2, order 5\n    0.125,0.125,0.375,0.375,\n    0.125,0.375,0.125,0.375,\n    0.125,0.375,0.375,0.125,\n    0.375,0.125,0.125,0.375,\n    0.375,0.125,0.375,0.125,\n    0.375,0.375,0.125,0.125,\n    0.125,0.125,0.125,0.625,\n    0.125,0.125,0.625,0.125,\n    0.125,0.625,0.125,0.125,\n    0.625,0.125,0.125,0.125,\n    \n    // Additional points for rule 3, order 7\n    0.1,0.3,0.3,0.3,\n    0.3,0.1,0.3,0.3,\n    0.3,0.3,0.1,0.3,\n    0.3,0.3,0.3,0.1,\n    0.1,0.1,0.3,0.5,\n    0.1,0.1,0.5,0.3,\n    0.1,0.3,0.1,0.5,\n    0.1,0.3,0.5,0.1,\n    0.1,0.5,0.1,0.3,\n    0.1,0.5,0.3,0.1,\n    0.3,0.1,0.1,0.5,\n    0.3,0.1,0.5,0.1,\n    0.3,0.5,0.1,0.1,\n    0.5,0.1,0.1,0.3,\n    0.5,0.1,0.3,0.1,\n    0.5,0.3,0.1,0.1,\n    0.1,0.1,0.1,0.7,\n    0.1,0.1,0.7,0.1,\n    0.1,0.7,0.1,0.1,\n    0.7,0.1,0.1,0.1,\n    \n    // Additional points for rule 4, order 9\n    0.25,0.25,0.25,0.25,0.083333333333333333333,0.25,0.25,\n       0.41666666666666666667,0.083333333333333333333,0.25,0.41666666666666666667,0.25,\n       0.083333333333333333333,0.41666666666666666667,0.25,0.25,0.25,\n       0.083333333333333333333,0.25,0.41666666666666666667,0.25,0.083333333333333333333,\n       0.41666666666666666667,0.25,0.25,0.25,0.083333333333333333333,\n       0.41666666666666666667,0.25,0.25,0.41666666666666666667,0.083333333333333333333,\n       0.25,0.41666666666666666667,0.083333333333333333333,0.25,0.25,\n       0.41666666666666666667,0.25,0.083333333333333333333,0.41666666666666666667,\n       0.083333333333333333333,0.25,0.25,0.41666666666666666667,0.25,\n       0.083333333333333333333,0.25,0.41666666666666666667,0.25,0.25,\n       0.083333333333333333333,0.083333333333333333333,0.083333333333333333333,\n       0.41666666666666666667,0.41666666666666666667,0.083333333333333333333,\n       0.41666666666666666667,0.083333333333333333333,0.41666666666666666667,\n       0.083333333333333333333,0.41666666666666666667,0.41666666666666666667,\n       0.083333333333333333333,0.41666666666666666667,0.083333333333333333333,\n       0.083333333333333333333,0.41666666666666666667,0.41666666666666666667,\n       0.083333333333333333333,0.41666666666666666667,0.083333333333333333333,\n       0.41666666666666666667,0.41666666666666666667,0.083333333333333333333,\n       0.083333333333333333333,0.083333333333333333333,0.083333333333333333333,0.25,\n       0.58333333333333333333,0.083333333333333333333,0.083333333333333333333,\n       0.58333333333333333333,0.25,0.083333333333333333333,0.25,0.083333333333333333333,\n       0.58333333333333333333,0.083333333333333333333,0.25,0.58333333333333333333,\n       0.083333333333333333333,0.083333333333333333333,0.58333333333333333333,\n       0.083333333333333333333,0.25,0.083333333333333333333,0.58333333333333333333,0.25,\n       0.083333333333333333333,0.25,0.083333333333333333333,0.083333333333333333333,\n       0.58333333333333333333,0.25,0.083333333333333333333,0.58333333333333333333,\n       0.083333333333333333333,0.25,0.58333333333333333333,0.083333333333333333333,\n       0.083333333333333333333,0.58333333333333333333,0.083333333333333333333,\n       0.083333333333333333333,0.25,0.58333333333333333333,0.083333333333333333333,0.25,\n       0.083333333333333333333,0.58333333333333333333,0.25,0.083333333333333333333,\n       0.083333333333333333333,0.083333333333333333333,0.083333333333333333333,\n       0.083333333333333333333,0.75,0.083333333333333333333,0.083333333333333333333,0.75,\n       0.083333333333333333333,0.083333333333333333333,0.75,0.083333333333333333333,\n       0.083333333333333333333,0.75,0.083333333333333333333,0.083333333333333333333,\n       0.083333333333333333333,\n    \n    // Additional points for rule 5, order 11\n       0.21428571428571428571,0.21428571428571428571,\n       0.21428571428571428571,0.35714285714285714286,0.21428571428571428571,\n       0.21428571428571428571,0.35714285714285714286,0.21428571428571428571,\n       0.21428571428571428571,0.35714285714285714286,0.21428571428571428571,\n       0.21428571428571428571,0.35714285714285714286,0.21428571428571428571,\n       0.21428571428571428571,0.21428571428571428571,0.071428571428571428571,\n       0.21428571428571428571,0.35714285714285714286,0.35714285714285714286,\n       0.071428571428571428571,0.35714285714285714286,0.21428571428571428571,\n       0.35714285714285714286,0.071428571428571428571,0.35714285714285714286,\n       0.35714285714285714286,0.21428571428571428571,0.21428571428571428571,\n       0.071428571428571428571,0.35714285714285714286,0.35714285714285714286,\n       0.21428571428571428571,0.35714285714285714286,0.071428571428571428571,\n       0.35714285714285714286,0.21428571428571428571,0.35714285714285714286,\n       0.35714285714285714286,0.071428571428571428571,0.35714285714285714286,\n       0.071428571428571428571,0.21428571428571428571,0.35714285714285714286,\n       0.35714285714285714286,0.071428571428571428571,0.35714285714285714286,\n       0.21428571428571428571,0.35714285714285714286,0.21428571428571428571,\n       0.071428571428571428571,0.35714285714285714286,0.35714285714285714286,\n       0.21428571428571428571,0.35714285714285714286,0.071428571428571428571,\n       0.35714285714285714286,0.35714285714285714286,0.071428571428571428571,\n       0.21428571428571428571,0.35714285714285714286,0.35714285714285714286,\n       0.21428571428571428571,0.071428571428571428571,0.071428571428571428571,\n       0.21428571428571428571,0.21428571428571428571,0.5,0.071428571428571428571,\n       0.21428571428571428571,0.5,0.21428571428571428571,0.071428571428571428571,0.5,\n       0.21428571428571428571,0.21428571428571428571,0.21428571428571428571,\n       0.071428571428571428571,0.21428571428571428571,0.5,0.21428571428571428571,\n       0.071428571428571428571,0.5,0.21428571428571428571,0.21428571428571428571,\n       0.21428571428571428571,0.071428571428571428571,0.5,0.21428571428571428571,\n       0.21428571428571428571,0.5,0.071428571428571428571,0.21428571428571428571,0.5,\n       0.071428571428571428571,0.21428571428571428571,0.21428571428571428571,0.5,\n       0.21428571428571428571,0.071428571428571428571,0.5,0.071428571428571428571,\n       0.21428571428571428571,0.21428571428571428571,0.5,0.21428571428571428571,\n       0.071428571428571428571,0.21428571428571428571,0.5,0.21428571428571428571,\n       0.21428571428571428571,0.071428571428571428571,0.071428571428571428571,\n       0.071428571428571428571,0.35714285714285714286,0.5,0.071428571428571428571,\n       0.071428571428571428571,0.5,0.35714285714285714286,0.071428571428571428571,\n       0.35714285714285714286,0.071428571428571428571,0.5,0.071428571428571428571,\n       0.35714285714285714286,0.5,0.071428571428571428571,0.071428571428571428571,0.5,\n       0.071428571428571428571,0.35714285714285714286,0.071428571428571428571,0.5,\n       0.35714285714285714286,0.071428571428571428571,0.35714285714285714286,\n       0.071428571428571428571,0.071428571428571428571,0.5,0.35714285714285714286,\n       0.071428571428571428571,0.5,0.071428571428571428571,0.35714285714285714286,0.5,\n       0.071428571428571428571,0.071428571428571428571,0.5,0.071428571428571428571,\n       0.071428571428571428571,0.35714285714285714286,0.5,0.071428571428571428571,\n       0.35714285714285714286,0.071428571428571428571,0.5,0.35714285714285714286,\n       0.071428571428571428571,0.071428571428571428571,0.071428571428571428571,\n       0.071428571428571428571,0.21428571428571428571,0.64285714285714285714,\n       0.071428571428571428571,0.071428571428571428571,0.64285714285714285714,\n       0.21428571428571428571,0.071428571428571428571,0.21428571428571428571,\n       0.071428571428571428571,0.64285714285714285714,0.071428571428571428571,\n       0.21428571428571428571,0.64285714285714285714,0.071428571428571428571,\n       0.071428571428571428571,0.64285714285714285714,0.071428571428571428571,\n       0.21428571428571428571,0.071428571428571428571,0.64285714285714285714,\n       0.21428571428571428571,0.071428571428571428571,0.21428571428571428571,\n       0.071428571428571428571,0.071428571428571428571,0.64285714285714285714,\n       0.21428571428571428571,0.071428571428571428571,0.64285714285714285714,\n       0.071428571428571428571,0.21428571428571428571,0.64285714285714285714,\n       0.071428571428571428571,0.071428571428571428571,0.64285714285714285714,\n       0.071428571428571428571,0.071428571428571428571,0.21428571428571428571,\n       0.64285714285714285714,0.071428571428571428571,0.21428571428571428571,\n       0.071428571428571428571,0.64285714285714285714,0.21428571428571428571,\n       0.071428571428571428571,0.071428571428571428571,0.071428571428571428571,\n       0.071428571428571428571,0.071428571428571428571,0.78571428571428571429,\n       0.071428571428571428571,0.071428571428571428571,0.78571428571428571429,\n       0.071428571428571428571,0.071428571428571428571,0.78571428571428571429,\n       0.071428571428571428571,0.071428571428571428571,0.78571428571428571429,\n       0.071428571428571428571,0.071428571428571428571,0.071428571428571428571\n};\n\ndouble grundmann3d0wts[] = {\n    1.0\n};\n\ndouble grundmann3d1wts[] = {\n    -0.8,0.45,0.45,0.45,0.45\n};\n\ndouble grundmann3d2wts[] = {\n    0.26666666666666666667,-0.57857142857142857143,-0.57857142857142857143,\n    -0.57857142857142857143,-0.57857142857142857143,0.3047619047619047619,\n    0.3047619047619047619,0.3047619047619047619,0.3047619047619047619,\n    0.3047619047619047619,0.3047619047619047619,0.3047619047619047619,\n    0.3047619047619047619,0.3047619047619047619,0.3047619047619047619\n};\n\ndouble grundmann3d3wts[] = {\n    -0.050793650793650793651,0.32544642857142857143,0.32544642857142857143,\n       0.32544642857142857143,0.32544642857142857143,-0.54179894179894179894,\n       -0.54179894179894179894,-0.54179894179894179894,-0.54179894179894179894,\n       -0.54179894179894179894,-0.54179894179894179894,-0.54179894179894179894,\n       -0.54179894179894179894,-0.54179894179894179894,-0.54179894179894179894,\n       0.25834986772486772487,0.25834986772486772487,0.25834986772486772487,\n       0.25834986772486772487,0.25834986772486772487,0.25834986772486772487,\n       0.25834986772486772487,0.25834986772486772487,0.25834986772486772487,\n       0.25834986772486772487,0.25834986772486772487,0.25834986772486772487,\n       0.25834986772486772487,0.25834986772486772487,0.25834986772486772487,\n       0.25834986772486772487,0.25834986772486772487,0.25834986772486772487,\n       0.25834986772486772487,0.25834986772486772487\n};\n\ndouble grundmann3d4wts[] = {\n    0.0063492063492063492063,-0.10848214285714285714,-0.10848214285714285714,\n       -0.10848214285714285714,-0.10848214285714285714,0.43343915343915343915,\n       0.43343915343915343915,0.43343915343915343915,0.43343915343915343915,\n       0.43343915343915343915,0.43343915343915343915,0.43343915343915343915,\n       0.43343915343915343915,0.43343915343915343915,0.43343915343915343915,\n       -0.58715879028379028379,-0.58715879028379028379,-0.58715879028379028379,\n       -0.58715879028379028379,-0.58715879028379028379,-0.58715879028379028379,\n       -0.58715879028379028379,-0.58715879028379028379,-0.58715879028379028379,\n       -0.58715879028379028379,-0.58715879028379028379,-0.58715879028379028379,\n       -0.58715879028379028379,-0.58715879028379028379,-0.58715879028379028379,\n       -0.58715879028379028379,-0.58715879028379028379,-0.58715879028379028379,\n       -0.58715879028379028379,-0.58715879028379028379,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753,0.25246753246753246753,0.25246753246753246753,\n       0.25246753246753246753\n};\n\ndouble grundmann3d5wts[] = {\n    -0.00056437389770723104056,0.024408482142857142857,0.024408482142857142857,\n       0.024408482142857142857,0.024408482142857142857,-0.21015231681898348565,\n       -0.21015231681898348565,-0.21015231681898348565,-0.21015231681898348565,\n       -0.21015231681898348565,-0.21015231681898348565,-0.21015231681898348565,\n       -0.21015231681898348565,-0.21015231681898348565,-0.21015231681898348565,\n       0.61162373987894821228,0.61162373987894821228,0.61162373987894821228,\n       0.61162373987894821228,0.61162373987894821228,0.61162373987894821228,\n       0.61162373987894821228,0.61162373987894821228,0.61162373987894821228,\n       0.61162373987894821228,0.61162373987894821228,0.61162373987894821228,\n       0.61162373987894821228,0.61162373987894821228,0.61162373987894821228,\n       0.61162373987894821228,0.61162373987894821228,0.61162373987894821228,\n       0.61162373987894821228,0.61162373987894821228,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,-0.69914085914085914086,-0.69914085914085914086,\n       -0.69914085914085914086,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715,\n       0.27217694439048605715,0.27217694439048605715,0.27217694439048605715\n};\n\nquadraturerule grundmann3d5 = {\n    .name = \"grundmann3d5\",\n    .grade = 3,\n    .order = 11,\n    .nnodes = 126,\n    .nodes = grundmann3dpts,\n    .weights = grundmann3d5wts,\n    .ext = NULL\n};\n\nquadraturerule grundmann3d4 = {\n    .name = \"grundmann3d4\",\n    .grade = 3,\n    .order = 9,\n    .nnodes = 70,\n    .nodes = grundmann3dpts,\n    .weights = grundmann3d4wts,\n    .ext = &grundmann3d5\n};\n\nquadraturerule grundmann3d3 = {\n    .name = \"grundmann3d3\",\n    .grade = 3,\n    .order = 7,\n    .nnodes = 35,\n    .nodes = grundmann3dpts,\n    .weights = grundmann3d3wts,\n    .ext = &grundmann3d4\n};\n\nquadraturerule grundmann3d2 = {\n    .name = \"grundmann3d2\",\n    .grade = 3,\n    .order = 5,\n    .nnodes = 15,\n    .nodes = grundmann3dpts,\n    .weights = grundmann3d2wts,\n    .ext = &grundmann3d3\n};\n\nquadraturerule grundmann3d1 = {\n    .name = \"grundmann3d1\",\n    .grade = 3,\n    .order = 3,\n    .nnodes = 5,\n    .nodes = grundmann3dpts,\n    .weights = grundmann3d1wts,\n    .ext = &grundmann3d2\n};\n\nquadraturerule grundmann3d0 = {\n    .name = \"grundmann3d0\",\n    .grade = 3,\n    .order = 1,\n    .nnodes = 1,\n    .nodes = grundmann3dpts,\n    .weights = grundmann3d0wts,\n    .ext = &grundmann3d1\n};\n\n/* --------------------------------\n * List of quadrature rules\n * -------------------------------- */\n\nquadraturerule *quadrules[] = {\n    &midpoint, &simpson,\n    &gauss1, &kronrod3,\n    &gauss2, &kronrod5,\n    &gauss5, &kronrod11,\n    &gauss7, &kronrod15,\n\n    &tri0, &tri4, &tri10, &tri20,\n    &cubtri0, &cubtri7, &cubtri19,\n    &grundmann2d0, &grundmann2d1, &grundmann2d2, &grundmann2d3, &grundmann2d4, &grundmann2d5,\n    \n    &keast4, &keast5,\n    &tet5, &tet6,\n\n    &grundmann3d0, &grundmann3d1, &grundmann3d2, &grundmann3d3, &grundmann3d4, &grundmann3d5,\n    NULL\n};\n\n// Specify a list of default rules for each grade\nquadraturerule *defaultquadrule[] = {\n    &gauss5,\n    &cubtri7,\n    &grundmann3d0,\n    NULL\n};\n\n/* **********************************************\n * Subdivision rules\n * ********************************************** */\n\n/* -------\n *   1D\n * ------- */\n\n/** Bisection */\ndouble bisectionpts[] = {\n    0.5, 0.5\n};\n\ndouble bisectionweights[] = {\n    0.5, 0.5\n};\n\nint bisectionintervals[] = {\n    2, 1,\n    0, 2\n};\n\nsubdivisionrule bisection = {\n    .grade = 1,\n    .npts = 1,\n    .pts = bisectionpts,\n    .nels = 2,\n    .newels = bisectionintervals,\n    .weights = bisectionweights,\n    .alt = NULL\n};\n\n/** Trisection */\ndouble trisectionpts[] = {\n    0.666666666666666667, 0.333333333333333333,\n    0.333333333333333333, 0.666666666666666667\n};\n\ndouble trisectionweights[] = {\n    0.333333333333333333, 0.333333333333333333, 0.333333333333333333\n};\n\nint trisectionintervals[] = {\n    0, 2,\n    3, 1,\n    2, 3\n};\n\nsubdivisionrule trisection = {\n    .grade = 1,\n    .npts = 2,\n    .pts = trisectionpts,\n    .nels = 3,\n    .newels = trisectionintervals,\n    .weights = trisectionweights,\n    .alt = NULL\n};\n\n/* -------\n *   2D\n * ------- */\n\n/*\n *       2\n *      / \\\n *     / | \\\n *    /  |  \\\n *   0 - 3 - 1\n */\n\n/** Bisection of 2D triangle */\ndouble tribisectionpts[] = {\n    0.5, 0.5, 0.0\n};\n\ndouble tribisectionweights[] = {\n    0.5, 0.5\n};\n\nint tribisectiontris[] = {\n    0, 3, 2,\n    3, 1, 2\n};\n\nsubdivisionrule trianglebisection = {\n    .grade = 2,\n    .npts = 1,\n    .pts = tribisectionpts,\n    .nels = 2,\n    .newels = tribisectiontris,\n    .weights = tribisectionweights,\n    .alt = NULL\n};\n\n/** Quadrasection of 2D triangle */\n\n/*\n *       2\n *      / \\\n *     5 - 4\n *    / \\ / \\\n *   0 - 3 - 1\n */\n\ndouble triquadrasectionpts[] = {\n    0.5, 0.5, 0.0,\n    0.0, 0.5, 0.5,\n    0.5, 0.0, 0.5\n};\n\ndouble triquadrasectionweights[] = {\n    0.25, 0.25, 0.25, 0.25\n};\n\nint triquadrasectiontris[] = {\n    0, 3, 5,\n    3, 1, 4,\n    3, 4, 5,\n    5, 4, 2\n};\n\nsubdivisionrule trianglequadrasection = {\n    .grade = 2,\n    .npts = 3,\n    .pts = triquadrasectionpts,\n    .nels = 4,\n    .newels = triquadrasectiontris,\n    .weights = triquadrasectionweights,\n    .alt = &trianglebisection\n};\n\n/* -------\n *   3D\n * ------- */\n\n/** Splitting of tetrahedra */\ndouble tetsubdivpts[] = {\n    0.5, 0.5, 0.0, 0.0,\n    0.5, 0.0, 0.5, 0.0,\n    0.5, 0.0, 0.0, 0.5,\n    0.0, 0.5, 0.5, 0.0,\n    0.0, 0.5, 0.0, 0.5,\n    0.0, 0.0, 0.5, 0.5\n};\n\ndouble tetsubdivwts[] = {\n    0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125\n};\n\nint tetsubdivtets[] =  {\n    1, 4, 7, 8,\n    0, 4, 7, 9,\n    0, 4, 8, 9,\n    4, 7, 8, 9,\n    0, 5, 7, 9,\n    0, 6, 8, 9,\n    2, 5, 7, 9,\n    3, 6, 8, 9 };\n\nsubdivisionrule tetsection = {\n    .grade = 3,\n    .npts = 6,\n    .pts = tetsubdivpts,\n    .nels = 8,\n    .newels = tetsubdivtets,\n    .weights = tetsubdivwts,\n    .alt = NULL\n};\n\nsubdivisionrule *subdivisionrules[] = {\n    &bisection,\n    &trianglequadrasection,\n    &tetsection,\n    NULL\n};\n\n\n/* **********************************************\n * Integrator data structure and operations\n * ********************************************** */\n\nDEFINE_VARRAY(quadratureworkitem, quadratureworkitem)\n\n/** Initialize an integrator structure */\nvoid integrator_init(integrator *integrate) {\n    integrate->integrand=NULL;\n    \n    integrate->dim=0;\n    integrate->nbary=0;\n    integrate->nquantity=0;\n    \n    integrate->adapt=true;\n    integrate->rule = NULL;\n    integrate->errrule = NULL;\n    \n    integrate->subdivide = NULL;\n    \n    varray_quadratureworkiteminit(&integrate->worklist);\n    varray_doubleinit(&integrate->vertexstack);\n    varray_intinit(&integrate->elementstack);\n    \n    integrate->ztol = INTEGRATE_ZEROCHECK;\n    integrate->tol = INTEGRATE_ACCURACYGOAL;\n    integrate->maxiterations = INTEGRATE_MAXITERATIONS;\n    \n    integrate->niterations = 0;\n    integrate->val = 0;\n    integrate->err = 0;\n    \n    integrate->ref = NULL;\n}\n\n/** Free data associated with an integrator */\nvoid integrator_clear(integrator *integrate) {\n    varray_quadratureworkitemclear(&integrate->worklist);\n    varray_intclear(&integrate->elementstack);\n    varray_doubleclear(&integrate->vertexstack);\n}\n\n/** Adds a vertex to the integrators vertex stack, returning the id */\nint integrator_addvertex(integrator *integrate, int ndof, double *v) {\n    int vid = integrate->vertexstack.count;\n    varray_doubleadd(&integrate->vertexstack, v, ndof);\n    return vid;\n}\n\n/** Adds an element to the element stack, returning the id. Elements are specified by their coordinates in the reference element */\nint integrator_addelement(integrator *integrate, int *vids) {\n    int elid=integrate->elementstack.count;\n    varray_intadd(&integrate->elementstack, vids, integrate->nbary);\n    return elid;\n}\n\n/** Process the list of quantities given */\nvoid integrator_initializequantities(integrator *integrate, int nq, quantity *quantity) {\n    integrate->nquantity=nq;\n    integrate->quantity=quantity;\n    \n    for (int i=0; i<nq; i++) {\n        value q = quantity[i].vals[0]; // Take the first element from each quantity list as paradigmatic\n        if (MORPHO_ISFLOAT(q)) {\n            quantity[i].ndof=1;\n            integrate->qval[i]=q;\n        } else if (MORPHO_ISMATRIX(q)) {\n            objectmatrix *m = MORPHO_GETMATRIX(q);\n            quantity[i].ndof=matrix_countdof(m);\n            \n            objectmatrix *new = object_clonematrix(m); // Use a copy of the matrix\n            integrate->qval[i]=MORPHO_OBJECT(new);\n        } else return;\n    }\n}\n\n/** Frees up any objects used in the quantities list */\nvoid integrator_finalizequantities(integrator *integrate) {\n    for (int i=0; i<integrate->nquantity; i++) morpho_freeobject(integrate->qval[i]);\n}\n\n/** Retrieves the vertex pointers given an elementid.\n @warning: The pointers returned become invalid after a subsequent call to integrator_addvertex . */\nvoid integrator_getvertices(integrator *integrate, int elementid, double **vert) {\n    for (int i=0; i<integrate->nbary; i++) {\n        int vid=integrate->elementstack.data[elementid+i];\n        vert[i]=&(integrate->vertexstack.data[vid]);\n    }\n}\n\n/** Retrieves an element with elementid */\nvoid integrator_getelement(integrator *integrate, int elementid, int *vid) {\n    for (int i=0; i<integrate->nbary; i++) {\n        vid[i]=integrate->elementstack.data[elementid+i];\n    }\n}\n\n/** Adds a work item to the integrator's work list.\n    Uses a binary queue data structure to facilitate ln(N) push and pop - https://en.wikipedia.org/wiki/Binary_heap */\nbool integrator_pushworkitem(integrator *integrate, quadratureworkitem *work) {\n    varray_quadratureworkitemadd(&integrate->worklist, work, 1);\n    \n    for (int i=integrate->worklist.count-1, p; i>0; i=p) {\n        p=floor((i-1)/2); // Parent\n        if (integrate->worklist.data[i].err>integrate->worklist.data[p].err) {\n            quadratureworkitem swp=integrate->worklist.data[i];\n            integrate->worklist.data[i]=integrate->worklist.data[p];\n            integrate->worklist.data[p]=swp;\n        } else break;\n    }\n    \n    return true;\n}\n\n/** Pops the work item with the largest error */\nbool integrator_popworkitem(integrator *integrate, quadratureworkitem *work) {\n    *work = integrate->worklist.data[0];\n    \n    // Move the last element into first place and pop\n    int n=integrate->worklist.count-1;\n    if (n>0) integrate->worklist.data[0]=integrate->worklist.data[n];\n    integrate->worklist.count--;\n    \n    // Go down the heap, ensuring that the heap property is maintained\n    for (int i=0, p, q; i<n; i=p) {\n        p=2*i + 1; // Left - child nodes\n        q=p+1;     // Right\n        \n        // Check if the right child element has a larger value, if it exists\n        if (q<n &&\n            integrate->worklist.data[q].err>integrate->worklist.data[p].err) {\n            p=q;\n        }\n        \n        // If the child element is larger, swap it up\n        if (p<n && integrate->worklist.data[p].err>integrate->worklist.data[i].err) {\n            quadratureworkitem swp=integrate->worklist.data[i];\n            integrate->worklist.data[i]=integrate->worklist.data[p];\n            integrate->worklist.data[p]=swp;\n        } else break;\n    }\n    \n    return true;\n}\n\n/** Estimate the value and error of the integrand given a worklist */\nvoid integrator_estimate(integrator *integrate) {\n    double sumval=0.0, cval=0.0, yval, tval,\n           sumerr=0.0, cerr=0.0, yerr, terr;\n\n    // Sum in reverse as smallest entries should be nearer the end\n    for (int i=integrate->worklist.count-1; i>=0; i--) {\n        yval=integrate->worklist.data[i].val-cval;\n        yerr=integrate->worklist.data[i].err-cerr;\n        tval=sumval+yval;\n        terr=sumerr+yerr;\n        cval=(tval-sumval)-yval;\n        cerr=(terr-sumerr)-yerr;\n        sumval=tval;\n        sumerr=terr;\n    }\n    \n    integrate->val = sumval;\n    integrate->errest = sumerr;\n}\n\n/* --------------------------------\n * Linear interpolation\n * -------------------------------- */\n\n/** Construct vertex transformation matrices\n @param[in] integrate - the integrator\n @param[in] vref - vertices specified in reference element (length integrate->nbary)\n @param[out] r - matrix mapping local node coordinates to ref. el coordinates [r has nbary rows and nbary columns]\n @param[out] v - matrix mapping ref. el coordinates to physical coordinates [v has dim rows and nbary columns] */\nvoid integrator_preparevertices(integrator *integrate, double **vref, double *r, double *v) {\n    int l=0;\n    if (r) for (int i=0; i<integrate->nbary; i++) { // Loop over vertices [defined rel. to ref. element]\n        for (int k=0; k<integrate->nbary; k++) { // Sum over barycentric coordinates\n            r[l]=vref[i][k];\n            l++;\n        }\n    }\n    \n    l=0;\n    if (v) for (int i=0; i<integrate->nbary; i++) { // Loop over vertices [defined rel. to ref. element]\n        for (int j=0; j<integrate->dim; j++) { // Loop over dimensions\n            v[l]=integrate->x[i][j];\n            l++;\n        }\n    }\n}\n\n/** Sets up interpolation matrix */\nvoid integrator_prepareinterpolation(integrator *integrate, int elementid, double *rmat, double *vmat) {\n    double *vert[integrate->nbary]; // Vertex information\n    integrator_getvertices(integrate, elementid, vert);\n    integrator_preparevertices(integrate, vert, rmat, vmat);\n}\n\n/** Weighted sum of a list */\ndouble integrator_sumlistweighted(unsigned int nel, double *list, double *wts) {\n    return cblas_ddot(nel, list, 1, wts, 1);\n}\n\n/** Transforms local element coordinates to reference element coordinates */\nvoid integrator_transformtorefelement(integrator *integrate, double *rmat, double *local, double *bary) {\n    // Multiply nbary x nbary (rmat) with nbary x 1 (local) to get nbary x 1 (bary)\n    // [1/13/25] Manual matrix multiply is faster on macOS/Intel. TODO: Check on other platforms\n    int nbary=integrate->nbary;\n    for (int j=0; j<nbary; j++) bary[j]=0;\n    for (int k=0; k<nbary; k++) for (int j=0; j<nbary; j++) bary[j]+=rmat[k*nbary+j]*local[k];\n    \n    //cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->nbary, 1, integrate->nbary, 1.0, rmat, integrate->nbary, local, integrate->nbary, 0.0, bary, integrate->nbary);\n}\n\n/** Transform from reference element barycentric coordinates to physical coordinates */\nvoid integrator_interpolatecoordinates(integrator *integrate, double *lambda, double *vmat, double *x) {\n    // Multiply dim x nbary (vmat) with nbary x 1 (lambda) to get dim x 1 (x)\n    int dim=integrate->dim, nbary=integrate->nbary;\n    for (int j=0; j<dim; j++) x[j]=0;\n    for (int k=0; k<nbary; k++) for (int j=0; j<dim; j++) x[j]+=vmat[k*dim+j]*lambda[k];\n    \n    //cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->dim, 1, integrate->nbary, 1.0, vmat, integrate->dim, lambda, integrate->nbary, 0.0, x, integrate->dim);\n}\n\n/** Sums a weighted list of quantities */\nbool integrator_sumquantityweighted(int n, double *wts, value *q, value *out) {\n    bool success=false;\n    if (MORPHO_ISFLOAT(q[0])) {\n        double qval[n];\n        for (int j=0; j<n; j++) qval[j]=MORPHO_GETFLOATVALUE(q[j]);\n        double val=integrator_sumlistweighted(n, qval, wts);\n        *out=MORPHO_FLOAT(val);\n        success=true;\n    } else if (MORPHO_ISMATRIX(q[0])) {\n        objectmatrix *sum = MORPHO_GETMATRIX(*out);\n        matrix_zero(sum);\n        for (int j=0; j<n; j++) matrix_accumulate(sum, wts[j], MORPHO_GETMATRIX(q[j]));\n        success=true;\n    }\n    return success;\n}\n\n/** Interpolates quantities */\nvoid integrator_interpolatequantities(integrator *integrate, double *bary) {\n    for (int i=0; i<integrate->nquantity; i++) {\n        int nnodes = integrate->quantity[i].nnodes;\n        double wts[nnodes];\n        if (integrate->quantity[i].ifn) {\n            (integrate->quantity[i].ifn) (bary, wts);\n        } else {\n            for (int k=0; k<nnodes; k++) wts[k]=bary[k];\n        }\n        \n        integrator_sumquantityweighted(nnodes, wts, integrate->quantity[i].vals, &integrate->qval[i]);\n    }\n}\n\n/* --------------------------------\n * Function to perform quadrature\n * -------------------------------- */\n\n/** Evaluates the integrand at specified places */\nbool integrator_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *rmat, double *vmat, double *x, double *f) {\n    double node[integrate->nbary];\n    \n    for (int i=imin; i<imax; i++) {\n        integrator_transformtorefelement(integrate, rmat, &rule->nodes[integrate->nbary*i], node);\n        integrator_interpolatecoordinates(integrate, node, vmat, x);\n        if (integrate->nquantity) integrator_interpolatequantities(integrate, node);\n        \n        // Evaluate function\n        if (!(*integrate->integrand) (integrate->dim, node, x, integrate->nquantity, integrate->qval, integrate->ref, &f[i])) return false;\n    }\n    return true;\n}\n\n/** Integrates a function over an element specified in work, filling out the integral and error estimate if provided */\nbool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem *work) {\n    int n = rule->nnodes;\n    \n    int nmax = rule->nnodes;\n    int np = 0; // Number of levels of p-refinement\n    for (quadraturerule *q = rule->ext; q!=NULL; q=q->ext) { // Find maximum number of pts \n        nmax = q->nnodes;\n        np++;\n    }\n    \n    double rmat[integrate->nbary*integrate->nbary]; // Transform local element coordinates to ref. el.\n    double vmat[integrate->nbary*integrate->dim]; // Transform barycentric coordinates in ref. el. to physical coordinates\n    integrator_prepareinterpolation(integrate, work->elementid, rmat, vmat);\n    \n    // Evaluate function at quadrature points\n    double x[integrate->dim], f[nmax];\n    if (!integrator_evalfn(integrate, rule, 0, rule->nnodes, rmat, vmat, x, f)) return false;\n    \n    double r[np+1];\n    double eps[np+1]; eps[0]=0.0;\n    \n    // Obtain estimate\n    r[0]=integrator_sumlistweighted(rule->nnodes, f, rule->weights);\n    work->lval = work->val = work->weight*r[0];\n    \n    // Estimate error\n    if (rule->ext!=NULL) { // Evaluate extension rule\n        int nmin = rule->nnodes, ip=0;\n        \n        // Attempt p-refinement if available\n        for (quadraturerule *q=rule->ext; q!=NULL; q=q->ext) {\n            ip++;\n            if (!integrator_evalfn(integrate, q, nmin, q->nnodes, rmat, vmat, x, f)) return false;\n            \n            r[ip]=integrator_sumlistweighted(q->nnodes, f, q->weights);\n            eps[ip]=fabs(r[ip]-r[ip-1]);\n            nmin = q->nnodes;\n            \n            if (fabs(r[ip])<integrate->ztol ||\n                fabs(eps[ip]/r[ip])<integrate->tol) break;\n        }\n        \n        work->lval = work->weight*r[ip-1];\n        work->val = work->weight*r[ip]; // Record better estimate\n        work->err = work->weight*eps[ip]; // Use the difference as the error estimator\n    } else if (integrate->errrule) {  // Otherwise, use the error rule to obtain the estimate\n        if (rule==integrate->errrule) return true; // We already are using the error rule\n        double temp = work->val; // Retain the lower order estimate\n        if (!integrator_quadrature(integrate, integrate->errrule, work)) return false;\n        work->lval=temp;\n        work->err=fabs(work->val-temp); // Estimate error from difference of rules\n    } else {\n        UNREACHABLE(\"Integrator definition inconsistent.\");\n    }\n    \n    return true;\n}\n\n/* --------------------------------\n * Subdivision\n * -------------------------------- */\n\nbool integrator_subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) {\n    subdivisionrule *rule = integrate->subdivide;\n    \n    // Fetch the element data\n    int vid[integrate->nbary+rule->npts];\n    integrator_getelement(integrate, work->elementid, vid);\n    \n    // Get ready for interpolation\n    double rmat[integrate->nbary*integrate->nbary]; // Vertex information\n    integrator_prepareinterpolation(integrate, work->elementid, rmat, NULL);\n    \n    // Interpolate vertices\n    double lambda[integrate->nbary];\n    for (int j=0; j<rule->npts; j++) {\n        integrator_transformtorefelement(integrate, rmat, &rule->pts[j*integrate->nbary], lambda);\n        vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->nbary, lambda);\n    }\n    \n    // Create elements\n    for (int i=0; i<rule->nels; i++) {\n        newitems[i].val=0.0;\n        newitems[i].err=0.0;\n        newitems[i].weight=work->weight*rule->weights[i];\n        if (newitems[i].weight<integrate->val*DBL_EPSILON) {\n            error_writewithid(integrate->err, INTEGRATE_SBDVSNS);\n            return false; \n        }\n        \n        // Construct new element from the vertex ids\n        int vids[integrate->nbary];\n        for (int k=0; k<integrate->nbary; k++) {\n            vids[k]=vid[rule->newels[integrate->nbary*i+k]];\n        }\n        \n        // Define the new element\n        newitems[i].elementid=integrator_addelement(integrate, vids);\n    }\n    \n    *nels = rule->nels;\n    \n    return true;\n}\n\n/* --------------------------------\n * Laurie's sharper error estimate\n * -------------------------------- */\n\n/** Laurie's sharper error estimator: BIT 23 (1983), 258-261\n    The norm of the difference between two rules |A-B| is usually too pessimistic;\n    this attempts to extrapolate a sharper estimate if convergence looks good */\nvoid integrator_sharpenerrorestimate(integrator *integrate, quadratureworkitem *work, int nels, quadratureworkitem *newitems) {\n    double a1=work->val, b1=work->lval, a2=0, b2=0;\n    for (int k=0; k<nels; k++) {\n        a2+=newitems[k].val;\n        b2+=newitems[k].lval;\n    }\n    \n    // Scale errors if conditions are met\n    if (fabs(a2-a1)<fabs(b2-b1) && // Laurie's second condition\n        fabs(a2-b2)<fabs(a1-b1)) // Weak form of first condition (see Gonnet)\n    {\n        double sigma=fabs((a2-a1)/(b2-b1-a2+a1));\n        for (int k=0; k<nels; k++) newitems[k].err*=sigma;\n    }\n}\n\n/** Adds newitems to the work list and updates the value and error */\nvoid integrator_update(integrator *integrate, quadratureworkitem *work, int nels, quadratureworkitem *newitems) {\n    double dval=0, derr=0;\n    integrate->val-=work->val;\n    integrate->errest-=work->err;\n    for (int k=0; k<nels; k++) {\n        dval+=newitems[k].val;\n        derr+=newitems[k].err;\n        integrator_pushworkitem(integrate, &newitems[k]);\n    }\n    integrate->val+=dval;\n    integrate->errest+=derr;\n}\n\n/* --------------------------------\n * Integrator configuration\n * -------------------------------- */\n\n/** Finds a rule by name */\nbool integrator_matchrulebyname(int grade, char *name, quadraturerule **out) {\n    for (int i=0; quadrules[i]!=NULL; i++) {\n        if (quadrules[i]->grade!=grade) continue;\n        if (name && quadrules[i]->name &&\n            (strcmp(name, quadrules[i]->name)==0)) { // Match a rule by name\n            *out = quadrules[i];\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Attempts to find a quadrature rule that uses rule as an extension. */\nbool integrator_matchrulebyextension(quadraturerule *rule, quadraturerule **out) {\n    for (int i=0; quadrules[i]!=NULL; i++) {\n        if (quadrules[i]->ext==rule) {\n            *out = quadrules[i];\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Finds the [highest/lowest] rule with order such that minorder <= order <= maxorder */\nbool integrator_matchrulebyorder(int grade, int minorder, int maxorder, bool highest, quadraturerule **out) {\n    int best=-1, bestorder=(highest ? -1 : INT_MAX);\n    for (int i=0; quadrules[i]!=NULL; i++) {\n        if (quadrules[i]->grade!=grade) continue;\n        \n        if (quadrules[i]->order>=minorder &&\n            quadrules[i]->order<=maxorder &&\n            ( (highest && quadrules[i]->order>bestorder) ||\n              (!highest && quadrules[i]->order<bestorder) )) {\n            best = i;\n            bestorder = quadrules[i]->order;\n        }\n    }\n    if (best>=0) *out = quadrules[best];\n    return (best>=0);\n}\n\n/** Returns a default rule for each grade */\nbool integrator_matchrulebygrade(int grade, quadraturerule **out) {\n    for (int i=0; defaultquadrule[i]!=NULL; i++) {\n        if (defaultquadrule[i]->grade==grade) {\n            *out = defaultquadrule[i];\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Configures an integrator based on the grade to integrate and hints for order and rule type\n * @param[in] integrate     - integrator structure to be configured\n * @param[in] err                  - error structure to report errors to\n * @param[in] adapt             - enable adaptive refinement\n * @param[in] grade              - Dimension of the vertices\n * @param[in] order              - Requested order of quadrature rule\n * @param[in] name                - Alternatively, supply the name of a known rule\n * @returns true if the configuration was successful */\nbool integrator_configure(integrator *integrate, error *err, bool adapt, int grade, int order, char *name) {\n    integrate->rule=NULL;\n    integrate->errrule=NULL;\n    integrate->adapt=adapt;\n    integrate->err=err;\n    integrate->nbary=grade+1; // Number of barycentric coordinates\n    \n    if (name) {\n        if (!integrator_matchrulebyname(grade, name, &integrate->rule)) {\n            error_writewithid(err, INTEGRATE_RLNTFND, name);\n            return false;\n        }\n    } else if (order>=0) {\n        integrator_matchrulebyorder(grade, order, INT_MAX, false, &integrate->rule);\n    } else {\n        integrator_matchrulebygrade(grade, &integrate->rule);\n    }\n    \n    // Check we succeeded in finding a rule\n    if (!integrate->rule) {\n        error_writewithid(err, INTEGRATE_RLUNAVLB);\n        return false;\n    }\n    \n    // Do we need to find an extension rule?\n    if (adapt && integrate->rule->ext==NULL) {\n        // Find if the rule obtained is an extension of another rule\n        if (integrator_matchrulebyextension(integrate->rule, &integrate->rule)) {\n            \n        } else if (!integrator_matchrulebyorder(grade, integrate->rule->order+1, INT_MAX, false,  &integrate->errrule)) { // Otherwise attempt to find a rule of higher order\n            // but if there wasn't one, find the next lowest one...\n            if (!integrator_matchrulebyorder(grade, 0, integrate->rule->order-1, true,  &integrate->errrule)) return false;\n        }\n        \n        // Ensure that the error rule is higher than the integration rule\n        if (integrate->errrule && integrate->rule->order>integrate->errrule->order) {\n            quadraturerule *swp=integrate->rule;\n            integrate->rule=integrate->errrule;\n            integrate->errrule=swp;\n        }\n    }\n    \n    // Select subdivision rule\n    for (int i=0; subdivisionrules[i]!=NULL; i++) {\n        if (subdivisionrules[i]->grade==grade) {\n            integrate->subdivide = subdivisionrules[i];\n            break;\n        }\n    }\n    \n    return true;\n}\n\n/** Configures the integrator based on the contents of a dictionary */\nbool integrator_configurewithdictionary(integrator *integrate, error *err, grade g, objectdictionary *dict) {\n    char *name=NULL;\n    bool adapt=true;\n    int order=-1;\n    value val;\n    \n    objectstring rulelabel = MORPHO_STATICSTRING(INTEGRATE_RULELABEL);\n    objectstring degreelabel = MORPHO_STATICSTRING(INTEGRATE_DEGREELABEL);\n    objectstring adaptlabel = MORPHO_STATICSTRING(INTEGRATE_ADAPTLABEL);\n    \n    if (dictionary_get(&dict->dict, MORPHO_OBJECT(&rulelabel), &val)) {\n        if (MORPHO_ISSTRING(val)) {\n            name = MORPHO_GETCSTRING(val);\n        } else {\n            error_writewithid(err, INTEGRATE_MTHDTYP, INTEGRATE_RULELABEL, STRING_CLASSNAME);\n            return false;\n        }\n    }\n\n    if (dictionary_get(&dict->dict, MORPHO_OBJECT(&degreelabel), &val)) {\n        if (MORPHO_ISINTEGER(val)) {\n            order = MORPHO_GETINTEGERVALUE(val);\n        } else {\n            error_writewithid(err, INTEGRATE_MTHDTYP, INTEGRATE_DEGREELABEL, INT_CLASSNAME);\n            return false;\n        }\n    }\n    \n    if (dictionary_get(&dict->dict, MORPHO_OBJECT(&adaptlabel), &val)) {\n        if (MORPHO_ISBOOL(val)) {\n            adapt = MORPHO_GETBOOLVALUE(val);\n        } else {\n            error_writewithid(err, INTEGRATE_MTHDTYP, INTEGRATE_ADAPTLABEL, BOOL_CLASSNAME);\n            return false;\n        }\n    }\n    \n    return integrator_configure(integrate, err, adapt, g, order, name);\n}\n\n/* --------------------------------\n * Driver routine\n * -------------------------------- */\n\n/** Integrates over a function\n * @param[in] integrate     - integrator structure, that has been configured with integrator_configure\n * @param[in] integrand     - function to integrate\n * @param[in] dim                  - Dimension of the vertices\n * @param[in] x                       - vertices of the line x[0] = {x,y,z} etc.\n * @param[in] nquantity     - number of quantities per vertex\n * @param[in] quantity       - List of quantities for each vertex.\n * @param[in] ref                  - a pointer to any data required by the function\n * @returns True on success */\nbool integrator_integrate(integrator *integrate, integrandfunction *integrand, int dim, double **x, unsigned int nquantity, quantity *quantity, void *ref) {\n    bool success=false;\n    \n    integrate->integrand=integrand; // Integrand function\n    integrate->ref=ref;\n    \n    integrate->x=x; // Vertices\n    integrate->dim=dim;\n    \n    integrate->worklist.count=0;    // Reset these\n    integrate->vertexstack.count=0;\n    integrate->elementstack.count=0;\n    \n    // Quantities\n    value qval[nquantity+1];\n    integrate->qval=qval;\n    \n    integrator_initializequantities(integrate, nquantity, quantity);\n    \n    // Create first element, which corresponds to the reference element\n    int vids[integrate->nbary];\n    double xref[integrate->nbary];\n    for (int i=0; i<integrate->nbary; i++) xref[i]=0.0;\n    for (int i=0; i<integrate->nbary; i++) {\n        xref[i]=1.0;\n        vids[i]=integrator_addvertex(integrate, integrate->nbary, xref);\n        xref[i]=0.0;\n    }\n    int elid = integrator_addelement(integrate, vids);\n    \n    // Add it to the work list\n    quadratureworkitem work;\n    work.weight = 1.0;\n    work.elementid = elid;\n    integrator_quadrature(integrate, integrate->rule, &work); // Perform initial quadrature\n    \n    integrator_pushworkitem(integrate, &work);\n    integrator_estimate(integrate); // Initial estimate\n    \n    if (integrate->adapt) for (integrate->niterations=0; integrate->niterations<=integrate->maxiterations; integrate->niterations++) {\n        // Convergence check\n        if (fabs(integrate->val)<integrate->ztol || fabs(integrate->errest/integrate->val)<integrate->tol) break;\n        \n        // Get worst interval\n        integrator_popworkitem(integrate, &work);\n        \n        // Subdivide\n        int nels; // Number of elements created\n        quadratureworkitem newitems[integrate->subdivide->nels];\n        \n        if (!integrator_subdivide(integrate, &work, &nels, newitems)) goto integrator_integrate_error;\n        for (int k=0; k<nels; k++) integrator_quadrature(integrate, integrate->rule, &newitems[k]);\n        \n        // Error estimate\n        integrator_sharpenerrorestimate(integrate, &work, nels, newitems);\n        \n        // Add new items to heap and update error estimates\n        integrator_update(integrate, &work, nels, newitems);\n    }\n    \n    // Final estimate by Kahan summing heap\n    integrator_estimate(integrate);\n    \n    success=true;\n    \nintegrator_integrate_error:\n    integrator_finalizequantities(integrate);\n    \n    return success;\n}\n\n/* ---------------------------------------\n * Public interface resembling old version\n * --------------------------------------- */\n\n/** Integrate over an element - public interface for one off integrals.\n * @param[in] integrand   - integrand\n * @param[in] method         - Dictionary with method selection (optional)\n * @param[in] err                - Error structure to report errors (optional)\n * @param[in] dim                - Dimension of the vertices\n * @param[in] grade            - Grade to integrate over\n * @param[in] x                     - vertices of the triangle x[0] = {x,y,z} etc.\n * @param[in] nquantity   - number of quantities per vertex\n * @param[in] quantity     - List of quantities\n * @param[in] ref                - a pointer to any data required by the function\n * @param[out] out              - value of the integral\n * @param[out] errest        - an estimate of the error\n * @returns true on success. */\nbool integrate(integrandfunction *integrand, objectdictionary *method, error *err, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, quantity *quantity, void *ref, double *out, double *errest) {\n    bool success=false;\n    integrator integrate;\n    integrator_init(&integrate);\n    \n    if (method) {\n        if (!integrator_configurewithdictionary(&integrate, err, grade, method)) return false;\n    } else if (!integrator_configure(&integrate, err, true, grade, -1, NULL)) return false;\n    success=integrator_integrate(&integrate, integrand, dim, x, nquantity, quantity, ref);\n    \n    *out = integrate.val;\n    if (errest) *errest = integrate.errest;\n    \n    integrator_clear(&integrate);\n    \n    return success;\n}\n\n/* -------------------------------------\n * Public interface matching old version\n * ------------------------------------- */\n\nvoid integrate_initialize(void) {\n    morpho_defineerror(INTEGRATE_SBDVSNS, ERROR_HALT, INTEGRATE_SBDVSNS_MSG);\n    morpho_defineerror(INTEGRATE_RLNTFND, ERROR_HALT, INTEGRATE_RLNTFND_MSG);\n    morpho_defineerror(INTEGRATE_RLUNAVLB, ERROR_HALT, INTEGRATE_RLUNAVLB_MSG);\n    morpho_defineerror(INTEGRATE_MTHDTYP, ERROR_HALT, INTEGRATE_MTHDTYP_MSG);\n}\n\n#endif\n"
  },
  {
    "path": "src/geometry/integrate.h",
    "content": "/** @file integrate.h\n *  @author T J Atherton\n *\n *  @brief Numerical integration\n*/\n\n#ifndef integration_h\n#define integration_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include <stdio.h>\n#include \"morpho.h\"\n#include \"dict.h\"\n#include \"fespace.h\"\n\n#define INTEGRATE_RULELABEL \"rule\"\n#define INTEGRATE_DEGREELABEL \"degree\"\n#define INTEGRATE_ADAPTLABEL \"adapt\"\n\n#define INTEGRATE_ACCURACYGOAL 1e-6\n#define INTEGRATE_ZEROCHECK 1e-15\n#define INTEGRATE_MAXRECURSION 100\n#define INTEGRATE_MAXITERATIONS 1000\n\n/* -------------------------------------------------------\n * Integrator type definitions\n * ------------------------------------------------------- */\n\n/* ----------------------------------\n * Integrands\n * ---------------------------------- */\n\n/** Generic specification for an integrand.\n * @param[in] dim            - The dimension of the space\n * @param[in] lambda      - Barycentric coordinates for the element\n * @param[in] x                 - Coordinates of the point calculated from interpolation\n * @param[in] nquantity - Number of quantities\n * @param[in] quantity - List of quantities evaluated for the point, calculated from interpolation\n * @param[in] ref             - A reference passed by the caller (typically things constant over the domain\n * @returns value of the integrand at the appropriate point with interpolated quantities.\n */\ntypedef bool (integrandfunction) (unsigned int dim, double *lambda, double *x, unsigned int nquantity, value *quantity, void *ref, double *fout);\n\n/* ----------------------------------\n * Quadrature rules define wts/nodes\n * ---------------------------------- */\n\ntypedef struct quadraturerule_s quadraturerule;\n\n/** @details A quadrature rule is defined by:\n    - a set of nodes, given in barycentric coordinates (d+1 values per node)\n    - and a set of weights\n    - metadata\n    The integrator is designed to work with rules which provide a higher order extension. */\nstruct quadraturerule_s {\n    char *name; /** Identifier for the rule */\n    int grade; /** Dimensionality of element the rule operates on */\n    int order; /** Order of integrator */\n    int nnodes; /** Number of nodes */\n    double *nodes; /** Nodes */\n    double *weights; /** Weights */\n    quadraturerule *ext; /** Extension rule that uses same points */\n};\n\n/* ----------------------------------\n * Subdivision rules\n * ---------------------------------- */\n\n/** @details A subdivision rule is defined by:\n    - a set of new nodes to be created in the original element, given as barycentric coordinates\n    - a list of vertex ids defining the new element (the original vertices are labelled 0...grade-1\n    - a list of weights for the new elements (the fraction of the total d-volume of the original element)\n      N.B. weights should sum to 1 (NOT the volume of the element\n    - metadata */\n\ntypedef struct subdivisionrule_struct subdivisionrule;\n\nstruct subdivisionrule_struct {\n    int grade;    /** Appropriate grade for the strategy */\n    int npts;     /** Number of new pts created */\n    double *pts;  /** New barycentric coordinates */\n    int nels;     /** Number of new elements created */\n    int *newels;  /** Indices of new elements */\n    double *weights;  /** Weights of new elements */\n    subdivisionrule *alt; /** Alternative subdivision rule */\n} ;\n\n/* --------------------------------\n * Quadrature work items\n * -------------------------------- */\n\ntypedef struct {\n    double weight; /** Overall element weight */\n    int elementid; /** Id of element on the element stack */\n    //value **quantity;\n    double val; /** Value of work item */\n    double lval; /** Value of work item from lower order estimate */\n    double err; /** Error estimate of work item */\n} quadratureworkitem;\n\nDECLARE_VARRAY(quadratureworkitem, quadratureworkitem)\n\n/* ----------------------------------\n * Quantities\n * ---------------------------------- */\n\ntypedef struct {\n    int nnodes;  /** Number of quantity values per element */\n    value *vals; /** List of quantity values */\n    interpolationfn ifn; /** Interpolation function */\n    int ndof; /** Number of degrees of freedom (this will be filled out by the integrator) */\n} quantity;\n\n/* ----------------------------------\n * Integrator\n * ---------------------------------- */\n\ntypedef struct {\n    integrandfunction *integrand; /** Function to integrate */\n    void *ref; /** Reference to pass to integrand function */\n    \n    int dim; /** Dimension of points in embedded space */\n    double **x; /** Vertices defining the element */\n    \n    int nbary; /** Number of barycentric coordinates */\n    \n    int nquantity; /** Number of quantities to interpolate */\n    quantity *quantity; /** Quantity list */\n    value *qval; /** Interpolated quantity values */\n    \n    quadraturerule *rule;  /** Quadrature rule to use */\n    quadraturerule *errrule; /** Additional rule for error estimation */\n    \n    bool adapt; /** Enable adaptive integration */\n    subdivisionrule *subdivide; /** Subdivision rule to use */\n    \n    varray_quadratureworkitem worklist; /** Work list */\n    varray_double vertexstack; /** Stack of vertices */\n    varray_int elementstack; /** Stack of elements */\n    \n    double ztol; /** Tolerance for zero detection */\n    double tol; /** Tolerance for relative error */\n    int maxiterations; /** Maximum number of subdivisions to perform */\n    \n    int niterations; /** Number of iterations performed */\n    double val; /** Estimated value of the integral */\n    double errest; /** Estimated error of the integral */\n    \n    error *err; /** Error structure to report errors */\n} integrator;\n\n/* -------------------------------------------------------\n * Integrator errors\n * ------------------------------------------------------- */\n\n#define INTEGRATE_SBDVSNS             \"IntgrtrSbdvns\"\n#define INTEGRATE_SBDVSNS_MSG         \"Too many subdivisions in evaluating integral; possible singularity detected.\"\n\n#define INTEGRATE_RLNTFND             \"IntgrtrRlNtFnd\"\n#define INTEGRATE_RLNTFND_MSG         \"Integrator quadrature rule '%s' not found.\"\n\n#define INTEGRATE_RLUNAVLB            \"IntgrtrRlUnavlb\"\n#define INTEGRATE_RLUNAVLB_MSG        \"No quadrature rule is available that matches the provided integrator method dictionary.\"\n\n#define INTEGRATE_MTHDTYP             \"IntgrtrMthdTyp\"\n#define INTEGRATE_MTHDTYP_MSG         \"Integrator method dictionary option '%s' must be a %s.\"\n\n/* -------------------------------------------------------\n * Integrator interface\n * ------------------------------------------------------- */\n\nbool integrate_integrate(integrandfunction *integrand, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out);\n\nbool integrate(integrandfunction *integrand, objectdictionary *method, error *err, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, quantity *quantity, void *ref, double *out, double *errest);\n\nvoid integrate_initialize(void);\n\n#endif\n\n#endif /* integration_h */\n\n"
  },
  {
    "path": "src/geometry/mesh.c",
    "content": "/** @file mesh.c\n *  @author T J Atherton\n *\n *  @brief Mesh class and associated functionality\n */\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include \"morpho.h\"\n#include \"classes.h\"\n#include \"mesh.h\"\n#include \"file.h\"\n#include \"parse.h\"\n#include \"sparse.h\"\n#include \"matrix.h\"\n#include \"selection.h\"\n\n// Temporary include\n#include \"integrate.h\"\n\n#include <limits.h>\n\nvoid mesh_link(objectmesh *mesh, object *obj);\n\nDEFINE_VARRAY(elementid, elementid);\n\n/* **********************************************************************\n * Mesh object definitions\n * ********************************************************************** */\n\nobjecttype objectmeshtype;\n\n/** Mesh object definitions */\nvoid objectmesh_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Mesh>\");\n}\n\nvoid objectmesh_markfn(object *obj, void *v) {\n    objectmesh *c = (objectmesh *) obj;\n    if (c->vert) morpho_markobject(v, (object *) c->vert);\n    if (c->conn) morpho_searchunmanagedobject(v, (object *) c->conn);\n}\n\nvoid objectmesh_freefn(object *obj) {\n    objectmesh *m = (objectmesh *) obj;\n    if (m->link) {\n        object *next=NULL;\n        for (object *obj=m->link; obj!=NULL; obj=next) {\n            next=obj->next;\n            object_free(obj);\n        }\n    }\n    if (m->conn) object_free((object *) m->conn);\n}\n\nsize_t objectmesh_sizefn(object *obj) {\n    return sizeof(objectmesh);\n}\n\nobjecttypedefn objectmeshdefn = {\n    .printfn=objectmesh_printfn,\n    .markfn=objectmesh_markfn,\n    .freefn=objectmesh_freefn,\n    .sizefn=objectmesh_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * Create mesh objects\n * ********************************************************************** */\n\nobjectmesh *object_newmesh(unsigned int dim, unsigned int nv, double *v) {\n    objectmesh *new = MORPHO_MALLOC(sizeof(objectmesh));\n\n    if (new) {\n        object_init((object *) new, OBJECT_MESH);\n\n        new->dim=dim;\n        new->conn=NULL;\n        new->vert=object_newmatrix(dim, nv, false);\n        new->link=NULL;\n        if (new->vert) {\n            mesh_link(new, (object *) new->vert);\n            if (dim>0){\n                memcpy(new->vert->elements, v, sizeof(double)*dim*nv);\n            }\n        }\n    }\n\n    return new;\n}\n\n/** Links an object to the mesh; used to keep track of unbound child objects */\nvoid mesh_link(objectmesh *mesh, object *obj) {\n    for (object *e = mesh->link; e!=NULL; e=e->next) if (e==obj) return;\n\n    if (obj->status==OBJECT_ISUNMANAGED && obj->next==NULL) {\n        obj->next=mesh->link;\n        mesh->link=obj;\n    }\n}\n\n/** Delinks an object from the mesh; used to keep track of unbound child objects */\nvoid mesh_delink(objectmesh *mesh, object *obj) {\n    if (mesh->link==obj) { // If the first, simply delink\n        mesh->link=obj->next;\n        return;\n    }\n\n    // Otherwise, search and delink once the object is found\n    for (object *e = mesh->link; e!=NULL; e=e->next) {\n        if (e->next==obj) {\n            e->next=obj->next;\n            break;\n        }\n    }\n}\n\n/* **********************************************************************\n * Manipulate mesh objects\n * ********************************************************************** */\n\n/* -------------------------------------\n * Vertices\n * ------------------------------------- */\n\n/** Gets vertex coordinates */\nbool mesh_getvertexcoordinates(objectmesh *mesh, elementid id, double *out) {\n    double *coords;\n    if (matrix_getcolumn(mesh->vert, id, &coords)) {\n        for (unsigned int i=0; i<mesh->dim; i++) out[i]=coords[i];\n        return true;\n    }\n    return false;\n}\n\n/** Gets vertex coordinates as a list */\nbool mesh_getvertexcoordinatesaslist(objectmesh *mesh, elementid id, double **out) {\n    double *coords=NULL;\n    if (matrix_getcolumn(mesh->vert, id, &coords)) {\n        *out=coords;\n    }\n    return coords;\n}\n\n/** Gets vertex coordinates */\nbool mesh_setvertexcoordinates(objectmesh *mesh, elementid id, double *x) {;\n    return matrix_setcolumn(mesh->vert, id, x);\n}\n\n/** Gets vertex coordinates as a value list */\nbool mesh_getvertexcoordinatesasvalues(objectmesh *mesh, elementid id, value *val) {\n    double *x=NULL; // The vertex positions\n\n    bool success=matrix_getcolumn(mesh->vert, id, &x);\n\n    if (success) {\n        for (unsigned int i=0; i<mesh->dim; i++) val[i]=MORPHO_FLOAT(x[i]);\n    }\n\n    return success;\n}\n\n/** Finds the nearest vertex to a point\n * @param[in] mesh - mesh to search\n * @param[in] x - position [should be of mesh->dim\n * @param[out] id - closest vertex\n * @param[out] separation (optional)\n * @returns true on success. */\nbool mesh_nearestvertex(objectmesh *mesh, double *x, elementid *id, double *separation) {\n    double *vx;\n    double best=0, sep=0;\n    elementid bestid=0;\n\n    for (elementid i=0; i<mesh_nvertices(mesh); i++) {\n        if (!matrix_getcolumn(mesh->vert, i, &vx)) return false;\n        sep=0;\n        for (int k=0; k<mesh->dim; k++) sep+=(vx[k]-x[k])*(vx[k]-x[k]);\n        if (i==0 || sep<best) { best=sep; bestid = i; }\n    }\n    *id = bestid;\n    if (separation) *separation=sqrt(best);\n    return true;\n}\n\n\n/* -------------------------------------\n * The connectivity array\n * ------------------------------------- */\n\n/** Ensures a mesh has a valid connectivity array */\nbool mesh_checkconnectivity(objectmesh *mesh) {\n    if (mesh->conn) return true;\n    unsigned int dim[2]={mesh->dim+1, mesh->dim+1};\n    mesh->conn=object_newarray(2, dim);\n\n    return (mesh->conn);\n}\n\n/** Freezes mesh connectivity, converting subsidiary data structures to fixed but efficient versions */\nvoid mesh_freezeconnectivity(objectmesh *mesh) {\n    if (!mesh_checkconnectivity(mesh)) return;\n\n    for (unsigned int i=0; i<mesh->dim+1; i++) {\n        for (unsigned int j=0; j<mesh->dim+1; j++) {\n            objectsparse *s=mesh_getconnectivityelement(mesh, i, j);\n            if (s) sparse_checkformat(s, SPARSE_CCS, true, false);\n        }\n    }\n}\n\n/** Creates a new blank connectivity element */\nobjectsparse *mesh_newconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col) {\n    objectsparse *out=NULL;\n    unsigned int indx[2] = {row, col};\n\n    out=object_newsparse(NULL, NULL);\n    if (out) array_setelement(mesh->conn, 2, indx, MORPHO_OBJECT(out));\n\n    if (out) mesh_link(mesh, (object *) out);\n\n    return out;\n}\n\n/** Sets a connectivity element */\nbool mesh_setconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col, objectsparse *el) {\n    if (row==col) return false;\n    unsigned int indx[2]={row,col};\n    if (mesh_checkconnectivity(mesh)) {\n        value old = MORPHO_NIL;\n        if ((array_getelement(mesh->conn, 2, indx, &old)==ARRAY_OK) &&\n            MORPHO_ISOBJECT(old)) {\n            object *oel = MORPHO_GETOBJECT(old);\n            mesh_delink(mesh, oel);\n            if (oel->status==OBJECT_ISUNMANAGED) object_free(oel);\n        }\n\n        value val = MORPHO_NIL;\n        if (el) val = MORPHO_OBJECT(el);\n\n        if (array_setelement(mesh->conn, 2, indx, val) == ARRAY_OK) {\n            if (el && el->obj.status==OBJECT_ISUNMANAGED) mesh_link(mesh, (object *) el);\n        }\n    }\n    return false;\n}\n\n/** Gets the connectivity matrix corresponding to (row, col) */\nobjectsparse *mesh_getconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col) {\n    objectsparse *out=NULL;\n    unsigned int indx[2]={row,col};\n    value matrix=MORPHO_NIL;\n\n    if (mesh->conn) array_getelement(mesh->conn, 2, indx, &matrix);\n    if (MORPHO_ISSPARSE(matrix)) {\n        out=MORPHO_GETSPARSE(matrix);\n    }\n\n    return out;\n}\n\n/** How many vertices are in a matrix? */\nelementid mesh_nvertices(objectmesh *mesh) {\n    elementid out = 0;\n    if (mesh->vert) out = mesh->vert->ncols;\n    return out;\n}\n\n/** How many elements are in a connectivity matrix? */\nelementid mesh_nelements(objectsparse *conn) {\n    return conn->ccs.ncols;\n}\n\n/** How many elements exist in a given grade? */\nelementid mesh_nelementsforgrade(objectmesh *mesh, grade g) {\n    elementid count=0;\n    if (g==MESH_GRADE_VERTEX) {\n        count=mesh_nvertices(mesh);\n    } else {\n        objectsparse *conn = mesh_getconnectivityelement(mesh, 0, g);\n        if (conn) count=mesh_nelements(conn);\n    }\n    return count;\n}\n\n/** Maximum grade in the mesh */\ngrade mesh_maxgrade(objectmesh *mesh) {\n    for (grade g=mesh->dim; g>0; g--) {\n        if (mesh_getconnectivityelement(mesh, 0, g)) return g;\n    }\n\n    return 0;\n}\n\n/** Gets connectivitiy infornation for a given element\n * @param[in] conn - the connectivity matrix for the element\n * @param[in] id - the element id\n * @param[out] nentries - number of entries\n * @param[out] entries - a list of entries\n * @returns true on success, false otherwise */\nbool mesh_getconnectivity(objectsparse *conn, elementid id, int *nentries, int **entries) {\n    sparse_checkformat(conn, SPARSE_CCS, true, false);\n\n    if (conn) {\n        return sparseccs_getrowindices(&conn->ccs, id, nentries, entries);\n    }\n\n    return false;\n}\n\n\n/* ------------------------------------------\n * Functions to modify the connectivity array\n * ------------------------------------------ */\n\n/* Data structure to store ntuples of elementids and determine if a\n   particular ntuple is already present. */\ntypedef struct {\n    int n; // number of elements in a tuple\n    elementid maxval; // Maximum elementid that will be used\n    int *elementoffset; // Offset to tuples based on first element of the tuple\n    varray_elementid tuples; // data store\n} ntuplelist;\n\n/* Initialize an ntuplelist data structure */\nvoid ntuplelist_init(ntuplelist *list, int n, elementid maxval) {\n    list->n=n;\n    list->maxval=maxval;\n    list->elementoffset=MORPHO_MALLOC(sizeof(int)*(maxval+1));\n    if (list->elementoffset) for (unsigned int i=0; i<=maxval; i++) list->elementoffset[i]=-1;\n    varray_elementidinit(&list->tuples);\n}\n\n/* Clear ntuplelist data structure */\nvoid ntuplelist_clear(ntuplelist *list) {\n    if (list->elementoffset) MORPHO_FREE(list->elementoffset);\n    varray_elementidclear(&list->tuples);\n}\n\n/* Add a tuple */\nvoid ntuplelist_add(ntuplelist *list, elementid *tuple) {\n    unsigned int posn = list->tuples.count;\n    if (posn>INT_MAX) UNREACHABLE(\"Overflow in ntuplelist_add\");\n    varray_elementidadd(&list->tuples, tuple, list->n);\n    if (list->elementoffset) { // Store an offset to the first\n        varray_elementidwrite(&list->tuples, list->elementoffset[tuple[0]]);\n        list->elementoffset[tuple[0]]=posn;\n    }\n}\n\n/* Compare two tuples */\nbool ntuplelist_compare(int n, elementid *t1, elementid *t2) {\n    for (unsigned int i=0; i<n; i++) if (t1[i]!=t2[i]) return false;\n    return true;\n}\n\n/* Determine whether a tuple is present in an ntuplelist */\nbool ntuplelist_find(ntuplelist *list, elementid *tuple) {\n    if (list->elementoffset) {\n        for (int i=list->elementoffset[tuple[0]]; i>=0; ) {\n            if (ntuplelist_compare(list->n, &list->tuples.data[i], tuple)) return true;\n            i=list->tuples.data[i+list->n];\n        }\n    } else {\n        for (unsigned int i=0; i<list->tuples.count; i+=list->n) {\n            if (ntuplelist_compare(list->n, &list->tuples.data[i], tuple)) return true;\n        }\n    }\n    return false;\n}\n\nobjectsparse *mesh_addgrade(objectmesh *mesh, grade g) {\n    /* Does the grade already exist? */\n    objectsparse *el=mesh_getconnectivityelement(mesh, 0, g);\n    if (el) return el;\n    grade maxG = mesh_maxgrade(mesh);\n    grade h;\n    /* Otherwise, find the next available grade above it */\n    for (h=g+1; (h<=maxG) && (!el); h++) {\n        el=mesh_getconnectivityelement(mesh, 0, h);\n    }\n    /* if this grade doesn't exist and we can't find the next available\n       grade above it return NULL */\n    if (!el) return NULL;\n\n    /* Create a new sparse matrix */\n    objectsparse *new=object_newsparse(NULL, NULL);\n    if (!new) return NULL;\n\n    /* Create an ntuplelist to keep track of elements already created */\n    ntuplelist list;\n    int n = g+1; // Number of elements in the tuple\n    elementid maxvid = mesh_nvertices(mesh)-1; // Highest vertexid\n    ntuplelist_init(&list, n, maxvid);\n\n    int nel, *entries;\n    elementid newid = 0;\n    /* Loop over elements in the higher grade */\n    for (elementid id=0; id<el->ccs.ncols; id++) {\n        /* Get the associated connectivity */\n        if (!mesh_getconnectivity(el, id, &nel, &entries)) break;\n\n        // Initialize n-tuple and counters with [0,1,2...]\n        elementid tuple[n]; // Store the tuple\n        int counter[n], cmax[n]; // Counters\n        for (unsigned int i=0; i<n; i++) {\n            counter[i]=i; tuple[i]=entries[i]; cmax[i]=nel-n+i;\n        }\n\n        if (!ntuplelist_find(&list, tuple)) { // Check if the first tuple exists\n            ntuplelist_add(&list, tuple);\n            for (unsigned int i=0; i<n; i++) sparsedok_insert(&new->dok, tuple[i], newid, MORPHO_NIL);\n            newid++;\n        }\n\n        /* Generate tuples */\n        int k;\n        while (counter[0]<cmax[0]) {\n            counter[n-1]++; // Increment last counter\n            for (k=n-1; k>=0 && counter[k]>cmax[k]; k--) counter[k-1]++; // Carry\n            if (k<n-1) for (unsigned int i=k+1; i<n; i++) counter[i]=counter[i-1]+1;\n\n            // Set up tuple from counter\n            for (unsigned int i=0; i<n; i++) tuple[i]=entries[counter[i]];\n\n            if (!ntuplelist_find(&list, tuple)) { // Check if we have the tuple\n                ntuplelist_add(&list, tuple);\n                for (unsigned int i=0; i<n; i++) sparsedok_insert(&new->dok, tuple[i], newid, MORPHO_NIL);\n                newid++;\n            }\n        }\n\n    }\n\n    ntuplelist_clear(&list);\n\n    mesh_setconnectivityelement(mesh, 0, g, new);\n    mesh_link(mesh, (object *) new);\n    mesh_freezeconnectivity(mesh);\n\n    return new;\n}\n\nvoid mesh_removegrade(objectmesh *mesh, grade g) {\n    /* Does the grade already exist? */\n    objectsparse *el=mesh_getconnectivityelement(mesh, 0, g);\n    grade maxg = mesh_maxgrade(mesh);\n    \n    if (el && g<=maxg) {\n        mesh_setconnectivityelement(mesh, 0, g, NULL);\n        mesh_resetconnectivity(mesh);\n    }\n}\n\n/** Adds a missing grade */\nobjectsparse *mesh_addgradeold(objectmesh *mesh, grade g) {\n    if (g>1) UNREACHABLE(\"mesh_addgrade only supports adding grade 1.\");\n    /* Does the grade already exist? */\n    objectsparse *el=mesh_getconnectivityelement(mesh, 0, g);\n    if (el) return el;\n    grade maxG = mesh_maxgrade(mesh);\n    grade h;\n    /* Otherwise, find the next available grade above it */\n    for (h=g+1; (h<=maxG) && (!el); h++) {\n        el=mesh_getconnectivityelement(mesh, 0, h);\n    }\n    /* if this grade doesn't exist\n    and we can't find the next available grade above it return NULL */\n    if (!el){\n        return NULL;\n    }\n\n    /* Create a new sparse matrix */\n    objectsparse *new=object_newsparse(NULL, NULL);\n    if (!new) return NULL;\n\n    /* Create a temporary sparse matrix to hold connectivity information */\n    objectsparse *temp=object_newsparse(NULL, NULL);\n    if (!temp) {\n        object_free((object *) new);\n        return NULL;\n    }\n\n    int nentries, *entries;\n    elementid newid=0;\n    /* Loop over elements in the higher grade */\n    if (temp) for (elementid id=0; id<el->ccs.ncols; id++) {\n        /* Get the associated connectivity */\n        if (mesh_getconnectivity(el, id, &nentries, &entries)) {\n            /* Now loop over pairs of vertices in the element */\n            /* Should be n-tuples */\n            for (unsigned int j=0; j<nentries; j++) {\n                for (unsigned int k=j+1; k<nentries; k++) {\n                    /* Sort the indices */\n                    int l=(entries[j]<entries[k] ? entries[j] : entries[k]);\n                    int m=(entries[j]<entries[k] ? entries[k] : entries[j]);\n\n                    /* Does this pair already exist? */\n                    if (!sparse_getelement(temp, l, m, NULL)) {\n                        /* Add the element to our new matrix */\n                        sparsedok_insert(&new->dok, l, newid, MORPHO_NIL);\n                        sparsedok_insert(&new->dok, m, newid, MORPHO_NIL);\n                        /* Keep track of elements made */\n                        sparsedok_insert(&temp->dok, l, m, MORPHO_NIL);\n                        newid++;\n                    }\n                }\n            }\n\n        }\n    }\n\n    if (temp) object_free((object *) temp);\n\n    mesh_setconnectivityelement(mesh, 0, g, new);\n    mesh_link(mesh, (object *) new);\n    mesh_freezeconnectivity(mesh);\n\n    return new;\n}\n\n\n/** Internal function used for sorting ids */\nstatic int mesh_compareid(const void *a, const void *b) {\n    return *((int *) a) - *((int *) b);\n}\n\n/** Find elements that match grade g in a connectivity matrix .\n * @param[in] vmatrix - the (g, 0) connectivity matrix (i.e. the vertex raising matrix for grade g)\n * @param[in] g - the grade of interest\n * @param[in] nids - number of vertex ids to match\n * @param[in] ids - list of vertex ids to match\n * @param[in] maxmatches - maximum number of matches to find\n * @param[out] nmatches - the number of matches found\n * @param[out] matches - matched vertex ids\n * @returns true on success, false otherwise */\nbool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches) {\n    int nentries[nids], *entries[nids], length=0, k=0;\n\n    /* Obtain connectivity information from the columns of vertex connectivity matrix */\n    for (unsigned int i=0; i<nids; i++) {\n        if (!mesh_getconnectivity(vmatrix, ids[i], &nentries[i], &entries[i])) return false;\n        length+=nentries[i];\n    }\n\n    /* Copy ids a single list */\n    int sids[length+1]; sids[length]=-1;\n    for (unsigned int i=0; i<nids; i++) {\n        memcpy(sids+k, entries[i], nentries[i]*sizeof(int));\n        k+=nentries[i];\n    }\n\n    /* and sort it */\n    qsort(sids, length, sizeof(int), mesh_compareid);\n\n    //for (unsigned int i=0; i<length; i++) printf(\"%u \", sids[i]);\n    //printf(\"\\n\");\n\n    /* Now look for repeated ids */\n    k=0; *nmatches=0;\n    for (unsigned int i=0; i<length; i++) {\n        if (sids[i+1]==sids[i]) { k++; continue; } // Keep counting if the next one is the same\n        if (k==g) { // if the number of repeats matches the grade we have a match\n            if (*nmatches<maxmatches) matches[*nmatches]=sids[i];\n            *nmatches+=1;\n        }\n        k=0; // Reset counter\n    }\n\n    return true;\n}\n\n/** Adds a missing grade lowering element */\nstatic objectsparse *mesh_addlowermatrix(objectmesh *mesh, unsigned int row, unsigned int col) {\n    objectsparse *new=NULL;\n\n    /* Try to obtain the (row, 0) element (i.e. the grade raising matrix for the row) */\n    objectsparse *traise=mesh_getconnectivityelement(mesh, row, 0);\n    if (!traise) traise=mesh_addconnectivityelement(mesh, row, 0);\n\n    /* Also need to obtain the (0, col) element (i.e. the grade definition) */\n    objectsparse *tlower=mesh_getconnectivityelement(mesh, 0, col);\n\n    if (traise && tlower) {\n        /* Create a new sparse matrix */\n        new=object_newsparse(NULL, NULL);\n        if (!new) return NULL;\n\n        int maxmatches = (col+1)*(col+2)/2+1; // Maximum number of elements for a given grade\n        int nentries, *entries, nmatches, matches[maxmatches];\n\n        /* Loop over elements in the higher grade */\n        for (elementid rid=0; rid<tlower->ccs.ncols; rid++) {\n            /* Get the associated connectivity */\n            if (mesh_getconnectivity(tlower, rid, &nentries, &entries)) {\n                if (mesh_matchelements(traise, row, nentries, entries, maxmatches, &nmatches, matches)) {\n                    if (nmatches>=maxmatches) {\n                        UNREACHABLE(\"Too many connections.\");\n                    }\n\n                    for (unsigned int i=0; i<nmatches; i++) {\n                        sparsedok_insert(&new->dok, matches[i], rid, MORPHO_NIL);\n                    }\n                }\n            }\n        }\n\n        mesh_setconnectivityelement(mesh, row, col, new);\n        mesh_freezeconnectivity(mesh);\n    }\n\n    return new;\n}\n\n/** Fill in a missing connectivity element */\nobjectsparse *mesh_addconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col) {\n    /** Does the element already exist? */\n    objectsparse *el=mesh_getconnectivityelement(mesh, row, col);\n    if (el) return el;\n\n    /* If not, what kind of element is it? */\n    if (row==0) { /* First row */\n        /* Can't add a missing grade; use addgrade instead*/\n    } else if (row<col) { /* A grade lowering element */\n        el=mesh_addlowermatrix(mesh, row, col);\n    } else if (row!=col) { /* A grade raising element */\n        /* Try to obtain the transpose */\n        objectsparse *tlow=mesh_getconnectivityelement(mesh, col, row);\n        if (!tlow) tlow=mesh_addconnectivityelement(mesh, col, row);\n\n        if (tlow) {\n            el=mesh_newconnectivityelement(mesh, row, col);\n            sparse_transpose(tlow, el);\n        }\n    }\n\n    return el;\n}\n\n\n/** Adds an element to a mesh\n * @param[in] mesh the mesh\n * @param[in] g grade of the element\n * @param[in] v elementids */\nbool mesh_addelementwithvertices(objectmesh *mesh, grade g, elementid *v) {\n    if (!mesh_checkconnectivity(mesh)) return false;\n\n    objectsparse *m=mesh_getconnectivityelement(mesh, 0, g);\n    if (!m) m=mesh_newconnectivityelement(mesh, 0, g);\n    if (!m) return false;\n\n    int eid=m->dok.ncols; // The new element is one after the last element\n\n    bool success=true;\n    for (unsigned int i=0; i<g+1; i++) {\n        if (!sparsedok_insert(&m->dok, (int) v[i], eid, MORPHO_NIL)) success=false;\n    }\n\n    return success;\n}\n\n/** Resets connectivity elements other than the first row */\nvoid mesh_resetconnectivity(objectmesh *m) {\n    grade max = mesh_maxgrade(m);\n    \n    for (grade i=1; i<=max; i++) {\n        for (grade j=0; j<=max; j++) {\n            mesh_setconnectivityelement(m, i, j, NULL);\n        }\n    }\n}\n\n/* **********************************************************************\n * Clone\n * ********************************************************************** */\n\n/** Clones a mesh object */\nobjectmesh *mesh_clone(objectmesh *mesh) {\n    objectmesh *new = object_newmesh(mesh->dim, mesh->vert->ncols, mesh->vert->elements);\n\n    if (new) {\n        if (mesh->conn &&\n            mesh_checkconnectivity(new)) {\n\n            grade max = mesh_maxgrade(mesh);\n            for (grade i=0; i<=max; i++) {\n                for (grade j=0; j<=max; j++) {\n                    objectsparse *conn=mesh_getconnectivityelement(mesh, i, j);\n\n                    if (conn) {\n                        objectsparse *cl=sparse_clone(conn);\n                        if (cl) mesh_setconnectivityelement(new, i, j, cl);\n                    }\n                }\n            }\n\n        }\n    }\n\n    return new;\n}\n\n/* **********************************************************************\n * Symmetries\n * ********************************************************************** */\n\n/** Adds a symmetry to a mesh. */\nbool mesh_addsymmetry(vm *v, objectmesh *mesh, value symmetry, objectselection *sel) {\n    value method=MORPHO_NIL;\n    objectstring s = MORPHO_STATICSTRING(MESH_TRANSFORM_METHOD);\n\n    objectsparse *sym=mesh_getconnectivityelement(mesh, MESH_GRADE_VERTEX, MESH_GRADE_VERTEX);\n\n    double x[mesh->dim];\n    objectmatrix posn = MORPHO_STATICMATRIX(x, mesh->dim, 1);\n\n    elementid nv = mesh_nvertices(mesh);\n\n    value arg = MORPHO_OBJECT(&posn);\n    value ret = MORPHO_NIL;\n\n    if (morpho_lookupmethod(symmetry, MORPHO_OBJECT(&s), &method)) {\n        /* Loop over vertices */\n        for (elementid i=0; i<nv; i++) {\n            /* Read the vertex coordinates into x */\n            if (!mesh_getvertexcoordinates(mesh, i, x)) return false;\n            /* Call transformation */\n            if (!morpho_invoke(v, symmetry, method, 1, &arg, &ret)) return false;\n\n            if (MORPHO_ISMATRIX(ret)) {\n                objectmatrix *newvert=MORPHO_GETMATRIX(ret);\n                elementid nearest;\n                double sep;\n                if (!mesh_nearestvertex(mesh, newvert->elements, &nearest, &sep)) return false;\n\n                if (sep<MESH_NEARESTPOINTEPS) {\n                    /* Only add a symmetry matrix if we have a match */\n                    if (!sym) {\n                        sym=mesh_newconnectivityelement(mesh, MESH_GRADE_VERTEX, MESH_GRADE_VERTEX);\n                        sparsedok_setdimensions(&sym->dok, nv, nv);\n                    }\n                    if (!sym) return false;\n\n                    sparse_setelement(sym, i, nearest, symmetry);\n                }\n            }\n        }\n\n    } else morpho_runtimeerror(v, MESH_ADDSYMMSNGTRNSFRM);\n\n    return false;\n}\n\n/* Get a list of synonymous elements for a given element */\nbool mesh_getsynonyms(objectmesh *mesh, grade g, elementid id, varray_elementid *synonymids) {\n    objectsparse *sym = mesh_getconnectivityelement(mesh, g, g);\n    if (sym) {\n        synonymids->count=0;\n        void *ctr=sparsedok_loopstart(&sym->dok);\n        int row, col;\n        while (sparsedok_loop(&sym->dok, &ctr, &row, &col)) {\n            if (id==row) varray_elementidwriteunique(synonymids, col);\n            if (id==col) varray_elementidwriteunique(synonymids, row);\n        }\n    }\n\n    return true;\n}\n\nvoid varray_elementidwriteunique(varray_elementid *list, elementid id) {\n    for (unsigned int i=0; i<list->count; i++) if (list->data[i]==id) return;\n    varray_elementidwrite(list, id);\n}\n\n/** Insert ids for a given element\n * @param[in] conn - Connectivity matrix\n * @param[in] id - id to insert\n * @param[in] ignoreid - whether or not to include id if it is found\n * @param[out] out - varray to hold output */\nvoid mesh_insertidsforelement(objectsparse *conn, elementid id, bool ignore, elementid ignoreid, varray_elementid *out) {\n    int nids, *entries;\n    if (sparseccs_getrowindices(&conn->ccs, id, &nids, &entries)) {\n        for (unsigned int i=0; i<nids; i++) {\n            if (ignore && entries[i]==ignoreid) continue;\n            varray_elementidwriteunique(out, entries[i]);\n        }\n    }\n}\n\n#define MAX_NEIGHBORS 64\nint mesh_findneighbors(objectmesh *mesh, grade g, elementid id, grade target, varray_elementid *neighbors) {\n    int nvert, *vids, vvid=id; // List of vertices in the element\n\n    /* If the element is not a point, find all vertices associated with that point */\n    if (g>0) {\n        objectsparse *down = mesh_getconnectivityelement(mesh, 0, g);\n        sparseccs_getrowindices(&down->ccs, id, &nvert, &vids);\n    } else {\n        nvert = 1; vids=&vvid;\n    }\n\n    objectsparse *conn = mesh_getconnectivityelement(mesh, target, 0);\n    // Now find the neighboring elements\n    if (conn && sparse_checkformat(conn, SPARSE_CCS, true, false)) {\n        for (unsigned int k=0; k<nvert; k++) {\n            mesh_insertidsforelement(conn, vids[k], g==target, id, neighbors);\n        }\n    }\n\n    /* Now find any vertices that are related to an element vertex through symmetries */\n    objectsparse *sym = mesh_getconnectivityelement(mesh, 0, 0);\n    int nsymids=0, *symids;\n\n    if (sym && sparse_checkformat(sym, SPARSE_CCS, true, false)) {\n        for (unsigned int k=0; k<nvert; k++) { // Loop over vertices in the element\n            // Is this vertex an image vertex of another element?\n            if (sparseccs_getrowindices(&sym->ccs, vids[k], &nsymids, &symids)) {\n                for (unsigned int k=0; k<nsymids; k++) {\n                    mesh_insertidsforelement(conn, symids[k], g==target, id, neighbors);\n                }\n            }\n\n            // Is this vertex a target vertex of any image vertices\n            int nrids=0, rids[MAX_NEIGHBORS];\n            if (sparseccs_getcolindicesforrow(&sym->ccs, id, MAX_NEIGHBORS, &nrids, rids)) {\n                for (unsigned int k=0; k<nrids; k++) {\n                    mesh_insertidsforelement(conn, rids[k], g==target, id, neighbors);\n                }\n            }\n            if (nrids>=MAX_NEIGHBORS) UNREACHABLE(\"Too many neighbors.\");\n\n        }\n    }\n\n    return (neighbors->count);\n}\n\n/* **********************************************************************\n * Mesh loader\n * ********************************************************************** */\n\nstatic unsigned int mesh_nsections = 4;\nstatic char *mesh_sections[] = {MESH_VERTSECTION, MESH_EDGESECTION, MESH_FACESECTION, MESH_VOLSECTION};\nstatic size_t mesh_slength[4];\n\n/** Checks whether line matches a section marker.\n * @param[in] line line to match\n * @param[out] g if line matches, grade is updated\n * @returns true if line matched a section marker; false otherwise */\nstatic bool mesh_checksection(char *line, grade *g) {\n    /* Check if the line starts with a section marker */\n    unsigned int i;\n    for (i=0; i<mesh_nsections; i++) {\n        if (strncmp(line, mesh_sections[i], mesh_slength[i])==0) {\n            *g=i; return true;\n        }\n    }\n\n    return false;\n}\n\n/** Loads a .mesh file. */\nobjectmesh *mesh_load(vm *v, char *file) {\n    objectmesh *out = NULL;\n    error err;\n    error_init(&err);\n\n    /* Open the file */\n    FILE *f = file_openrelative(file, \"r\");\n    if (!f) {\n        morpho_runtimeerror(v, MESH_FILENOTFOUND, file);\n        return NULL;\n    }\n\n    grade g=-1; /* The current grade we're loading */\n    int ndim=-1; /* Dimensionality of the mesh */\n    int nv=0; /* Number of vertices */\n    int fline=0;\n\n    value val[5]; /* Values to read in per line */\n    unsigned int n; /* Number of values read */\n\n    dictionary vdict; // Record how the file's vertex ids map to ours.\n    dictionary_init(&vdict);\n\n    varray_double vert; // Hold the vertex positions\n    varray_doubleinit(&vert);\n\n    varray_char line; // Buffer to hold a line from the file\n    varray_charinit(&line);\n\n    /* Load vertices */\n    do {\n        line.count=0; // Reset buffer contents.\n        file_readlineintovarray(f, &line);\n        fline++;\n\n        if (!mesh_checksection(line.data, &g)) {\n            /* Convert the string to an array of values */\n            if (parse_stringtovaluearray(line.data, 5, val, &n, &err)) {\n                if (n>0 && g==0) {\n                    /* Check dimensionality */\n                    if (ndim<0) ndim=n-1;\n                    else if (n-1!=ndim) {\n                        morpho_runtimeerror(v, MESH_LOADVERTEXDIM, fline);\n                        goto meshload_cleanup;\n                    }\n\n                    /* Add the vertex */\n                    for (unsigned int k=0; k<ndim; k++) {\n                        double coord;\n                        if (!morpho_valuetofloat(val[k+1], &coord)) {\n                            morpho_runtimeerror(v, MESH_LOADVERTEXCOORD, fline);\n                            goto meshload_cleanup;\n                        }\n                        varray_doubleadd(&vert, &coord, 1);\n                    }\n\n                    /* Keey a record of the id in the file */\n                    dictionary_insert(&vdict, val[0], MORPHO_INTEGER(nv));\n                    nv++;\n                }\n            } else {\n                morpho_runtimeerror(v, MESH_LOADPARSEERR, fline);\n                goto meshload_cleanup;\n            }\n        }\n    } while (!feof(f) && g<1);\n\n    /* Create the mesh */\n    out=object_newmesh(ndim, nv, vert.data);\n\n    /* Now continue to parse the file for the remaining elements */\n    while (!feof(f)) {\n        line.count=0; // Reset buffer contents.\n        file_readlineintovarray(f, &line);\n        fline++;\n        \n        /* Check if this is a section header */\n        if (line.count>0 && !mesh_checksection(line.data, &g)) {\n            /* Convert the string to an array of values */\n            if (parse_stringtovaluearray(line.data, 5, val, &n, &err)) {\n                if (n>0) {\n                    elementid vid[g+1];\n                    /* Check number of vertices is consistent with the grade */\n                    if (n-1!=g+1) {\n                        morpho_runtimeerror(v, MESH_LOADVERTEXNUM, fline);\n                        goto meshload_cleanup;\n                    }\n                    \n                    for (unsigned int i=0; i<g+1; i++) {\n                        /* Look up our corresponding vertex id */\n                        value vx;\n                        if (dictionary_get(&vdict, val[i+1], &vx)) {\n                            if (MORPHO_ISINTEGER(vx)) {\n                                vid[i]=MORPHO_GETINTEGERVALUE(vx);\n                            } else {\n                                morpho_runtimeerror(v, MESH_LOADVERTEXID, fline);\n                                goto meshload_cleanup;\n                            }\n                        } else {\n                            morpho_runtimeerror(v, MESH_LOADVERTEXNOTFOUND, fline);\n                            goto meshload_cleanup;\n                        }\n                    }\n                    \n                    mesh_addelementwithvertices(out, g, vid);\n                }\n            }\n        }\n    }\n\n    mesh_freezeconnectivity(out);\n\nmeshload_cleanup:\n\n    fclose(f);\n    \n    varray_charclear(&line);\n    dictionary_clear(&vdict);\n    varray_doubleclear(&vert);\n\n    return out;\n}\n\n/* **********************************************************************\n * Mesh exporter\n * ********************************************************************** */\n\nbool mesh_save(objectmesh *m, char *file) {\n    /* Open the file */\n    FILE *f = file_openrelative(file, \"w\");\n    if (!f) return false;\n\n    /* Export vertices */\n    fprintf(f, \"%s\\n\\n\", MESH_VERTSECTION);\n    for (unsigned int i=0; i<mesh_nvertices(m); i++) {\n        fprintf(f, \"%i \", i+1); // Keep the mesh files 1-indexed\n\n        for (unsigned int j=0; j<m->vert->nrows; j++) {\n            double x;\n            if (matrix_getelement(m->vert, j, i, &x)) {\n                fprintf(f, \"%g \", x);\n            }\n        }\n        fprintf(f, \"\\n\");\n    }\n\n    fprintf(f, \"\\n\");\n\n    for (grade g=1; g<=m->dim; g++) {\n        objectsparse *conn=mesh_getconnectivityelement(m, 0, g);\n\n        if (conn) {\n            fprintf(f, \"%s\\n\\n\", mesh_sections[g]);\n            int nentries=0, *entries=NULL;\n            int nel = mesh_nelements(conn);\n\n            for (elementid id=0; id<nel; id++) {\n                if (mesh_getconnectivity(conn, id, &nentries, &entries)) {\n                    fprintf(f, \"%i \", id+1);\n                    for (int j=0; j<nentries; j++) {\n                        fprintf(f, \"%i \", entries[j]+1);\n                    }\n                    fprintf(f, \"\\n\");\n                }\n            }\n        }\n    }\n\n    fclose(f);\n    return true;\n}\n\n/* **********************************************************************\n * Mesh veneer class\n * ********************************************************************** */\n\n/** Constructs a Matrix object */\nvalue mesh_constructor(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectmesh *new=NULL;\n\n    if (nargs==1 && MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        new=mesh_load(v, MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)));\n    } else if (nargs==0) {\n        // empty mesh constructor\n        new=object_newmesh(0, 0, NULL);\n    }\n    else {\n        morpho_runtimeerror(v,MESH_CONSTRUCTORARGS);\n    }\n\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\n/** Print the mesh */\nvalue Mesh_save(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n\n    if (nargs==1 && MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        mesh_save(m, MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)));\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Print the mesh */\nvalue Mesh_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISMESH(self)) return Object_print(v, nargs, args);\n    \n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    morpho_printf(v, \"<Mesh:\");\n    if (m->vert) morpho_printf(v, \" %u vertices\", mesh_nvertices(m));\n    morpho_printf(v, \">\");\n    return MORPHO_NIL;\n}\n\n/** Get the vertex matrix */\nvalue Mesh_vertexmatrix(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (m->vert) out=MORPHO_OBJECT(m->vert);\n    mesh_delink(m, (object *) m->vert);\n    morpho_bindobjects(v, 1, &out);\n\n    return out;\n}\n\n/** Set the vertex matrix */\nvalue Mesh_setvertexmatrix(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n\n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *mat = MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n\n        if (m->dim>0 && (mesh_nvertices(m)!=mat->ncols || m->vert->nrows!=mat->nrows)) {\n            morpho_runtimeerror(v, MESH_VERTMTRXDIM);\n        } else {\n            if (m->dim==0) m->dim=mat->nrows;\n            m->vert=mat;\n        }\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Get position of a vertex */\nvalue Mesh_vertexposition(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        unsigned int id=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        double *vals;\n\n        if (matrix_getcolumn(m->vert, id, &vals)) {\n            objectmatrix *new=object_newmatrix(m->dim, 1, true);\n            if (new) {\n                matrix_setcolumn(new, 0, vals);\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else morpho_runtimeerror(v, MESH_INVLDID);\n    } else morpho_runtimeerror(v, MESH_VRTPSNARGS);\n\n    return out;\n}\n\n/** Set position of a vertex */\nvalue Mesh_setvertexposition(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n\n    if (nargs==2 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISMATRIX(MORPHO_GETARG(args, 1))) {\n        unsigned int id=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        objectmatrix *mat = MORPHO_GETMATRIX(MORPHO_GETARG(args, 1));\n\n        if (!matrix_setcolumn(m->vert, id, mat->elements)) morpho_runtimeerror(v, MESH_INVLDID);\n    } else morpho_runtimeerror(v, MESH_STVRTPSNARGS);\n\n    return MORPHO_NIL;\n}\n\n/** Gets a connectivity matrix */\nvalue Mesh_connectivitymatrix(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==2 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) {\n        unsigned int row=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        unsigned int col=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1));\n\n        objectsparse *s=mesh_getconnectivityelement(m, row, col);\n        if (!s && row>0 && row!=col) s=mesh_addconnectivityelement(m, row, col);\n        if (s) {\n            mesh_delink(m, (object *) s);\n            out=MORPHO_OBJECT(s);\n            morpho_bindobjects(v, 1, &out);\n        }\n    } else morpho_runtimeerror(v, MESH_CNNMTXARGS);\n\n    return out;\n}\n\n/** Clears any connectivity matrices */\nvalue Mesh_resetconnectivity(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n\n    mesh_resetconnectivity(m);\n\n    return MORPHO_NIL;\n}\n\n/** Adds a grade to a mesh */\nvalue Mesh_addgrade(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        unsigned int g=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        if (g==0) return MORPHO_NIL;\n        objectsparse *s=mesh_getconnectivityelement(m, 0, g);\n        if (!s) {\n            s=mesh_addgrade(m, g);\n            if(!s){\n                morpho_runtimeerror(v, MESH_ADDGRDOOB,g,mesh_maxgrade(m));\n                return MORPHO_NIL;\n\n            }\n        }\n\n    } else if (nargs==2 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n               MORPHO_ISSPARSE(MORPHO_GETARG(args, 1))) {\n        unsigned int grade=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        objectsparse *s=MORPHO_GETSPARSE(MORPHO_GETARG(args, 1));\n\n        if (s && grade>0) mesh_setconnectivityelement(m, 0, grade, s);\n        mesh_freezeconnectivity(m);\n    } else morpho_runtimeerror(v, MESH_ADDGRDARGS);\n\n    return out;\n}\n\n/** Removes a grade from a mesh */\nvalue Mesh_removegrade(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        unsigned int g=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        if (g==0) return MORPHO_NIL;\n        mesh_removegrade(m, g);\n    } else morpho_runtimeerror(v, MESH_ADDGRDARGS);\n\n    return out;\n}\n\n/** Adds a symmetry to a mesh */\nvalue Mesh_addsymmetry(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    value obj = MORPHO_NIL;\n    objectselection *sel = NULL;\n\n    if (nargs>0 && MORPHO_ISOBJECT(MORPHO_GETARG(args, 0))) {\n        obj=MORPHO_GETARG(args, 0);\n    } else morpho_runtimeerror(v, MESH_ADDSYMARGS);\n\n    if (nargs>1) {\n        if (MORPHO_ISSELECTION(MORPHO_GETARG(args, 1))) {\n            sel = MORPHO_GETSELECTION(MORPHO_GETARG(args, 1));\n        } else morpho_runtimeerror(v, MESH_ADDSYMARGS);\n    }\n\n    if (!MORPHO_ISNIL(obj)) {\n        mesh_addsymmetry(v, m, obj, sel);\n    }\n\n    return MORPHO_NIL;\n}\n\n/* Returns the highest grade present */\nvalue Mesh_maxgrade(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n\n    return MORPHO_INTEGER(mesh_maxgrade(m));\n}\n\n/** Counts the number of elements for a given grade, or returns the number of vertices if no argument is supplied. */\nvalue Mesh_count(vm *v, int nargs, value *args) {\n    objectmesh *m=MORPHO_GETMESH(MORPHO_SELF(args));\n    grade g=0;\n    value out=MORPHO_INTEGER(0);\n\n    if (nargs>0 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        g = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n    }\n\n    if (g==0) {\n        out = MORPHO_INTEGER(m->vert->ncols);\n    } else {\n        objectsparse *s = mesh_getconnectivityelement(m, 0, g);\n        if (s) out = MORPHO_INTEGER(s->ccs.ncols);\n    }\n\n    return out;\n}\n\n/** Clones a mesh */\nvalue Mesh_clone(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectmesh *a=MORPHO_GETMESH(MORPHO_SELF(args));\n    objectmesh *new=mesh_clone(a);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    return out;\n}\n\nMORPHO_BEGINCLASS(Mesh)\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Mesh_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SAVE_METHOD, Mesh_save, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_VERTEXMATRIX_METHOD, Mesh_vertexmatrix, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_SETVERTEXMATRIX_METHOD, Mesh_setvertexmatrix, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_VERTEXPOSITION_METHOD, Mesh_vertexposition, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_SETVERTEXPOSITION_METHOD, Mesh_setvertexposition, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_RESETCONNECTIVITY_METHOD, Mesh_resetconnectivity, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_CONNECTIVITYMATRIX_METHOD, Mesh_connectivitymatrix, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_ADDGRADE_METHOD, Mesh_addgrade, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_REMOVEGRADE_METHOD, Mesh_removegrade, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_ADDSYMMETRY_METHOD, Mesh_addsymmetry, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MESH_MAXGRADE_METHOD, Mesh_maxgrade, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Mesh_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Mesh_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nvoid mesh_initialize(void) {\n    // integrate_test();\n    \n    objectmeshtype=object_addtype(&objectmeshdefn);\n    \n    for (unsigned int i=0; i<mesh_nsections; i++) mesh_slength[i]=strlen(mesh_sections[i]);\n\n    builtin_addfunction(MESH_CLASSNAME, mesh_constructor, BUILTIN_FLAGSEMPTY);\n\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value meshclass=builtin_addclass(MESH_CLASSNAME, MORPHO_GETCLASSDEFINITION(Mesh), objclass);\n    object_setveneerclass(OBJECT_MESH, meshclass);\n\n    morpho_defineerror(MESH_FILENOTFOUND, ERROR_HALT, MESH_FILENOTFOUND_MSG);\n    morpho_defineerror(MESH_VERTMTRXDIM, ERROR_HALT, MESH_VERTMTRXDIM_MSG);\n    morpho_defineerror(MESH_LOADVERTEXDIM, ERROR_HALT, MESH_LOADVERTEXDIM_MSG);\n    morpho_defineerror(MESH_LOADVERTEXCOORD, ERROR_HALT, MESH_LOADVERTEXCOORD_MSG);\n    morpho_defineerror(MESH_LOADPARSEERR, ERROR_HALT, MESH_LOADPARSEERR_MSG);\n    morpho_defineerror(MESH_LOADVERTEXNUM, ERROR_HALT, MESH_LOADVERTEXNUM_MSG);\n    morpho_defineerror(MESH_LOADVERTEXID, ERROR_HALT, MESH_LOADVERTEXID_MSG);\n    morpho_defineerror(MESH_LOADVERTEXNOTFOUND, ERROR_HALT, MESH_LOADVERTEXNOTFOUND_MSG);\n    morpho_defineerror(MESH_STVRTPSNARGS, ERROR_HALT, MESH_STVRTPSNARGS_MSG);\n    morpho_defineerror(MESH_VRTPSNARGS, ERROR_HALT, MESH_VRTPSNARGS_MSG);\n    morpho_defineerror(MESH_INVLDID, ERROR_HALT, MESH_INVLDID_MSG);\n    morpho_defineerror(MESH_CNNMTXARGS, ERROR_HALT, MESH_CNNMTXARGS_MSG);\n    morpho_defineerror(MESH_ADDGRDARGS, ERROR_HALT, MESH_ADDGRDARGS_MSG);\n    morpho_defineerror(MESH_ADDGRDOOB, ERROR_HALT, MESH_ADDGRDOOB_MSG);\n    morpho_defineerror(MESH_ADDSYMARGS, ERROR_HALT, MESH_ADDSYMARGS_MSG);\n    morpho_defineerror(MESH_ADDSYMMSNGTRNSFRM, ERROR_HALT, MESH_ADDSYMMSNGTRNSFRM_MSG);\n    morpho_defineerror(MESH_CONSTRUCTORARGS, ERROR_HALT, MESH_CONSTRUCTORARGS_MSG);\n}\n\n#endif\n"
  },
  {
    "path": "src/geometry/mesh.h",
    "content": "/** @file mesh.h\n *  @author T J Atherton\n *\n *  @brief Mesh class and associated functionality\n*/\n\n\n#ifndef mesh_h\n#define mesh_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include \"varray.h\"\n#include \"matrix.h\"\n#include \"sparse.h\"\n\n/* -------------------------------------------------------\n * Mesh object\n * ------------------------------------------------------- */\n\nextern objecttype objectmeshtype;\n#define OBJECT_MESH objectmeshtype\n\ntypedef struct {\n    object obj;\n    unsigned int dim;\n    objectmatrix *vert;\n    objectarray *conn;\n    object *link;\n} objectmesh;\n\n/** Tests whether an object is a mesh */\n#define MORPHO_ISMESH(val) object_istype(val, OBJECT_MESH)\n\n/** Gets the object as a mesh */\n#define MORPHO_GETMESH(val)   ((objectmesh *) MORPHO_GETOBJECT(val))\n\n/** Creates a mesh object */\nobjectmesh *object_newmesh(unsigned int dim, unsigned int nv, double *v);\n\n/* -------------------------------------------------------\n * Mesh class\n * ------------------------------------------------------- */\n\n#define MESH_CLASSNAME \"Mesh\"\n\n#define MESH_VERTSECTION \"vertices\"\n#define MESH_EDGESECTION \"edges\"\n#define MESH_FACESECTION \"faces\"\n#define MESH_VOLSECTION  \"volumes\"\n\n#define MESH_VERTEXMATRIX_METHOD           \"vertexmatrix\"\n#define MESH_SETVERTEXMATRIX_METHOD        \"setvertexmatrix\"\n\n#define MESH_VERTEXPOSITION_METHOD         \"vertexposition\"\n#define MESH_SETVERTEXPOSITION_METHOD      \"setvertexposition\"\n\n#define MESH_RESETCONNECTIVITY_METHOD      \"resetconnectivity\"\n#define MESH_CONNECTIVITYMATRIX_METHOD     \"connectivitymatrix\"\n#define MESH_ADDGRADE_METHOD               \"addgrade\"\n#define MESH_REMOVEGRADE_METHOD            \"removegrade\"\n#define MESH_MAXGRADE_METHOD               \"maxgrade\"\n#define MESH_ADDSYMMETRY_METHOD            \"addsymmetry\"\n\n#define MESH_TRANSFORM_METHOD              \"transform\"\n\ntypedef int grade;\ntypedef int elementid;\n\nDECLARE_VARRAY(elementid, elementid);\n\n#define MESH_GRADE_VERTEX 0\n#define MESH_GRADE_LINE 1\n#define MESH_GRADE_AREA 2\n#define MESH_GRADE_VOLUME 3\n\n#define MESH_VERTMTRXDIM                     \"MshVrtMtrxDim\"\n#define MESH_VERTMTRXDIM_MSG                 \"Vertex matrix dimensions inconsistent with mesh.\"\n\n#define MESH_LOADVERTEXDIM                   \"MshLdVrtDim\"\n#define MESH_LOADVERTEXDIM_MSG               \"Vertex has inconsistent dimensions at line %i.\"\n\n#define MESH_LOADVERTEXCOORD                 \"MshLdVrtCrd\"\n#define MESH_LOADVERTEXCOORD_MSG             \"Vertex has nonnumerical coordinates at line %i.\"\n\n#define MESH_LOADPARSEERR                    \"MshLdPrsErr\"\n#define MESH_LOADPARSEERR_MSG                \"Parse error in mesh file at line %i.\"\n\n#define MESH_LOADVERTEXNUM                   \"MshLdVrtNm\"\n#define MESH_LOADVERTEXNUM_MSG               \"Element has incorrect number of vertices at line %i.\"\n\n#define MESH_LOADVERTEXID                    \"MshLdVrtId\"\n#define MESH_LOADVERTEXID_MSG                \"Vertex id must be an integer at line %i.\"\n\n#define MESH_LOADVERTEXNOTFOUND              \"MshLdVrtNtFnd\"\n#define MESH_LOADVERTEXNOTFOUND_MSG          \"Vertex not found at line %i.\"\n\n#define MESH_FILENOTFOUND                    \"MshFlNtFnd\"\n#define MESH_FILENOTFOUND_MSG                \"Mesh file '%s' not found.\"\n\n#define MESH_STVRTPSNARGS                    \"MshStVrtPsnArgs\"\n#define MESH_STVRTPSNARGS_MSG                \"Method 'setvertexposition' expects a vertex id and a position matrix as arguments.\"\n\n#define MESH_VRTPSNARGS                      \"MshVrtPsnArgs\"\n#define MESH_VRTPSNARGS_MSG                  \"Method 'vertexposition' expects a vertex id as the argument.\"\n\n#define MESH_CNNMTXARGS                      \"MshCnnMtxArgs\"\n#define MESH_CNNMTXARGS_MSG                  \"Method 'connectivitymatrix' expects integer arguments.\"\n\n#define MESH_ADDGRDARGS                      \"MshAddGrdArgs\"\n#define MESH_ADDGRDARGS_MSG                  \"Method 'addgrade' expects either an integer grade and, optionally, a sparse connectivity matrix.\"\n\n#define MESH_ADDGRDOOB                       \"MshAddGrdOutOfBnds\"\n#define MESH_ADDGRDOOB_MSG                   \"Cannot add elements of grade %d to mesh with max grade %d\"\n\n#define MESH_INVLDID                         \"MshInvldId\"\n#define MESH_INVLDID_MSG                     \"Invalid element id.\"\n\n#define MESH_ADDSYMARGS                      \"MshAddSymArgs\"\n#define MESH_ADDSYMARGS_MSG                  \"Method 'addsymmetry' expects an object that provides a method 'transform' and an optional selection.\"\n\n#define MESH_ADDSYMMSNGTRNSFRM               \"MshAddSymMsngTrnsfrm\"\n#define MESH_ADDSYMMSNGTRNSFRM_MSG           \"Method 'addsymmetry' expects an object that provides a method 'transform'.\"\n\n#define MESH_ADDSYMNOMTCH                    \"MshAddSymNoMtch\"\n#define MESH_ADDSYMNOMTCH_MSG                \"Addsymmetry found no matching vertices.\"\n\n#define MESH_CONSTRUCTORARGS                  \"MshArgs\"\n#define MESH_CONSTRUCTORARGS_MSG              \"Mesh expects either a single file name or no argurments\"\n\n/* Tolerances */\n\n/** This controls how close two points can be before they're indistinct */\n#define MESH_NEARESTPOINTEPS 1e-10\n\nvoid varray_elementidwriteunique(varray_elementid *list, elementid id);\n\nelementid mesh_nvertices(objectmesh *mesh);\nelementid mesh_nelements(objectsparse *conn);\nelementid mesh_nelementsforgrade(objectmesh *mesh, grade g);\ngrade mesh_maxgrade(objectmesh *mesh);\n\nbool mesh_checkconnectivity(objectmesh *mesh);\nobjectsparse *mesh_newconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col);\nobjectsparse *mesh_addgrade(objectmesh *mesh, grade g);\nobjectsparse *mesh_addconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col);\nobjectsparse *mesh_getconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col);\n\nbool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches);\n\nbool mesh_getconnectivity(objectsparse *conn, elementid id, int *nentries, int **entries);\nvoid mesh_freezeconnectivity(objectmesh *mesh);\nvoid mesh_resetconnectivity(objectmesh *m);\n\nbool mesh_getvertexcoordinates(objectmesh *mesh, elementid id, double *val);\nbool mesh_getvertexcoordinatesaslist(objectmesh *mesh, elementid id, double **out);\nbool mesh_getvertexcoordinatesasvalues(objectmesh *mesh, elementid id, value *val);\n\nbool mesh_getsynonyms(objectmesh *mesh, grade g, elementid id, varray_elementid *synonymids);\nint mesh_findneighbors(objectmesh *mesh, grade g, elementid id, grade target, varray_elementid *neighbors);\n\nvoid mesh_initialize(void);\n\n#endif\n\n#endif /* mesh_h */\n"
  },
  {
    "path": "src/geometry/selection.c",
    "content": "/** @file selection.c\n *  @author T J Atherton\n *\n *  @brief Selections\n */\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include \"morpho.h\"\n#include \"object.h\"\n#include \"builtin.h\"\n#include \"classes.h\"\n#include \"matrix.h\"\n#include \"sparse.h\"\n#include \"mesh.h\"\n#include \"selection.h\"\n\n/* **********************************************************************\n * Selection object definitions\n * ********************************************************************** */\n\nobjecttype objectselectiontype;\n\n/** Selection object definitions */\nvoid objectselection_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Selection>\");\n}\n\nvoid objectselection_freefn(object *obj) {\n    objectselection *s = (objectselection *) obj;\n    selection_clear(s);\n}\n\nsize_t objectselection_sizefn(object *obj) {\n    return sizeof(objectselection)+sizeof(objectsparse *)*((objectselection *) obj)->ngrades;\n}\n\nobjecttypedefn objectselectiondefn = {\n    .printfn=objectselection_printfn,\n    .markfn=NULL,\n    .freefn=objectselection_freefn,\n    .sizefn=objectselection_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* **********************************************************************\n * Selection object constructor\n * ********************************************************************** */\n\n/** Create a new empty selection object */\nobjectselection *object_newselection(objectmesh *mesh) {\n    unsigned int ngrades = mesh->dim+1;\n    objectselection *new=(objectselection *) object_new(sizeof(objectselection)+sizeof(dictionary)*ngrades, OBJECT_SELECTION);\n    \n    if (new) {\n        new->mesh=mesh;\n        new->ngrades=ngrades;\n        new->mode=SELECT_NONE; \n        for (unsigned int i=0; i<ngrades; i++) dictionary_init(&new->selected[i]);\n    }\n    \n    return new;\n}\n\n/** Clones a selection */\nobjectselection *selection_clone(objectselection *sel) {\n    objectselection *new=object_newselection(sel->mesh);\n    \n    if (new) {\n        new->mode=sel->mode;\n        for (unsigned int i=0; i<sel->ngrades; i++) dictionary_copy(&sel->selected[i], &new->selected[i]);\n    }\n    \n    return new;\n}\n\n/** Clears all data structures associated with a selection */\nvoid selection_clear(objectselection *s) {\n    for (grade i=0; i<s->ngrades; i++) {\n        dictionary_clear(&s->selected[i]);\n    }\n}\n\n/** Removes a grade from a selection */\nvoid selection_removegrade(objectselection *sel, grade g) {\n    dictionary_clear(&sel->selected[g]);\n}\n\n/** Selects an element */\nbool selection_selectelement(objectselection *sel, grade g, elementid id) {\n    if (sel->mode==SELECT_ALL) return true; /* No need to store info in the selectall scenario */\n    \n    sel->mode=SELECT_SOME; // Ensure that we change modes\n    \n    return dictionary_insert(&sel->selected[g], MORPHO_INTEGER(id), MORPHO_NIL);\n}\n\n/** Attempts to change the grade of a selection by raising\n * @param[in] sel - selection to change\n * @param[in] g - grade to add\n * @param[in] includepartials - whether to include partially selected elements or not  */\nbool selection_addgraderaise(objectselection *sel, grade g, bool includepartials) {\n    // Get the corresponding grade from the mesh\n    objectsparse *conn=mesh_getconnectivityelement(sel->mesh, 0, g);\n    if (!conn) return false;\n    \n    int nentries, *entries=NULL;\n    \n    for (elementid id=0; id<conn->ccs.ncols; id++) {\n        if (mesh_getconnectivity(conn, id, &nentries, &entries)) {\n            int k=0;\n            for (int j=0; j<nentries; j++) {\n                if (dictionary_get(&sel->selected[0], MORPHO_INTEGER(entries[j]), NULL)) k++;\n            }\n            if ((includepartials && k>0) || (k==nentries)) {\n                dictionary_insert(&sel->selected[g], MORPHO_INTEGER(id), MORPHO_NIL);\n            }\n        }\n    }\n    \n    return true;\n}\n\n/** Attempts to change the grade of a selection by lowering\n * @param[in] sel - selection to change\n * @param[in] g - grade to add */\nbool selection_addgradelower(objectselection *sel, grade g) {\n    //dictionary *dest = &sel->selected[g];\n    \n    for (grade i=sel->ngrades; i>g; i--) { // Loop over grades higher than g\n        objectsparse *conn = mesh_addconnectivityelement(sel->mesh, g, i);\n        if (!conn) continue;\n        \n        // Look through the selected elements in grade i\n        for (unsigned int k=0; k<sel->selected[i].capacity; k++) {\n            value el = sel->selected[i].contents[k].key;\n            if (MORPHO_ISINTEGER(el)) {\n                int nentries, *entries;\n                elementid id = MORPHO_GETINTEGERVALUE(el);\n                 \n                // Get the element ids\n                if (mesh_getconnectivity(conn, id, &nentries, &entries)) {\n                    for (int j=0; j<nentries; j++) {\n                        dictionary_insert(&sel->selected[g], MORPHO_INTEGER(entries[j]), MORPHO_NIL);\n                    }\n                }\n            }\n        }\n    }\n    \n    return true;\n}\n\n/** Selects an element\n * @param v - the virtual machine to use\n * @param sel - selection object\n * @param fn - function to call */\nvoid selection_selectwithfunction(vm *v, objectselection *sel, value fn) {\n    if (!sel->mesh || !sel->mesh->vert) UNREACHABLE(\"Selection on mesh with invalid vertex structure.\");\n    \n    objectmatrix *vert=sel->mesh->vert;\n    int nv = vert->ncols;\n    \n    value ret=MORPHO_NIL; // Return value\n    value coords[sel->mesh->dim+1]; // Vertex coords\n    \n    for (elementid i=0; i<nv; i++) {\n        if (mesh_getvertexcoordinatesasvalues(sel->mesh, i, coords)) {\n            if (!morpho_call(v, fn, sel->mesh->dim, coords, &ret)) break;\n            if (MORPHO_ISTRUE(ret)) selection_selectelement(sel, MESH_GRADE_VERTEX, i);\n        }\n    }\n}\n\n/** Selects elements by mapping a function over a matrix.\n * @param v - the virtual machine to use\n * @param sel - selection object\n * @param fn - function to call\n * @param matrix - matrix to map over */\nvoid selection_selectwithmatrix(vm *v, objectselection *sel, value fn, objectmatrix *matrix) {\n    if (!sel->mesh || !sel->mesh->vert) UNREACHABLE(\"Selection on mesh with invalid vertex structure.\");\n    \n    objectmatrix *vert=sel->mesh->vert;\n    int nv = vert->ncols;\n\n    if (matrix->ncols!=nv) {\n        morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n        return;\n    }\n    \n    int nargs = matrix->nrows; // Number of args to pass to function\n    value args[nargs]; // Vertex coords\n    double *x; // Matrix column\n    value ret=MORPHO_NIL; // Return value\n    \n    for (elementid i=0; i<nv; i++) {\n        bool success=matrix_getcolumn(matrix, i, &x);\n        \n        if (success) {\n            for (unsigned int i=0; i<nargs; i++) args[i]=MORPHO_FLOAT(x[i]);\n        }\n        \n        if (!morpho_call(v, fn, nargs, args, &ret)) break;\n        if (MORPHO_ISTRUE(ret)) selection_selectelement(sel, MESH_GRADE_VERTEX, i);\n    }\n}\n\n/** Selects boundary elements\n * @param v - the virtual machine to use\n * @param sel - selection object */\nvoid selection_selectboundary(vm *v, objectselection *sel) {\n    grade max = mesh_maxgrade(sel->mesh);\n    if (max<1) { morpho_runtimeerror(v, SELECTION_BND); return; }\n    \n    grade bnd = max-1;\n    \n    objectsparse *conn=mesh_addconnectivityelement(sel->mesh, max, bnd);\n    \n    if (conn) {\n        int nentries, *entries;\n        for (elementid i=0; i<mesh_nelements(conn); i++) {\n            if (mesh_getconnectivity(conn, i, &nentries, &entries)) {\n                if (nentries==1) {\n                    selection_selectelement(sel, bnd, i);\n                }\n            }\n        }\n    }\n    // Add vertices if the boundary elements are higher in grade than vertices\n    if (bnd!=0) {selection_addgradelower(sel, 0); }\n}\n\n/** Selects an element */\nvoid selection_selectwithid(objectselection *sel, grade g, elementid id, bool selected) {\n    if (selected && (sel->mode==SELECT_NONE || sel->mode==SELECT_SOME)) {\n        if (g<sel->ngrades) {\n            dictionary_insert(&sel->selected[g], MORPHO_INTEGER(id), MORPHO_NIL);\n        }\n        \n        sel->mode=SELECT_SOME;\n    } else if (!selected && (sel->mode==SELECT_SOME)) {\n        if (g<sel->ngrades) {\n            dictionary_remove(&sel->selected[g], MORPHO_INTEGER(id));\n        }\n        \n        sel->mode=SELECT_SOME;\n    }\n    \n    if (sel->mode==SELECT_ALL) {\n        UNREACHABLE(\"Unimplemented modification to SELECTALL not implemented.\");\n    }\n    \n}\n\n/** Tests if an element is selected */\nbool selection_isselected(objectselection *sel, grade g, elementid id) {\n    switch (sel->mode) {\n        case SELECT_NONE: return false;\n        case SELECT_ALL: return true;\n        case SELECT_SOME: {\n            return dictionary_get(&sel->selected[g], MORPHO_INTEGER(id), NULL);\n        }\n    }\n}\n\n/** Finds the maximum nonempty grade in a selection */\ngrade selection_maxgrade(objectselection *sel) {\n    switch (sel->mode) {\n        case SELECT_NONE: return 0;\n        case SELECT_ALL: return mesh_maxgrade(sel->mesh);\n        case SELECT_SOME:\n            for (grade g=sel->ngrades-1; g>0; g--) {\n                if (sel->selected[g].count>0) return g;\n            }\n    }\n    return 0;\n}\n\n/** Gets the element ids for a given grade as a list */\nobjectlist *selection_idlistforgrade(objectselection *sel, grade g) {\n    objectlist *new = object_newlist(0, NULL);\n    dictionary *dict = &sel->selected[g];\n    \n    if (new) switch(sel->mode) {\n        case SELECT_NONE: break;\n        case SELECT_ALL: {\n            UNREACHABLE(\"ID list for select all not implemented.\");\n        }\n            break;\n        case SELECT_SOME: {\n            list_resize(new, dict->count);\n            for (unsigned int i=0; i<dict->capacity; i++) {\n                if (MORPHO_ISINTEGER(dict->contents[i].key)) {\n                    list_append(new, dict->contents[i].key);\n                }\n            }\n        }\n            break;\n    }\n    \n    return new;\n}\n\n/* **********************************************************************\n * Selection set operations\n * ********************************************************************** */\n\n/* Computes the union of selections a & b */\nobjectselection *selection_union(objectselection *a, objectselection *b) {\n    objectselection *new = object_newselection(a->mesh);\n    \n    if (a->mode==SELECT_ALL || b->mode==SELECT_ALL) { // No need to copy a select all element\n        new->mode=SELECT_ALL;\n    } else {\n        for (grade g=0; g<a->ngrades && g<b->ngrades; g++) {\n            dictionary_union(&a->selected[g], &b->selected[g], &new->selected[g]);\n            if (new->selected[g].count>0) new->mode=SELECT_SOME;\n        }\n    }\n    \n    return new;\n}\n\n/* Computes the union of selections a & b */\nobjectselection *selection_intersection(objectselection *a, objectselection *b) {\n    objectselection *new = object_newselection(a->mesh);\n    \n    if (a->mode==SELECT_ALL && b->mode==SELECT_ALL) { // No need to copy a select all element\n        new->mode=SELECT_ALL;\n    } else if (a->mode!=SELECT_NONE && b->mode!=SELECT_NONE) {\n        for (grade g=0; g<a->ngrades && g<b->ngrades; g++) {\n            dictionary_intersection(&a->selected[g], &b->selected[g], &new->selected[g]);\n            if (new->selected[g].count>0) new->mode=SELECT_SOME;\n        }\n    }\n    \n    return new;\n}\n\n/* Computes the union of selections a & b */\nobjectselection *selection_difference(objectselection *a, objectselection *b) {\n    objectselection *new = object_newselection(a->mesh);\n    \n    if (a->mode==SELECT_ALL) {\n        if (b->mode==SELECT_NONE) {\n            new->mode=SELECT_ALL;\n        } else UNREACHABLE(\"Selectall difference not implemented.\");\n    } else if (a->mode!=SELECT_NONE) {\n        for (grade g=0; g<a->ngrades && g<b->ngrades; g++) {\n            dictionary_difference(&a->selected[g], &b->selected[g], &new->selected[g]);\n            if (new->selected[g].count>0) new->mode=SELECT_SOME;\n        }\n    }\n    \n    return new;\n}\n\n/* **********************************************************************\n * Selection veneer class\n * ********************************************************************** */\n\nstatic value selection_boundaryoption;\nstatic value selection_partialsoption;\n\n/** Constructs a Selection object */\nvalue selection_constructor(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectmesh *mesh=NULL;\n    objectselection *new=NULL;\n    value fn=MORPHO_NIL;\n    value fnargs=MORPHO_NIL;\n    value boundary=MORPHO_FALSE;\n    int nfixed=nargs;\n    \n    builtin_options(v, nargs, args, &nfixed, 1, selection_boundaryoption, &boundary);\n    \n    /* Get mesh as first argument */\n    if (nfixed>0) {\n        if (MORPHO_ISMESH(MORPHO_GETARG(args, 0))) mesh=MORPHO_GETMESH(MORPHO_GETARG(args, 0));\n    }\n    \n    /* Selection function as optional second argument */\n    if (nfixed>1) fn = MORPHO_GETARG(args, 1);\n    \n    /* Selection function arguments as optional second argument */\n    if (nfixed>2) fnargs = MORPHO_GETARG(args, 2);\n    \n    if (mesh) {\n        new=object_newselection(mesh);\n    } else {\n        morpho_runtimeerror(v, SELECTION_NOMESH);\n        return out;\n    }\n    \n    if (new) {\n        if (!MORPHO_ISNIL(fn)) {\n            if (MORPHO_ISNIL(fnargs)) {\n                selection_selectwithfunction(v, new, fn);\n            } else if (MORPHO_ISMATRIX(fnargs)) {\n                selection_selectwithmatrix(v, new, fn, MORPHO_GETMATRIX(fnargs));\n            }\n        } else if (MORPHO_ISTRUE(boundary)) {\n            selection_selectboundary(v, new);\n        }\n        \n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    \n    return out;\n}\n\n/** Select an element by id */\nvalue Selection_setindex(vm *v, int nargs, value *args) {\n    objectselection *sel = MORPHO_GETSELECTION(MORPHO_SELF(args));\n    \n    if (nargs==3 &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) {\n    \n        selection_selectwithid(sel, MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)), MORPHO_ISTRUE(MORPHO_GETARG(args, 2)));\n    } else morpho_runtimeerror(v, SELECTION_ISSLCTDARG);\n    \n    return MORPHO_NIL;\n}\n\n/** Tests if something is selected */\nvalue Selection_isselected(vm *v, int nargs, value *args) {\n    objectselection *sel = MORPHO_GETSELECTION(MORPHO_SELF(args));\n    value out = MORPHO_FALSE;\n    \n    if (nargs==2 &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) {\n     \n        out = MORPHO_BOOL(selection_isselected(sel, MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1))));\n    } else morpho_runtimeerror(v, SELECTION_ISSLCTDARG);\n    \n    return out;\n}\n\n/** Get the id list for a given grade */\nvalue Selection_idlistforgrade(vm *v, int nargs, value *args) {\n    objectselection *sel = MORPHO_GETSELECTION(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n    \n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        objectlist *lst=selection_idlistforgrade(sel, MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)));\n        if (lst) {\n            out = MORPHO_OBJECT(lst);\n            morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    } else morpho_runtimeerror(v, SELECTION_GRADEARG);\n    \n    return out;\n}\n\n/** Adds a grade to a selection */\nvalue Selection_addgrade(vm *v, int nargs, value *args) {\n    objectselection *sel = MORPHO_GETSELECTION(MORPHO_SELF(args));\n    value partials = MORPHO_FALSE;\n    int nfixed;\n\n    builtin_options(v, nargs, args, &nfixed, 1, selection_partialsoption, &partials);\n    \n    if (nargs>0 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        grade g = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        grade max = selection_maxgrade(sel);\n        if (g>max) {\n            selection_addgraderaise(sel, g, MORPHO_ISTRUE(partials));\n        } else {\n            selection_addgradelower(sel, g);\n        }\n    } else morpho_runtimeerror(v, SELECTION_GRADEARG);\n    \n    return MORPHO_NIL;\n}\n\n/** Removes a grade from a selection */\nvalue Selection_removegrade(vm *v, int nargs, value *args) {\n    objectselection *sel = MORPHO_GETSELECTION(MORPHO_SELF(args));\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        selection_removegrade(sel, MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)));\n    } else morpho_runtimeerror(v, SELECTION_GRADEARG);\n    \n    return MORPHO_NIL;\n}\n\n/** Print the selection */\nvalue Selection_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISSELECTION(self)) return Object_print(v, nargs, args);\n    morpho_printf(v, \"<Selection>\");\n    return MORPHO_NIL;\n}\n\n/** Counts number of elements selected in each grade  */\nvalue Selection_count(vm *v, int nargs, value *args) {\n    objectselection *sel = MORPHO_GETSELECTION(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        grade g = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        out = MORPHO_INTEGER(sel->selected[g].count);\n    } else morpho_runtimeerror(v, SELECTION_GRADEARG);\n    \n    return out;\n}\n\n/** Clones a selection */\nvalue Selection_clone(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectselection *a=MORPHO_GETSELECTION(MORPHO_SELF(args));\n    objectselection *new=selection_clone(a);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    return out;\n}\n\n#define SELECTION_SETOP(op) \\\nvalue Selection_##op(vm *v, int nargs, value *args) { \\\n    objectselection *slf = MORPHO_GETSELECTION(MORPHO_SELF(args)); \\\n    value out=MORPHO_NIL; \\\n    \\\n    if (nargs>0 && MORPHO_ISSELECTION(MORPHO_GETARG(args, 0))) { \\\n        objectselection *new = selection_##op(slf, MORPHO_GETSELECTION(MORPHO_GETARG(args, 0))); \\\n        \\\n        if (new) { \\\n            out=MORPHO_OBJECT(new); \\\n            morpho_bindobjects(v, 1, &out); \\\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); \\\n    } else morpho_runtimeerror(v, SELECTION_STARG); \\\n    \\\n    return out; \\\n}\n\nSELECTION_SETOP(union)\nSELECTION_SETOP(intersection)\nSELECTION_SETOP(difference)\n\nMORPHO_BEGINCLASS(Selection)\nMORPHO_METHOD(SELECTION_ISSELECTEDMETHOD, Selection_isselected, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Selection_isselected, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Selection_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SELECTION_IDLISTFORGRADEMETHOD, Selection_idlistforgrade, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Selection_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Selection_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_UNION_METHOD, Selection_union, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_INTERSECTION_METHOD, Selection_intersection, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIFFERENCE_METHOD, Selection_difference, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADD_METHOD, Selection_union, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUB_METHOD, Selection_difference, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SELECTION_ADDGRADEMETHOD, Selection_addgrade, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SELECTION_REMOVEGRADEMETHOD, Selection_removegrade, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Selection_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************** */\n\nvoid selection_initialize(void) {\n    objectselectiontype=object_addtype(&objectselectiondefn);\n    \n    selection_boundaryoption=builtin_internsymbolascstring(SELECTION_BOUNDARYOPTION);\n    selection_partialsoption=builtin_internsymbolascstring(SELECTION_PARTIALSOPTION);\n    \n    builtin_addfunction(SELECTION_CLASSNAME, selection_constructor, BUILTIN_FLAGSEMPTY);\n    \n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value selectionclass=builtin_addclass(SELECTION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Selection), objclass);\n    object_setveneerclass(OBJECT_SELECTION, selectionclass);\n    \n    morpho_defineerror(SELECTION_NOMESH, ERROR_HALT, SELECTION_NOMESH_MSG);\n    morpho_defineerror(SELECTION_ISSLCTDARG, ERROR_HALT, SELECTION_ISSLCTDARG_MSG);\n    morpho_defineerror(SELECTION_GRADEARG, ERROR_HALT, SELECTION_GRADEARG_MSG);\n    morpho_defineerror(SELECTION_STARG, ERROR_HALT, SELECTION_STARG_MSG);\n    morpho_defineerror(SELECTION_BND, ERROR_HALT, SELECTION_BND_MSG);\n}\n\n#endif\n"
  },
  {
    "path": "src/geometry/selection.h",
    "content": "/** @file selection.c\n *  @author T J Atherton\n *\n *  @brief Selections\n */\n\n#ifndef selection_h\n#define selection_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_GEOMETRY\n\n#include \"mesh.h\"\n\n/* -------------------------------------------------------\n * Selection objects\n * ------------------------------------------------------- */\n\nextern objecttype objectselectiontype;\n#define OBJECT_SELECTION objectselectiontype\n\ntypedef struct {\n    object obj;\n    objectmesh *mesh; /** The mesh the selection is referring to */\n    \n    enum {\n        SELECT_ALL, SELECT_NONE, SELECT_SOME\n    } mode; /** What is selected? */\n    \n    unsigned int ngrades; /** Number of grades */\n    dictionary selected[]; /** Selections */\n} objectselection;\n\n/** Tests whether an object is a selection */\n#define MORPHO_ISSELECTION(val) object_istype(val, OBJECT_SELECTION)\n\n/** Gets the object as a selection */\n#define MORPHO_GETSELECTION(val)   ((objectselection *) MORPHO_GETOBJECT(val))\n\n/** Creates an empty selection object */\nobjectselection *object_newselection(objectmesh *mesh);\n\n/* -------------------------------------------------------\n * Selection class\n * ------------------------------------------------------- */\n\n#define SELECTION_CLASSNAME \"Selection\"\n#define SELECTION_ISSELECTEDMETHOD \"isselected\"\n#define SELECTION_IDLISTFORGRADEMETHOD \"idlistforgrade\"\n#define SELECTION_ADDGRADEMETHOD \"addgrade\"\n#define SELECTION_REMOVEGRADEMETHOD \"removegrade\"\n\n#define SELECTION_COMPLEMENTMETHOD \"complement\"\n\n#define SELECTION_BOUNDARYOPTION \"boundary\"\n#define SELECTION_PARTIALSOPTION \"partials\"\n\n#define SELECTION_NOMESH                     \"SlNoMsh\"\n#define SELECTION_NOMESH_MSG                 \"Selection requires a Mesh object as an argument.\"\n\n#define SELECTION_ISSLCTDARG                 \"SlIsSlArg\"\n#define SELECTION_ISSLCTDARG_MSG             \"Selection.isselected requires a grade and element id as arguments.\"\n\n#define SELECTION_GRADEARG                   \"SlGrdArg\"\n#define SELECTION_GRADEARG_MSG               \"Method requires a grade as the argument.\"\n\n#define SELECTION_STARG                      \"SlStArg\"\n#define SELECTION_STARG_MSG                  \"Selection set methods require a selection as the argument.\"\n\n#define SELECTION_BND                        \"SlBnd\"\n#define SELECTION_BND_MSG                    \"Mesh has no boundary elements.\"\n\nvoid selection_clear(objectselection *s);\n\nbool selection_isselected(objectselection *sel, grade g, elementid id);\nvoid selection_initialize(void);\n\n#endif\n\n#endif /* selection_h */\n"
  },
  {
    "path": "src/linalg/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        matrix.c  matrix.h\n        sparse.c  sparse.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        matrix.h\n        sparse.h\n)\n"
  },
  {
    "path": "src/linalg/matrix.c",
    "content": "/** @file matrix.c\n *  @author T J Atherton\n *\n *  @brief Veneer class over the objectmatrix type that interfaces with blas and lapack\n */\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_LINALG\n\n#include <string.h>\n#include \"morpho.h\"\n#include \"classes.h\"\n\n#include \"matrix.h\"\n#include \"sparse.h\"\n#include \"format.h\"\n\n/* **********************************************************************\n * Matrix objects\n * ********************************************************************** */\n\nobjecttype objectmatrixtype;\n\n/** Function object definitions */\nsize_t objectmatrix_sizefn(object *obj) {\n    return sizeof(objectmatrix)+sizeof(double) *\n            ((objectmatrix *) obj)->ncols *\n            ((objectmatrix *) obj)->nrows;\n}\n\nvoid objectmatrix_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Matrix>\");\n}\n\nobjecttypedefn objectmatrixdefn = {\n    .printfn=objectmatrix_printfn,\n    .markfn=NULL,\n    .freefn=NULL,\n    .sizefn=objectmatrix_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/** Creates a matrix object */\nobjectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero) {\n    unsigned int nel = nrows*ncols;\n    objectmatrix *new = (objectmatrix *) object_new(sizeof(objectmatrix)+nel*sizeof(double), OBJECT_MATRIX);\n    \n    if (new) {\n        new->ncols=ncols;\n        new->nrows=nrows;\n        new->elements=new->matrixdata;\n        if (zero) {\n            memset(new->elements, 0, sizeof(double)*nel);\n        }\n    }\n    \n    return new;\n}\n\n/* **********************************************************************\n * Other constructors\n * ********************************************************************** */\n\n/*\n * Create matrices from array objects\n */\n\nvoid matrix_raiseerror(vm *v, objectmatrixerror err) {\n    switch(err) {\n        case MATRIX_OK: break;\n        case MATRIX_INCMPTBLDIM: morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); break;\n        case MATRIX_SING: morpho_runtimeerror(v, MATRIX_SINGULAR); break;\n        case MATRIX_INVLD: morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); break;\n        case MATRIX_BNDS: morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); break;\n        case MATRIX_NSQ: morpho_runtimeerror(v, MATRIX_NOTSQ); break;\n        case MATRIX_FAILED: morpho_runtimeerror(v, MATRIX_OPFAILED); break;\n        case MATRIX_ALLOC: morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); break;\n    }\n}\n\n/** Recurses into an objectarray to find the dimensions of the array and all child arrays\n * @param[in] array - to search\n * @param[out] dim - array of dimensions to be filled out (must be zero'd before initial call)\n * @param[in] maxdim - maximum number of dimensions\n * @param[out] ndim - number of dimensions of the array */\nbool matrix_getarraydimensions(objectarray *array, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) {\n    unsigned int n=0, m=0;\n    for (n=0; n<maxdim && n<array->ndim; n++) {\n        int k=MORPHO_GETINTEGERVALUE(array->data[n]);\n        if (k>dim[n]) dim[n]=k;\n    }\n    \n    if (maxdim<array->ndim) return false;\n    \n    for (unsigned int i=array->ndim; i<array->ndim+array->nelements; i++) {\n        if (MORPHO_ISARRAY(array->data[i])) {\n            if (!matrix_getarraydimensions(MORPHO_GETARRAY(array->data[i]), dim+n, maxdim-n, &m)) return false;\n        }\n    }\n    *ndim=n+m;\n    \n    return true;\n}\n\n/** Looks up an array element recursively if necessary */\nvalue matrix_getarrayelement(objectarray *array, unsigned int ndim, unsigned int *indx) {\n    unsigned int na=array->ndim;\n    value out;\n    \n    if (array_getelement(array, na, indx, &out)==ARRAY_OK) {\n        if (ndim==na) return out;\n        if (MORPHO_ISARRAY(out)) {\n            return matrix_getarrayelement(MORPHO_GETARRAY(out), ndim-na, indx+na);\n        }\n    }\n    \n    return MORPHO_NIL;\n}\n\n/** Creates a new array from a list of values */\nobjectmatrix *object_matrixfromarray(objectarray *array) {\n    unsigned int dim[2]={0,1}; // The 1 is to allow for vector arrays.\n    unsigned int ndim=0;\n    objectmatrix *ret=NULL;\n    \n    if (matrix_getarraydimensions(array, dim, 2, &ndim)) {\n        ret=object_newmatrix(dim[0], dim[1], true);\n    }\n    \n    unsigned int indx[2];\n    if (ret) for (unsigned int i=0; i<dim[0]; i++) {\n        for (unsigned int j=0; j<dim[1]; j++) {\n            indx[0]=i; indx[1]=j;\n            value f = matrix_getarrayelement(array, ndim, indx);\n            if (morpho_isnumber(f)) {\n                morpho_valuetofloat(f, &ret->elements[j*dim[0]+i]);\n            } else if (!MORPHO_ISNIL(f)) {\n                object_free((object *) ret); return NULL;\n            }\n        }\n    }\n    \n    return ret;\n}\n\n/*\n * Create matrices from lists\n */\n\n/** Recurses into an objectlist to find the dimensions of the array and all child arrays\n * @param[in] list - to search\n * @param[out] dim - array of dimensions to be filled out (must be zero'd before initial call)\n * @param[in] maxdim - maximum number of dimensions\n * @param[out] ndim - number of dimensions of the array */\nbool matrix_getlistdimensions(objectlist *list, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) {\n    unsigned int m=0;\n    \n    if (maxdim==0) return false;\n    \n    /* Store the length */\n    if (list->val.count>dim[0]) dim[0]=list->val.count;\n    \n    for (unsigned int i=0; i<list->val.count; i++) {\n        if (MORPHO_ISLIST(list->val.data[i]) && maxdim>0) {\n            matrix_getlistdimensions(MORPHO_GETLIST(list->val.data[i]), dim+1, maxdim-1, &m);\n        }\n    }\n    *ndim=m+1;\n    \n    return true;\n}\n\n/** Gets a matrix element from a (potentially nested) list. */\nbool matrix_getlistelement(objectlist *list, unsigned int ndim, unsigned int *indx, value *val) {\n    value out=MORPHO_NIL;\n    objectlist *l=list;\n    for (unsigned int i=0; i<ndim; i++) {\n        if (indx[i]<l->val.count) {\n            out=l->val.data[indx[i]];\n            if (i<ndim-1 && MORPHO_ISLIST(out)) l=MORPHO_GETLIST(out);\n        } else return false;\n    }\n    *val=out;\n    return true;\n}\n\n/* Creates a matrix from a list */\nobjectmatrix *object_matrixfromlist(objectlist *list) {\n    unsigned int dim[2]={0,1}; // The 1 is to allow for vector arrays.\n    unsigned int ndim=0;\n    objectmatrix *ret=NULL;\n    \n    if (matrix_getlistdimensions(list, dim, 2, &ndim)) {\n        ret=object_newmatrix(dim[0], dim[1], true);\n    }\n    \n    if (ndim>2) return false;\n    \n    unsigned int indx[2];\n    if (ret) for (unsigned int i=0; i<dim[0]; i++) {\n        for (unsigned int j=0; j<dim[1]; j++) {\n            indx[0]=i; indx[1]=j;\n            value f;\n            if (matrix_getlistelement(list, ndim, indx, &f) &&\n                morpho_isnumber(f)) {\n                morpho_valuetofloat(f, &ret->elements[j*dim[0]+i]);\n            } else {\n                object_free((object *) ret);\n                return NULL;\n            }\n        }\n    }\n    \n    return ret;\n}\n\n/** Creates a matrix from a list of floats */\nobjectmatrix *object_matrixfromfloats(unsigned int nrows, unsigned int ncols, double *list) {\n    objectmatrix *ret=NULL;\n    \n    ret=object_newmatrix(nrows, ncols, true);\n    if (ret) cblas_dcopy(ncols*nrows, list, 1, ret->elements, 1);\n    \n    return ret;\n}\n\n/*\n * Clone matrices\n */\n\n/** Clone a matrix */\nobjectmatrix *object_clonematrix(objectmatrix *in) {\n    objectmatrix *new = object_newmatrix(in->nrows, in->ncols, false);\n    \n    if (new) {\n        cblas_dcopy(in->ncols * in->nrows, in->elements, 1, new->elements, 1);\n    }\n    \n    return new;\n}\n\n/* **********************************************************************\n * Matrix operations\n * ********************************************************************* */\n\n/** @brief Sets a matrix element.\n    @returns true if the element is in the range of the matrix, false otherwise */\nbool matrix_setelement(objectmatrix *matrix, unsigned int row, unsigned int col, double value) {\n    if (col<matrix->ncols && row<matrix->nrows) {\n        matrix->elements[col*matrix->nrows+row]=value;\n        return true;\n    }\n    return false;\n}\n\n/** @brief Gets a matrix element\n *  @returns true if the element is in the range of the matrix, false otherwise */\nbool matrix_getelement(objectmatrix *matrix, unsigned int row, unsigned int col, double *value) {\n    if (col<matrix->ncols && row<matrix->nrows) {\n        if (value) *value=matrix->elements[col*matrix->nrows+row];\n        return true;\n    }\n    return false;\n}\n\n/** @brief Gets a column's entries\n *  @param[in] matrix - the matrix\n *  @param[in] col - column number\n *  @param[out] v - column entries (matrix->nrows in number)\n *  @returns true if the element is in the range of the matrix, false otherwise */\nbool matrix_getcolumn(objectmatrix *matrix, unsigned int col, double **v) {\n    if (col<matrix->ncols) {\n        *v=&matrix->elements[col*matrix->nrows];\n        return true;\n    }\n    return false;\n}\n\n/** @brief Sets a column's entries\n *  @param[in] matrix - the matrix\n *  @param[in] col - column number\n *  @param[in] v - column entries (matrix->nrows in number)\n *  @returns true if the element is in the range of the matrix, false otherwise */\nbool matrix_setcolumn(objectmatrix *matrix, unsigned int col, double *v) {\n    if (col<matrix->ncols) {\n        cblas_dcopy(matrix->nrows, v, 1, &matrix->elements[col*matrix->nrows], 1);\n        return true;\n    }\n    return false;\n}\n\n/** @brief Add a vector to a column in a matrix\n *  @param[in] m - the matrix\n *  @param[in] col - column number\n *  @param[in] alpha - scale\n *  @param[out] v - column entries (matrix->nrows in number) [should have m->nrows entries]\n *  @returns true on success */\nbool matrix_addtocolumn(objectmatrix *m, unsigned int col, double alpha, double *v) {\n    if (col<m->ncols) {\n        cblas_daxpy(m->nrows, alpha, v, 1, &m->elements[col*m->nrows], 1);\n        return true;\n    }\n    return false;\n}\n\n/* **********************************************************************\n * Matrix arithmetic\n * ********************************************************************* */\n\n/** Copies one matrix to another */\nunsigned int matrix_countdof(objectmatrix *a) {\n    return a->ncols*a->nrows;\n}\n\n/** Copies one matrix to another */\nobjectmatrixerror matrix_copy(objectmatrix *a, objectmatrix *out) {\n    if (a->ncols==out->ncols && a->nrows==out->nrows) {\n        cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Copies a matrix to another at an arbitrary point */\nobjectmatrixerror matrix_copyat(objectmatrix *a, objectmatrix *out, int row0, int col0) {\n    if (col0+a->ncols<=out->ncols && row0+a->nrows<=out->nrows) {\n        for (int j=0; j<a->ncols; j++) {\n            for (int i=0; i<a->nrows; i++) {\n                double value;\n                if (!matrix_getelement(a, i, j, &value)) return MATRIX_BNDS;\n                if (!matrix_setelement(out, row0+i, col0+j, value)) return MATRIX_BNDS;\n            }\n        }\n        \n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Performs a + b -> out. */\nobjectmatrixerror matrix_add(objectmatrix *a, objectmatrix *b, objectmatrix *out) {\n    if (a->ncols==b->ncols && a->ncols==out->ncols &&\n        a->nrows==b->nrows && a->nrows==out->nrows) {\n        if (a!=out) cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1);\n        cblas_daxpy(a->ncols * a->nrows, 1.0, b->elements, 1, out->elements, 1);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Performs lambda*a + beta -> out. */\nobjectmatrixerror matrix_addscalar(objectmatrix *a, double lambda, double beta, objectmatrix *out) {\n    if (a->ncols==out->ncols && a->nrows==out->nrows) {\n        for (unsigned int i=0; i<out->nrows*out->ncols; i++) {\n            out->elements[i]=lambda*a->elements[i]+beta;\n        }\n        return MATRIX_OK;\n    }\n\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Performs a + lambda*b -> a. */\nobjectmatrixerror matrix_accumulate(objectmatrix *a, double lambda, objectmatrix *b) {\n    if (a->ncols==b->ncols && a->nrows==b->nrows ) {\n        cblas_daxpy(a->ncols * a->nrows, lambda, b->elements, 1, a->elements, 1);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Performs a - b -> out */\nobjectmatrixerror matrix_sub(objectmatrix *a, objectmatrix *b, objectmatrix *out) {\n    if (a->ncols==b->ncols && a->ncols==out->ncols &&\n        a->nrows==b->nrows && a->nrows==out->nrows) {\n        if (a!=out) cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1);\n        cblas_daxpy(a->ncols * a->nrows, -1.0, b->elements, 1, out->elements, 1);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Performs a * b -> out */\nobjectmatrixerror matrix_mul(objectmatrix *a, objectmatrix *b, objectmatrix *out) {\n    if (a->ncols==b->nrows && a->nrows==out->nrows && b->ncols==out->ncols) {\n        cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, a->nrows, b->ncols, a->ncols, 1.0, a->elements, a->nrows, b->elements, b->nrows, 0.0, out->elements, out->nrows);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Finds the Frobenius inner product of two matrices  */\nobjectmatrixerror matrix_inner(objectmatrix *a, objectmatrix *b, double *out) {\n    if (a->ncols==b->ncols && a->nrows==b->nrows) {\n        *out=cblas_ddot(a->ncols*a->nrows, a->elements, 1, b->elements, 1);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Computes the outer product of two matrices  */\nobjectmatrixerror matrix_outer(objectmatrix *a, objectmatrix *b, objectmatrix *out) {\n    int m=a->nrows*a->ncols, n=b->nrows*b->ncols;\n    if (m==out->nrows && n==out->ncols) {\n        cblas_dger(CblasColMajor, m, n, 1, a->elements, 1, b->elements, 1, out->elements, out->nrows);\n        return MATRIX_OK;\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Solves the system a.x = b\n * @param[in] a  lhs\n * @param[in] b  rhs\n * @param[in] out - the solution x\n * @param[out] lu - LU decomposition of a; you must provide an array the same size as a.\n * @param[out] pivot - you must provide an array with the same number of rows as a.\n * @returns objectmatrixerror indicating the status; MATRIX_OK indicates success.\n * */\nstatic objectmatrixerror matrix_div(objectmatrix *a, objectmatrix *b, objectmatrix *out, double *lu, int *pivot) {\n    int n=a->nrows, nrhs = b->ncols, info;\n    \n    cblas_dcopy(a->ncols * a->nrows, a->elements, 1, lu, 1);\n    if (b!=out) cblas_dcopy(b->ncols * b->nrows, b->elements, 1, out->elements, 1);\n#ifdef MORPHO_LINALG_USE_LAPACKE\n    info=LAPACKE_dgesv(LAPACK_COL_MAJOR, n, nrhs, lu, n, pivot, out->elements, n);\n#else\n    dgesv_(&n, &nrhs, lu, &n, pivot, out->elements, &n, &info);\n#endif\n    \n    return (info==0 ? MATRIX_OK : (info>0 ? MATRIX_SING : MATRIX_INVLD));\n}\n\n/** Solves the system a.x = b for small matrices (test with MATRIX_ISSMALL)\n * @warning Uses the C stack for storage, which avoids malloc but can cause stack overflow */\nobjectmatrixerror matrix_divs(objectmatrix *a, objectmatrix *b, objectmatrix *out) {\n    if (a->ncols==b->nrows && a->ncols == out->nrows) {\n        int pivot[a->nrows];\n        double lu[a->nrows*a->ncols];\n        \n        return matrix_div(a, b, out, lu, pivot);\n    }\n    return MATRIX_INCMPTBLDIM;\n}\n\n/** Solves the system a.x = b for large matrices (test with MATRIX_ISSMALL)  */\nobjectmatrixerror matrix_divl(objectmatrix *a, objectmatrix *b, objectmatrix *out) {\n    objectmatrixerror ret = MATRIX_ALLOC; // Returned if allocation fails\n    if (!(a->ncols==b->nrows && a->ncols == out->nrows)) return MATRIX_INCMPTBLDIM;\n    \n    int *pivot=MORPHO_MALLOC(sizeof(int)*a->nrows);\n    double *lu=MORPHO_MALLOC(sizeof(double)*a->nrows*a->ncols);\n    \n    if (pivot && lu) ret=matrix_div(a, b, out, lu, pivot);\n    \n    if (pivot) MORPHO_FREE(pivot);\n    if (lu) MORPHO_FREE(lu);\n    \n    return ret;\n}\n\n/** Inverts the matrix a\n * @param[in] a  lhs\n * @param[in] out - the solution x\n * @returns objectmatrixerror indicating the status; MATRIX_OK indicates success.\n * */\nobjectmatrixerror matrix_inverse(objectmatrix *a, objectmatrix *out) {\n    int nrows=a->nrows, ncols=a->ncols, info;\n    if (!(a->ncols==out->nrows && a->ncols == out->nrows)) return MATRIX_INCMPTBLDIM;\n    \n    int pivot[nrows];\n    \n    cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1);\n#ifdef MORPHO_LINALG_USE_LAPACKE\n    info=LAPACKE_dgetrf(LAPACK_COL_MAJOR, nrows, ncols, out->elements, nrows, pivot);\n#else\n    dgetrf_(&nrows, &ncols, out->elements, &nrows, pivot, &info);\n#endif\n\n    if (info!=0) return (info>0 ? MATRIX_SING : MATRIX_INVLD);\n    \n#ifdef MORPHO_LINALG_USE_LAPACKE\n    info=LAPACKE_dgetri(LAPACK_COL_MAJOR, nrows, out->elements, nrows, pivot);\n#else\n    int lwork=nrows*ncols; double work[nrows*ncols];\n    dgetri_(&nrows, out->elements, &nrows, pivot, work, &lwork, &info);\n#endif\n    \n    return (info==0 ? MATRIX_OK : (info>0 ? MATRIX_SING : MATRIX_INVLD));\n}\n\n/** Compute eigenvalues and eigenvectors of a matrix\n * @param[in] a - an objectmatrix to diagonalize of size n\n * @param[out] wr - a buffer of size n will hold the real part of the eigenvalues on exit\n * @param[out] wi - a buffer of size n will hold the imag part of the eigenvalues on exit\n * @param[out] vec - (optional) will be filled out with eigenvectors as columns (should be of size n)\n * @returns an error code or MATRIX_OK on success */\nobjectmatrixerror matrix_eigensystem(objectmatrix *a, double *wr, double *wi, objectmatrix *vec) {\n    int info, n=a->nrows;\n    if (a->nrows!=a->ncols) return MATRIX_NSQ;\n    if (vec && ((a->nrows!=vec->nrows) || (a->nrows!=vec->ncols))) return MATRIX_INCMPTBLDIM;\n    \n    // Copy a to prevent destruction\n    size_t size = ((size_t) n) * ((size_t) n) * sizeof(double);\n    double *acopy=MORPHO_MALLOC(size);\n    if (!acopy) return MATRIX_ALLOC;\n    cblas_dcopy(n*n, a->elements, 1, acopy, 1);\n    \n#ifdef MORPHO_LINALG_USE_LAPACKE\n    info=LAPACKE_dgeev(LAPACK_COL_MAJOR, 'N', (vec ? 'V' : 'N'), n, acopy, n, wr, wi, NULL, n, (vec ? vec->elements : NULL), n);\n#else\n    int lwork=4*n; double work[4*n];\n    dgeev_(\"N\", (vec ? \"V\" : \"N\"), &n, acopy, &n, wr, wi, NULL, &n, (vec ? vec->elements : NULL), &n, work, &lwork, &info);\n#endif\n    \n    if (acopy) MORPHO_FREE(acopy); // Free up buffer\n        \n    if (info!=0) return (info>0 ? MATRIX_FAILED : MATRIX_INVLD);\n    \n    return MATRIX_OK;\n}\n\n\n/** Sums all elements of a matrix using Kahan summation */\ndouble matrix_sum(objectmatrix *a) {\n    unsigned int nel=a->ncols*a->nrows;\n    double sum=0.0, c=0.0, y,t;\n    \n    for (unsigned int i=0; i<nel; i++) {\n        y=a->elements[i]-c;\n        t=sum+y;\n        c=(t-sum)-y;\n        sum=t;\n    }\n    return sum;\n}\n\n/** Norms */\n\n/** Computes the Frobenius norm of a matrix */\ndouble matrix_norm(objectmatrix *a) {\n    double nrm2=cblas_dnrm2(a->ncols*a->nrows, a->elements, 1);\n    return nrm2;\n}\n\n/** Computes the L1 norm of a matrix */\ndouble matrix_L1norm(objectmatrix *a) {\n    unsigned int nel=a->ncols*a->nrows;\n    double sum=0.0, c=0.0, y,t;\n    \n    for (unsigned int i=0; i<nel; i++) {\n        y=fabs(a->elements[i])-c;\n        t=sum+y;\n        c=(t-sum)-y;\n        sum=t;\n    }\n    return sum;\n}\n\n/** Computes the Ln norm of a matrix */\ndouble matrix_Lnnorm(objectmatrix *a, double n) {\n    unsigned int nel=a->ncols*a->nrows;\n    double sum=0.0, c=0.0, y,t;\n    \n    for (unsigned int i=0; i<nel; i++) {\n        y=pow(a->elements[i],n)-c;\n        t=sum+y;\n        c=(t-sum)-y;\n        sum=t;\n    }\n    return pow(sum,1.0/n);\n}\n\n/** Computes the Linf norm of a matrix */\ndouble matrix_Linfnorm(objectmatrix *a) {\n    unsigned int nel=a->ncols*a->nrows;\n    double max=0.0;\n    \n    for (unsigned int i=0; i<nel; i++) {\n        double y=fabs(a->elements[i]);\n        if (y>max) max=y;\n    }\n    return max;\n}\n\n/** Transpose a matrix */\nobjectmatrixerror matrix_transpose(objectmatrix *a, objectmatrix *out) {\n    if (!(a->ncols==out->nrows && a->nrows == out->ncols)) return MATRIX_INCMPTBLDIM;\n\n    /* Copy elements a column at a time */\n    for (unsigned int i=0; i<a->ncols; i++) {\n        cblas_dcopy(a->nrows, a->elements+(i*a->nrows), 1, out->elements+i, a->ncols);\n    }\n    return MATRIX_OK;\n}\n\n/** Calculate the trace of a matrix */\nobjectmatrixerror matrix_trace(objectmatrix *a, double *out) {\n    if (a->nrows!=a->ncols) return MATRIX_NSQ;\n    *out=1.0;\n    *out=cblas_ddot(a->nrows, a->elements, a->ncols+1, out, 0);\n    \n    return MATRIX_OK;\n}\n\n/** Scale a matrix */\nobjectmatrixerror matrix_scale(objectmatrix *a, double scale) {\n    cblas_dscal(a->ncols*a->nrows, scale, a->elements, 1);\n    \n    return MATRIX_OK;\n}\n\n/** Load the indentity matrix*/\nobjectmatrixerror matrix_identity(objectmatrix *a) {\n    if (a->ncols!=a->nrows) return MATRIX_NSQ;\n    memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols);\n    for (int i=0; i<a->nrows; i++) a->elements[i+a->nrows*i]=1.0;\n    return MATRIX_OK;\n}\n\n/** Sets a matrix to zero */\nobjectmatrixerror matrix_zero(objectmatrix *a) {\n    memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols);\n    \n    return MATRIX_OK;\n}\n\n/** Prints a matrix */\nvoid matrix_print(vm *v, objectmatrix *m) {\n    for (int i=0; i<m->nrows; i++) { // Rows run from 0...m\n        morpho_printf(v, \"[ \");\n        for (int j=0; j<m->ncols; j++) { // Columns run from 0...k\n            double val;\n            matrix_getelement(m, i, j, &val);\n            morpho_printf(v, \"%g \", (fabs(val)<MORPHO_EPS ? 0 : val));\n        }\n        morpho_printf(v, \"]%s\", (i<m->nrows-1 ? \"\\n\" : \"\"));\n    }\n}\n\n/** Prints a matrix to a buffer */\nbool matrix_printtobuffer(objectmatrix *m, char *format, varray_char *out) {\n    for (int i=0; i<m->nrows; i++) { // Rows run from 0...m\n        varray_charadd(out, \"[ \", 2);\n        \n        for (int j=0; j<m->ncols; j++) { // Columns run from 0...k\n            double val;\n            matrix_getelement(m, i, j, &val);\n            if (!format_printtobuffer(MORPHO_FLOAT(val), format, out)) return false; \n            varray_charadd(out, \" \", 1);\n        }\n        varray_charadd(out, \"]\", 1);\n        if (i<m->nrows-1) varray_charadd(out, \"\\n\", 1);\n    }\n    return true;\n}\n\n/** Matrix eigensystem */\nbool matrix_eigen(vm *v, objectmatrix *a, value *evals, value *evecs) {\n    double *ev = MORPHO_MALLOC(sizeof(double)*a->nrows*2); // Allocate temporary memory for eigenvalues\n    double *er=ev, *ei=ev+a->nrows;\n    \n    objectmatrix *vecs=NULL; // A new matrix for eigenvectors\n    objectlist *vallist = object_newlist(0, NULL); // List to hold eigenvalues\n    bool success=false;\n    \n    if (evecs) vecs=object_clonematrix(a); // Clones a to hold eigenvectors\n    \n    // Check that everything was allocated correctly\n    if (!(ev && vallist && (!evecs || vecs))) {\n        morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto matrix_eigen_cleanup; };\n    \n    objectmatrixerror err=matrix_eigensystem(a, er, ei, vecs);\n    \n    if (err!=MATRIX_OK) {\n        matrix_raiseerror(v, err);\n        goto matrix_eigen_cleanup;\n    }\n        \n    // Now process the eigenvalues\n    for (int i=0; i<a->nrows; i++) {\n        if (fabs(ei[i])<MORPHO_EPS) {\n            list_append(vallist, MORPHO_FLOAT(er[i]));\n        } else {\n            objectcomplex *c = object_newcomplex(er[i], ei[i]);\n            if (c) {\n                list_append(vallist, MORPHO_OBJECT(c));\n            } else {\n                morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n                goto matrix_eigen_cleanup;\n            }\n        }\n    }\n        \n    if (evals) *evals = MORPHO_OBJECT(vallist);\n    if (evecs) *evecs = MORPHO_OBJECT(vecs);\n    \n    success=true;\n    \nmatrix_eigen_cleanup:\n    if (ev) MORPHO_FREE(ev);\n    \n    if (!success) {\n        if (vallist) {\n            for (unsigned int i=0; i<vallist->val.count; i++) {\n                if (MORPHO_ISOBJECT(vallist->val.data[i])) object_free(MORPHO_GETOBJECT(vallist->val.data[i]));\n            }\n            object_free((object *) vallist);\n        }\n        if (vecs) object_free((object *) vecs);\n    }\n    \n    return success;\n}\n\n/* **********************************************************************\n * Matrix veneer class\n * ********************************************************************* */\n\n/** Constructs a Matrix object */\nvalue matrix_constructor(vm *v, int nargs, value *args) {\n    unsigned int nrows, ncols;\n    objectmatrix *new=NULL;\n    value out=MORPHO_NIL;\n    \n    if (nargs==2 &&\n         MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n         MORPHO_ISINTEGER(MORPHO_GETARG(args, 1)) ) {\n        nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1));\n        new=object_newmatrix(nrows, ncols, true);\n    } else if (nargs==1 &&\n               MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        ncols = 1;\n        new=object_newmatrix(nrows, ncols, true);\n    } else if (nargs==1 &&\n               MORPHO_ISARRAY(MORPHO_GETARG(args, 0))) {\n        new=object_matrixfromarray(MORPHO_GETARRAY(MORPHO_GETARG(args, 0)));\n        if (!new) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT);\n#ifdef MORPHO_INCLUDE_SPARSE\n    } else if (nargs==1 &&\n               MORPHO_ISLIST(MORPHO_GETARG(args, 0))) {\n        new=object_matrixfromlist(MORPHO_GETLIST(MORPHO_GETARG(args, 0)));\n        if (!new) {\n            /** Could this be a concatenation operation? */\n            objectsparseerror err = sparse_catmatrix(MORPHO_GETLIST(MORPHO_GETARG(args, 0)), &new);\n            if (err==SPARSE_INVLDINIT) {\n                morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT);\n            } else if (err!=SPARSE_OK) sparse_raiseerror(v, err);\n        }\n#endif\n    } else if (nargs==1 &&\n               MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        new=object_clonematrix(MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)));\n        if (!new) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT);\n#ifdef MORPHO_INCLUDE_SPARSE\n    } else if (nargs==1 &&\n               MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) {\n        objectsparseerror err=sparse_tomatrix(MORPHO_GETSPARSE(MORPHO_GETARG(args, 0)), &new);\n        if (err!=SPARSE_OK) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT);\n#endif\n    } else morpho_runtimeerror(v, MATRIX_CONSTRUCTOR);\n    \n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n/** Creates an identity matrix */\nvalue matrix_identityconstructor(vm *v, int nargs, value *args) {\n    int n;\n    objectmatrix *new=NULL;\n    value out = MORPHO_NIL;\n    \n    if (nargs==1 &&\n               MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        n = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        new=object_newmatrix(n, n, false);\n        if (new) {\n            matrix_identity(new);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    } else morpho_runtimeerror(v, MATRIX_IDENTCONSTRUCTOR);\n    \n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n/** Checks that a matrix is indexed with 2 indices with a generic interface */\nbool matrix_slicedim(value * a, unsigned int ndim){\n\tif (ndim>2||ndim<0) return false;\n\treturn true;\n}\n\n/** Constucts a new matrix with a generic interface */\nvoid matrix_sliceconstructor(unsigned int *slicesize,unsigned int ndim,value* out){\n\tunsigned int numcol = 1;\n\tif (ndim == 2) {\n\t\tnumcol = slicesize[1];\n\t}\n\t*out = MORPHO_OBJECT(object_newmatrix(slicesize[0],numcol,false));\n}\n/** Copies data from a at indx to out at newindx with a generic interface */\nobjectarrayerror matrix_slicecopy(value * a,value * out, unsigned int ndim, unsigned int *indx,unsigned int *newindx){\n\tdouble num; // matrices store doubles;\n\tunsigned int colindx = 0;\n\tunsigned int colnewindx = 0;\t\n\t\n\tif (ndim == 2) {\n\t\tcolindx = indx[1];\n\t\tcolnewindx = newindx[1];\n\t}\n\n\tif (!(matrix_getelement(MORPHO_GETMATRIX(*a),indx[0],colindx,&num)&&\n\t\tmatrix_setelement(MORPHO_GETMATRIX(*out),newindx[0],colnewindx,num))){\n\t\treturn ARRAY_OUTOFBOUNDS;\n\t}\n\treturn ARRAY_OK;\n}\n\n/** Rolls the matrix list */\nvoid matrix_rollflat(objectmatrix *a, objectmatrix *b, int nplaces) {\n    unsigned int N = a->nrows*a->ncols;\n    int n = abs(nplaces);\n    if (n>N) n = n % N;\n    unsigned int Np = N - n; // Number of elements to roll\n    \n    if (nplaces<0) {\n        memcpy(b->matrixdata, a->matrixdata+n, sizeof(double)*Np);\n        memcpy(b->matrixdata+Np, a->matrixdata, sizeof(double)*n);\n    } else {\n        memcpy(b->matrixdata+n, a->matrixdata, sizeof(double)*Np);\n        if (n>0) memcpy(b->matrixdata, a->matrixdata+Np, sizeof(double)*n);\n    }\n}\n\n/** Copies arow from matrix a into brow for matrix b */\nvoid matrix_copyrow(objectmatrix *a, int arow, objectmatrix *b, int brow) {\n    cblas_dcopy(a->ncols, a->elements+arow, a->nrows, b->elements+brow, a->nrows);\n}\n\n/** Rolls a list by a number of elements */\nobjectmatrix *matrix_roll(objectmatrix *a, int nplaces, int axis) {\n    objectmatrix *new=object_newmatrix(a->nrows, a->ncols, false);\n    \n    if (new) {\n        switch(axis) {\n            case 0: { // TODO: Could probably be faster\n                for (int i=0; i<a->nrows; i++) {\n                    int j = (i+nplaces);\n                    if (j<0) j+=a->nrows;\n                    matrix_copyrow(a, i, new, j % a->nrows);\n                }\n            }\n                break;\n            case 1: matrix_rollflat(a, new, nplaces*a->nrows); break;\n        }\n    }\n\n    return new;\n}\n\n/** Gets the matrix element with given indices */\nvalue Matrix_getindex(vm *v, int nargs, value *args) {\n    objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    unsigned int indx[2]={0,0};\n    value out = MORPHO_NIL;\n\tif (nargs>2){\n\t\tmorpho_runtimeerror(v, MATRIX_INVLDNUMINDICES);\n\t\treturn out;\n\t}\n    \n    if (array_valuelisttoindices(nargs, args+1, indx)) {\n        double outval;\n        if (!matrix_getelement(m, indx[0], indx[1], &outval)) {\n            morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n        } else {\n            out = MORPHO_FLOAT(outval);\n        }\n    } else { // now try to get a slice\n\t\tobjectarrayerror err = getslice(&MORPHO_SELF(args), &matrix_slicedim, &matrix_sliceconstructor, &matrix_slicecopy, nargs, &MORPHO_GETARG(args,0), &out);\n\t\tif (err!=ARRAY_OK) MORPHO_RAISE(v, array_to_matrix_error(err) );\n\t\tif (MORPHO_ISOBJECT(out)){\n\t\t\tmorpho_bindobjects(v,1,&out);\n\t\t} else morpho_runtimeerror(v, MATRIX_INVLDINDICES);\n\t}\n    return out;\n}\n\n/** Sets the matrix element with given indices */\nvalue Matrix_setindex(vm *v, int nargs, value *args) {\n    objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    unsigned int indx[2]={0,0};\n    \n    if (array_valuelisttoindices(nargs-1, args+1, indx)) {\n        double value=0.0;\n        if (MORPHO_ISFLOAT(args[nargs])) value=MORPHO_GETFLOATVALUE(args[nargs]);\n        if (MORPHO_ISINTEGER(args[nargs])) value=(double) MORPHO_GETINTEGERVALUE(args[nargs]);\n\n        if (!matrix_setelement(m, indx[0], indx[1], value)) {\n            morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n        }\n    } else morpho_runtimeerror(v, MATRIX_INVLDINDICES);\n    \n    return MORPHO_NIL;\n}\n\n/** Sets the column of a matrix */\nvalue Matrix_setcolumn(vm *v, int nargs, value *args) {\n    objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    \n    if (nargs==2 &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISMATRIX(MORPHO_GETARG(args, 1))) {\n        unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        objectmatrix *src = MORPHO_GETMATRIX(MORPHO_GETARG(args, 1));\n        \n        if (col<m->ncols) {\n            if (src && src->ncols*src->nrows==m->nrows) {\n                matrix_setcolumn(m, col, src->elements);\n            } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n        } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n    } else morpho_runtimeerror(v, MATRIX_SETCOLARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Gets a column of a matrix */\nvalue Matrix_getcolumn(vm *v, int nargs, value *args) {\n    objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    \n    if (nargs==1 &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        \n        if (col<m->ncols) {\n            double *vals;\n            if (matrix_getcolumn(m, col, &vals)) {\n                objectmatrix *new=object_matrixfromfloats(m->nrows, 1, vals);\n                if (new) {\n                    out=MORPHO_OBJECT(new);\n                    morpho_bindobjects(v, 1, &out);\n                }\n            }\n        } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n    } else morpho_runtimeerror(v, MATRIX_SETCOLARGS);\n    \n    return out;\n}\n\n/** Prints a matrix */\nvalue Matrix_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISMATRIX(self)) return Object_print(v, nargs, args);\n    \n    objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    matrix_print(v, m);\n    return MORPHO_NIL;\n}\n\n/** Formatted conversion to a string */\nvalue Matrix_format(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    \n    if (nargs==1 &&\n        MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) {\n        varray_char str;\n        varray_charinit(&str);\n        \n        if (matrix_printtobuffer(MORPHO_GETMATRIX(MORPHO_SELF(args)),\n                                 MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)),\n                                 &str)) {\n            out = object_stringfromvarraychar(&str);\n            if (MORPHO_ISOBJECT(out)) morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        \n        varray_charclear(&str);\n    } else {\n        morpho_runtimeerror(v, VALUE_FRMTARG);\n    }\n    \n    return out;\n}\n\n/** Matrix add */\nvalue Matrix_assign(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        if (a->ncols==b->ncols && a->nrows==b->nrows) {\n            matrix_copy(b, a);\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    }\n    \n    return MORPHO_NIL;\n}\n\n/** Matrix add */\nvalue Matrix_add(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        if (a->ncols==b->ncols && a->nrows==b->nrows) {\n            objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_add(a, b, new);\n            }\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_addscalar(a, 1.0, val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Right add */\nvalue Matrix_addr(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ||\n                     MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) {\n        int i=0;\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))<MORPHO_EPS ? 0 : 1);\n        \n        if (i==0) {\n            objectmatrix *new = object_clonematrix(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else { // If there is a value we're trying to add to just use Matrix_add for that \n            out = Matrix_add(v, nargs, args);\n        }\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Matrix subtract */\nvalue Matrix_sub(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        if (a->ncols==b->ncols && a->nrows==b->nrows) {\n            objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_sub(a, b, new);\n            }\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double val;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n            objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_addscalar(a, 1.0, -val, new);\n            }\n        }\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out);\n    \n    return out;\n}\n\n/** Right subtract */\nvalue Matrix_subr(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ||\n                     MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) {\n        int i=(MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ? 0 : MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)));\n\n        if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))<MORPHO_EPS ? 0 : 1);\n\n        \n        if (i==0) {\n            objectmatrix *new = object_clonematrix(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(new, -1.0);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else if (MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n            // try and subtract like normal\n            double val;\n            if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) {\n                objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false);\n                if (new) {\n                    matrix_addscalar(a, 1.0, -val, new);\n                    // now that did self - arg[0] and we want arg[0] - self so scale the whole thing by -1\n                    matrix_scale(new, -1.0);\n                    out=MORPHO_OBJECT(new);\n                    morpho_bindobjects(v, 1, &out);\n                }\n            }\n\n        } else morpho_runtimeerror(v, VM_INVALIDARGS);\n    } else morpho_runtimeerror(v, VM_INVALIDARGS);\n    \n    return out;\n}\n\n/** Matrix multiply */\nvalue Matrix_mul(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        if (a->ncols==b->nrows) {\n            objectmatrix *new = object_newmatrix(a->nrows, b->ncols, false);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_mul(a, b, new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double scale=1.0;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) {\n            objectmatrix *new = object_clonematrix(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(new, scale);\n                morpho_bindobjects(v, 1, &out);\n            }\n        }\n#ifdef MORPHO_INCLUDE_SPARSE\n    } else if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) {\n        // Returns nil to ensure it gets passed to mulr on Sparse\n#endif\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Called when multiplying on the right */\nvalue Matrix_mulr(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        double scale=1.0;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) {\n            objectmatrix *new = object_clonematrix(a);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(new, scale);\n                morpho_bindobjects(v, 1, &out);\n            }\n        }\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Solution of linear system a.x = b (i.e. x = b/a) */\nvalue Matrix_div(vm *v, int nargs, value *args) {\n    objectmatrix *b=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *a=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        if (a->ncols==b->nrows) {\n            objectmatrix *new = object_newmatrix(b->nrows, b->ncols, false);\n            if (new) {\n                objectmatrixerror err;\n                if (MATRIX_ISSMALL(a)) {\n                    err=matrix_divs(a, b, new);\n                } else {\n                    err=matrix_divl(a, b, new);\n                }\n                if (err==MATRIX_SING) {\n                    morpho_runtimeerror(v, MATRIX_SINGULAR);\n                    object_free((object *) new);\n                } else {\n                    out=MORPHO_OBJECT(new);\n                    morpho_bindobjects(v, 1, &out);\n                }\n            }\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n#ifdef MORPHO_INCLUDE_SPARSE\n    } else if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) {\n        /* Division by a sparse matrix: redirect to the divr selector of Sparse. */\n        value vargs[2]={args[1],args[0]};\n        return Sparse_divr(v, nargs, vargs);\n#endif\n    } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n        /* Division by a scalar */\n        double scale=1.0;\n        if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) {\n            if (fabs(scale)<MORPHO_EPS) MORPHO_RAISE(v, VM_DVZR);\n            \n            objectmatrix *new = object_clonematrix(b);\n            if (new) {\n                out=MORPHO_OBJECT(new);\n                matrix_scale(new, 1.0/scale);\n                morpho_bindobjects(v, 1, &out);\n            }\n        }\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Matrix accumulate */\nvalue Matrix_acc(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==2 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISMATRIX(MORPHO_GETARG(args, 1))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 1));\n        \n        if (a->ncols==b->ncols && a->nrows==b->nrows) {\n            out=MORPHO_SELF(args);\n            double lambda=1.0;\n            morpho_valuetofloat(MORPHO_GETARG(args, 0), &lambda);\n            matrix_accumulate(a, lambda, b);\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Frobenius inner product */\nvalue Matrix_inner(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        \n        double prod=0.0;\n        if (matrix_inner(a, b, &prod)==MATRIX_OK) {\n            out = MORPHO_FLOAT(prod);\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Outer product */\nvalue Matrix_outer(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n        objectmatrix *new=object_newmatrix(a->nrows*a->ncols, b->nrows*b->ncols, true);\n        \n        if (new &&\n            matrix_outer(a, b, new)==MATRIX_OK) {\n            out=MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n    } else morpho_runtimeerror(v, MATRIX_ARITHARGS);\n    \n    return out;\n}\n\n/** Matrix sum */\nvalue Matrix_sum(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    return MORPHO_FLOAT(matrix_sum(a));\n}\n\n/** Roll a matrix */\nvalue Matrix_roll(vm *v, int nargs, value *args) {\n    objectmatrix *slf = MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n    int roll, axis=0;\n\n    if (nargs>0 &&\n        morpho_valuetoint(MORPHO_GETARG(args, 0), &roll)) {\n        \n        if (nargs==2 && !morpho_valuetoint(MORPHO_GETARG(args, 1), &axis)) return out;\n        \n        objectmatrix *new = matrix_roll(slf, roll, axis);\n\n        if (new) {\n            out = MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        }\n\n    } else morpho_runtimeerror(v, LIST_ADDARGS);\n\n    return out;\n}\n\n\n/** Matrix norm */\nvalue Matrix_norm(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n    \n    if (nargs==1) {\n        value arg = MORPHO_GETARG(args, 0);\n        \n        if (MORPHO_ISNUMBER(arg)) {\n            double n;\n            \n            if (morpho_valuetofloat(arg, &n)) {\n                if (fabs(n-1.0)<MORPHO_EPS) {\n                    out=MORPHO_FLOAT(matrix_L1norm(a));\n                } else if (fabs(n-2.0)<MORPHO_EPS) {\n                    out=MORPHO_FLOAT(matrix_norm(a));\n                } else if (isinf(n)) {\n                    out=MORPHO_FLOAT(matrix_Linfnorm(a));\n                } else {\n                    out=MORPHO_FLOAT(matrix_Lnnorm(a, n));\n                }\n            } else morpho_runtimeerror(v, MATRIX_NORMARGS);\n        } else morpho_runtimeerror(v, MATRIX_NORMARGS);\n    } else if (nargs==0) {\n        out=MORPHO_FLOAT(matrix_norm(a));\n    } else morpho_runtimeerror(v, MATRIX_NORMARGS);\n    \n    return out;\n}\n\n/** Matrix eigenvalues */\nvalue Matrix_eigenvalues(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value evals=MORPHO_NIL;\n    \n    if (matrix_eigen(v, a, &evals, NULL)) {\n        objectlist *new = MORPHO_GETLIST(evals);\n        list_append(new, evals); // Ensure we retain the List object\n        morpho_bindobjects(v, new->val.count, new->val.data);\n        new->val.count--; // And pop it back off\n    }\n    \n    return evals;\n}\n\n/** Matrix eigensystem */\nvalue Matrix_eigensystem(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value evals=MORPHO_NIL, evecs=MORPHO_NIL, out=MORPHO_NIL;\n    objectlist *resultlist = object_newlist(0, NULL);\n    if (!resultlist) {\n        morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        return MORPHO_NIL;\n    }\n    \n    if (matrix_eigen(v, a, &evals, &evecs)) {\n        objectlist *evallist = MORPHO_GETLIST(evals);\n        \n        list_append(resultlist, evals); // Create the output list\n        list_append(resultlist, evecs);\n        out=MORPHO_OBJECT(resultlist);\n        \n        list_append(evallist, evals); // Ensure we bind all objects at once\n        list_append(evallist, evecs); // by popping them onto the evallist.\n        list_append(evallist, out);   //\n        morpho_bindobjects(v, evallist->val.count, evallist->val.data);\n        evallist->val.count-=3; // and then popping them back off.\n    }\n    \n    return out;\n}\n\n/** Inverts a matrix */\nvalue Matrix_inverse(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    // The inverse will have the number of rows and number of columns\n    // swapped. \n    objectmatrix *new = object_newmatrix(a->ncols, a->nrows, false);\n    if (new) {\n        objectmatrixerror mi = matrix_inverse(a, new);\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n        \n        if (mi!=MATRIX_OK) matrix_raiseerror(v, mi);\n    }\n    \n    return out;\n}\n\n/** Transpose of a matrix */\nvalue Matrix_transpose(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n \n    objectmatrix *new = object_newmatrix(a->ncols, a->nrows, false);\n    if (new) {\n        matrix_transpose(a, new);\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n    \n    return out;\n}\n\n/** Reshape a matrix */\nvalue Matrix_reshape(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    \n    if (nargs==2 &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) {\n        int nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        int ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1));\n        \n        if (nrows*ncols==a->nrows*a->ncols) {\n            a->nrows=nrows;\n            a->ncols=ncols;\n        } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);   \n    } else morpho_runtimeerror(v, MATRIX_RESHAPEARGS);\n    \n    return MORPHO_NIL;\n}\n\n/** Trace of a matrix */\nvalue Matrix_trace(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (a->nrows==a->ncols) {\n        double tr;\n        if (matrix_trace(a, &tr)==MATRIX_OK) out=MORPHO_FLOAT(tr);\n    } else {\n        morpho_runtimeerror(v, MATRIX_NOTSQ);\n    }\n    \n    return out;\n}\n\n/** Enumerate protocol */\nvalue Matrix_enumerate(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    \n    if (nargs==1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            \n            if (i<0) out=MORPHO_INTEGER(a->ncols*a->nrows);\n            else if (i<a->ncols*a->nrows) out=MORPHO_FLOAT(a->elements[i]);\n        }\n    }\n    \n    return out;\n}\n\n\n/** Number of matrix elements */\nvalue Matrix_count(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    \n    return MORPHO_INTEGER(a->ncols*a->nrows);\n}\n\n/** Matrix dimensions */\nvalue Matrix_dimensions(vm *v, int nargs, value *args) {\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    value dim[2];\n    value out=MORPHO_NIL;\n    \n    dim[0]=MORPHO_INTEGER(a->nrows);\n    dim[1]=MORPHO_INTEGER(a->ncols);\n    \n    objectlist *new=object_newlist(2, dim);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    \n    return out;\n}\n\n/** Clones a matrix */\nvalue Matrix_clone(vm *v, int nargs, value *args) {\n    value out=MORPHO_NIL;\n    objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args));\n    objectmatrix *new=object_clonematrix(a);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    return out;\n}\n\nMORPHO_BEGINCLASS(Matrix)\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Matrix_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Matrix_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_GETCOLUMN_METHOD, Matrix_getcolumn, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_SETCOLUMN_METHOD, Matrix_setcolumn, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Matrix_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_FORMAT_METHOD, Matrix_format, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ASSIGN_METHOD, Matrix_assign, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADD_METHOD, Matrix_add, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADDR_METHOD, Matrix_addr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUB_METHOD, Matrix_sub, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUBR_METHOD, Matrix_subr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MUL_METHOD, Matrix_mul, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MULR_METHOD, Matrix_mulr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIV_METHOD, Matrix_div, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ACC_METHOD, Matrix_acc, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_INNER_METHOD, Matrix_inner, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_OUTER_METHOD, Matrix_outer, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUM_METHOD, Matrix_sum, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_NORM_METHOD, Matrix_norm, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_INVERSE_METHOD, Matrix_inverse, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_TRANSPOSE_METHOD, Matrix_transpose, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_RESHAPE_METHOD, Matrix_reshape, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_EIGENVALUES_METHOD, Matrix_eigenvalues, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_EIGENSYSTEM_METHOD, Matrix_eigensystem, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_TRACE_METHOD, Matrix_trace, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Matrix_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Matrix_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_DIMENSIONS_METHOD, Matrix_dimensions, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ROLL_METHOD, Matrix_roll, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Matrix_clone, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* **********************************************************************\n * Initialization\n * ********************************************************************* */\n\nvoid matrix_initialize(void) {\n    objectmatrixtype=object_addtype(&objectmatrixdefn);\n    \n    builtin_addfunction(MATRIX_CLASSNAME, matrix_constructor, MORPHO_FN_CONSTRUCTOR);\n    builtin_addfunction(MATRIX_IDENTITYCONSTRUCTOR, matrix_identityconstructor, BUILTIN_FLAGSEMPTY);\n    \n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value matrixclass=builtin_addclass(MATRIX_CLASSNAME, MORPHO_GETCLASSDEFINITION(Matrix), objclass);\n    object_setveneerclass(OBJECT_MATRIX, matrixclass);\n    \n    morpho_defineerror(MATRIX_INDICESOUTSIDEBOUNDS, ERROR_HALT, MATRIX_INDICESOUTSIDEBOUNDS_MSG);\n    morpho_defineerror(MATRIX_INVLDINDICES, ERROR_HALT, MATRIX_INVLDINDICES_MSG);\n    morpho_defineerror(MATRIX_INVLDNUMINDICES, ERROR_HALT, MATRIX_INVLDNUMINDICES_MSG);\n    morpho_defineerror(MATRIX_CONSTRUCTOR, ERROR_HALT, MATRIX_CONSTRUCTOR_MSG);\n    morpho_defineerror(MATRIX_INVLDARRAYINIT, ERROR_HALT, MATRIX_INVLDARRAYINIT_MSG);\n    morpho_defineerror(MATRIX_ARITHARGS, ERROR_HALT, MATRIX_ARITHARGS_MSG);\n    morpho_defineerror(MATRIX_RESHAPEARGS, ERROR_HALT, MATRIX_RESHAPEARGS_MSG);\n    morpho_defineerror(MATRIX_INCOMPATIBLEMATRICES, ERROR_HALT, MATRIX_INCOMPATIBLEMATRICES_MSG);\n    morpho_defineerror(MATRIX_SINGULAR, ERROR_HALT, MATRIX_SINGULAR_MSG);\n    morpho_defineerror(MATRIX_NOTSQ, ERROR_HALT, MATRIX_NOTSQ_MSG);\n    morpho_defineerror(MATRIX_OPFAILED, ERROR_HALT, MATRIX_OPFAILED_MSG);\n    morpho_defineerror(MATRIX_SETCOLARGS, ERROR_HALT, MATRIX_SETCOLARGS_MSG);\n    morpho_defineerror(MATRIX_NORMARGS, ERROR_HALT, MATRIX_NORMARGS_MSG);\n    morpho_defineerror(MATRIX_IDENTCONSTRUCTOR, ERROR_HALT, MATRIX_IDENTCONSTRUCTOR_MSG);\n}\n\n#endif\n"
  },
  {
    "path": "src/linalg/matrix.h",
    "content": "/** @file matrix.h\n *  @author T J Atherton\n *\n *  @brief Veneer class over the objectmatrix type that interfaces with blas and lapack\n */\n\n#ifndef matrix_h\n#define matrix_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_LINALG\n\n#include <stdio.h>\n#include \"classes.h\"\n/** Use Apple's Accelerate library for LAPACK and BLAS */\n#ifdef __APPLE__\n#ifdef MORPHO_LINALG_USE_ACCELERATE\n#define ACCELERATE_NEW_LAPACK\n#include <Accelerate/Accelerate.h>\n#define MATRIX_LAPACK_PRESENT\n#endif\n#endif\n\n/** Otherwise, use LAPACKE */\n#ifndef MATRIX_LAPACK_PRESENT\n#include <cblas.h>\n#include <lapacke.h>\n#define MORPHO_LINALG_USE_LAPACKE\n#define MATRIX_LAPACK_PRESENT\n#endif\n\n#include \"cmplx.h\"\n#include \"list.h\"\n\n/* -------------------------------------------------------\n * Matrix objects\n * ------------------------------------------------------- */\n\nextern objecttype objectmatrixtype;\n#define OBJECT_MATRIX objectmatrixtype\n\n/** Matrices are a purely numerical collection type oriented toward linear algebra.\n    Elements are stored in column-major format, i.e.\n        [ 1 2 ]\n        [ 3 4 ]\n    is stored ( 1, 3, 2, 4 ) in memory. This is for compatibility with standard linear algebra packages */\n\ntypedef struct {\n    object obj;\n    unsigned int nrows;\n    unsigned int ncols;\n    double *elements;\n    double matrixdata[];\n} objectmatrix;\n\n/** Tests whether an object is a matrix */\n#define MORPHO_ISMATRIX(val) object_istype(val, OBJECT_MATRIX)\n\n/** Gets the object as an matrix */\n#define MORPHO_GETMATRIX(val)   ((objectmatrix *) MORPHO_GETOBJECT(val))\n\n/** Creates a matrix object */\nobjectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero);\n\n/** Creates a new matrix from an array */\nobjectmatrix *object_matrixfromarray(objectarray *array);\n\n/** Creates a new matrix from an existing matrix */\nobjectmatrix *object_clonematrix(objectmatrix *array);\n\n/** @brief Use to create static matrices on the C stack\n    @details Intended for small matrices; Caller needs to supply a double array of size nr*nc. */\n#define MORPHO_STATICMATRIX(darray, nr, nc)      { .obj.type=OBJECT_MATRIX, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .elements=darray, .nrows=nr, .ncols=nc }\n\n/** Macro to decide if a matrix is 'small' or 'large' and hence static or dynamic allocation should be used. */\n#define MATRIX_ISSMALL(m) (m->nrows*m->ncols<MORPHO_MAXIMUMSTACKALLOC)\n\n/* -------------------------------------------------------\n * Matrix veneer class\n * ------------------------------------------------------- */\n\n#define MATRIX_CLASSNAME \"Matrix\"\n\n#define MATRIX_IDENTITYCONSTRUCTOR \"IdentityMatrix\"\n\n#define MATRIX_INVERSE_METHOD \"inverse\"\n#define MATRIX_TRANSPOSE_METHOD \"transpose\"\n#define MATRIX_TRACE_METHOD \"trace\"\n#define MATRIX_INNER_METHOD \"inner\"\n#define MATRIX_OUTER_METHOD \"outer\"\n#define MATRIX_DET_METHOD \"det\"\n#define MATRIX_EIGENVALUES_METHOD \"eigenvalues\"\n#define MATRIX_EIGENSYSTEM_METHOD \"eigensystem\"\n#define MATRIX_NORM_METHOD \"norm\"\n#define MATRIX_GETCOLUMN_METHOD \"column\"\n#define MATRIX_SETCOLUMN_METHOD \"setcolumn\"\n#define MATRIX_RESHAPE_METHOD \"reshape\"\n#define MATRIX_EIGENVALUES_METHOD \"eigenvalues\"\n#define MATRIX_EIGENSYSTEM_METHOD \"eigensystem\"\n\n#define MATRIX_DIMENSIONS_METHOD \"dimensions\"\n\n/* -------------------------------------------------------\n * Matrix error messages\n * ------------------------------------------------------- */\n\n#define MATRIX_INDICESOUTSIDEBOUNDS       \"MtrxBnds\"\n#define MATRIX_INDICESOUTSIDEBOUNDS_MSG   \"Matrix index out of bounds.\"\n\n#define MATRIX_INVLDINDICES               \"MtrxInvldIndx\"\n#define MATRIX_INVLDINDICES_MSG           \"Matrix indices must be integers.\"\n\n#define MATRIX_INVLDNUMINDICES            \"MtrxInvldNumIndx\"\n#define MATRIX_INVLDNUMINDICES_MSG        \"Matrix expects two arguments for indexing.\"\n\n#define MATRIX_CONSTRUCTOR                \"MtrxCns\"\n#define MATRIX_CONSTRUCTOR_MSG            \"Matrix() constructor should be called either with dimensions or an array, list or matrix initializer.\"\n\n#define MATRIX_IDENTCONSTRUCTOR           \"MtrxIdnttyCns\"\n#define MATRIX_IDENTCONSTRUCTOR_MSG       \"IdentityMatrix expects the dimension as its argument.\"\n\n#define MATRIX_INVLDARRAYINIT             \"MtrxInvldInit\"\n#define MATRIX_INVLDARRAYINIT_MSG         \"Invalid initializer passed to Matrix().\"\n\n#define MATRIX_ARITHARGS                  \"MtrxInvldArg\"\n#define MATRIX_ARITHARGS_MSG              \"Matrix arithmetic methods expect a matrix or number as their argument.\"\n\n#define MATRIX_RESHAPEARGS                \"MtrxRShpArg\"\n#define MATRIX_RESHAPEARGS_MSG            \"Reshape requires two integer arguments.\"\n\n#define MATRIX_INCOMPATIBLEMATRICES       \"MtrxIncmptbl\"\n#define MATRIX_INCOMPATIBLEMATRICES_MSG   \"Matrices have incompatible shape.\"\n\n#define MATRIX_SINGULAR                   \"MtrxSnglr\"\n#define MATRIX_SINGULAR_MSG               \"Matrix is singular.\"\n\n#define MATRIX_NOTSQ                      \"MtrxNtSq\"\n#define MATRIX_NOTSQ_MSG                  \"Matrix is not square.\"\n\n#define MATRIX_OPFAILED                   \"MtrxOpFld\"\n#define MATRIX_OPFAILED_MSG               \"Matrix operation failed.\"\n\n#define MATRIX_SETCOLARGS                 \"MtrxStClArgs\"\n#define MATRIX_SETCOLARGS_MSG             \"Method setcolumn expects an integer column index and a column matrix as arguments.\"\n\n#define MATRIX_NORMARGS                   \"MtrxNrmArgs\"\n#define MATRIX_NORMARGS_MSG               \"Method norm expects an (optional) numerical argument.\"\n\n/* -------------------------------------------------------\n * objectmatrixerror type\n * ------------------------------------------------------- */\n\ntypedef enum { MATRIX_OK, MATRIX_INCMPTBLDIM, MATRIX_SING, MATRIX_INVLD, MATRIX_BNDS, MATRIX_NSQ, MATRIX_FAILED, MATRIX_ALLOC } objectmatrixerror;\n\n/* -------------------------------------------------------\n * Matrix interface\n * ------------------------------------------------------- */\n\nbool matrix_getarraydimensions(objectarray *array, unsigned int dim[], unsigned int maxdim, unsigned int *ndim);\nvalue matrix_getarrayelement(objectarray *array, unsigned int ndim, unsigned int *indx);\n\nbool matrix_getlistdimensions(objectlist *list, unsigned int dim[], unsigned int maxdim, unsigned int *ndim);\nbool matrix_getlistelement(objectlist *list, unsigned int ndim, unsigned int *indx, value *val);\n\nbool matrix_setelement(objectmatrix *matrix, unsigned int row, unsigned int col, double value);\nbool matrix_getelement(objectmatrix *matrix, unsigned int row, unsigned int col, double *value);\n\nbool matrix_getcolumn(objectmatrix *matrix, unsigned int col, double **v);\nbool matrix_setcolumn(objectmatrix *matrix, unsigned int col, double *v);\nbool matrix_addtocolumn(objectmatrix *m, unsigned int col, double alpha, double *v);\n\nunsigned int matrix_countdof(objectmatrix *a);\n\nobjectmatrixerror matrix_copy(objectmatrix *a, objectmatrix *out);\nobjectmatrixerror matrix_copyat(objectmatrix *a, objectmatrix *out, int row0, int col0);\nobjectmatrixerror matrix_add(objectmatrix *a, objectmatrix *b, objectmatrix *out);\nobjectmatrixerror matrix_accumulate(objectmatrix *a, double lambda, objectmatrix *b);\nobjectmatrixerror matrix_sub(objectmatrix *a, objectmatrix *b, objectmatrix *out);\nobjectmatrixerror matrix_mul(objectmatrix *a, objectmatrix *b, objectmatrix *out);\nobjectmatrixerror matrix_inner(objectmatrix *a, objectmatrix *b, double *out);\nobjectmatrixerror matrix_outer(objectmatrix *a, objectmatrix *b, objectmatrix *out);\nobjectmatrixerror matrix_divs(objectmatrix *a, objectmatrix *b, objectmatrix *out);\nobjectmatrixerror matrix_divl(objectmatrix *a, objectmatrix *b, objectmatrix *out);\nobjectmatrixerror matrix_inverse(objectmatrix *a, objectmatrix *out);\nobjectmatrixerror matrix_transpose(objectmatrix *a, objectmatrix *out);\nobjectmatrixerror matrix_trace(objectmatrix *a, double *out);\nobjectmatrixerror matrix_scale(objectmatrix *a, double scale);\nobjectmatrixerror matrix_identity(objectmatrix *a);\nobjectmatrixerror matrix_zero(objectmatrix *a);\ndouble matrix_sum(objectmatrix *a);\nobjectmatrixerror matrix_eigensystem(objectmatrix *a, double *wr, double *wi, objectmatrix *vec);\nbool matrix_eigen(vm *v, objectmatrix *a, value *evals, value *evecs);\n\ndouble matrix_norm(objectmatrix *a);\ndouble matrix_L1norm(objectmatrix *a);\ndouble matrix_Lnnorm(objectmatrix *a, double n);\ndouble matrix_Linfnorm(objectmatrix *a);\n\nvoid matrix_print(vm *v, objectmatrix *m);\n\nvoid matrix_initialize(void);\n\n#endif\n\n#endif /* matrix_h */\n"
  },
  {
    "path": "src/linalg/sparse.c",
    "content": "/** @file sparse.c\n *  @author T J Atherton\n *\n *  @brief Veneer class over the objectsparse type that provides sparse matrices\n */\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_SPARSE\n\n#include <limits.h>\n#include <stdlib.h>\n\n#include \"morpho.h\"\n#include \"classes.h\"\n\n#include \"sparse.h\"\n#include \"matrix.h\"\n\n/* ***************************************\n * Compatibility with Sparse libraries\n * *************************************** */\n\n/** ---- CSparse --- */\n#ifdef MORPHO_LINALG_USE_CSPARSE\n#include <cs.h>\n\n/* Convert a CCS structure into a CSPARSE structure */\nvoid sparse_ccstocsparse(sparseccs *s, cs *out) {\n    out->nzmax=s->nentries;\n    out->m=s->nrows;\n    out->n=s->ncols;\n    out->p=s->cptr;\n    out->i=s->rix;\n    out->x=s->values;\n    out->nz=-1;\n}\n\n/* Convert a CCS structure into a CSPARSE structure */\nvoid sparse_csparsetoccs(cs *in, sparseccs *out) {\n    out->nentries=in->nzmax;\n    out->nrows=in->m;\n    out->ncols=in->n;\n    out->cptr=in->p;\n    out->rix=in->i;\n    out->values=in->x;\n}\n#endif\n\n/* ***************************************\n * Dictionary of keys format\n * *************************************** */\n\nobjecttype objectdokkeytype;\n\n/** DOK key object definitions */\nvoid objectdokkey_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<DOK key>\");\n}\n\nsize_t objectdokkey_sizefn(object *obj) {\n    return sizeof(objectdokkey);\n}\n\n/** Fibonacci hash function for pairs of integers. */\nhash objectdokkey_hashfn(object *obj) {\n    objectdokkey *key = (objectdokkey *) obj;\n    uint64_t i1 = MORPHO_GETDOKKEYROW(key);\n    uint64_t i2 = MORPHO_GETDOKKEYCOL(key);\n    return ((i1<<32 | i2) * 11400714819323198485llu)>> 32;\n}\n\nint objectdokkey_cmpfn(object *a, object *b) {\n    objectdokkey *akey = (objectdokkey *) a;\n    objectdokkey *bkey = (objectdokkey *) b;\n\n    return ((MORPHO_GETDOKKEYCOL(akey)==MORPHO_GETDOKKEYCOL(bkey) &&\n             MORPHO_GETDOKKEYROW(akey)==MORPHO_GETDOKKEYROW(bkey)) ? MORPHO_EQUAL : MORPHO_NOTEQUAL);\n}\n\nobjecttypedefn objectdokkeydefn = {\n    .printfn=objectdokkey_printfn,\n    .markfn=NULL,\n    .freefn=NULL,\n    .sizefn=objectdokkey_sizefn,\n    .hashfn=objectdokkey_hashfn,\n    .cmpfn=objectdokkey_cmpfn\n};\n\nDEFINE_VARRAY(dokkey, objectdokkey);\n\n/** Initializes a sparsedok structure */\nvoid sparsedok_init(sparsedok *dok) {\n    dok->nrows=0;\n    dok->ncols=0;\n    dictionary_init(&dok->dict);\n    dok->keys=NULL;\n}\n\n/** Clears a sparsedok structure */\nvoid sparsedok_clear(sparsedok *dok) {\n    objectdokkey *next=NULL;\n    for (objectdokkey *key=dok->keys; key!=NULL; key=next) {\n        next=(objectdokkey *) key->obj.next;\n        MORPHO_FREE(key);\n    }\n    dictionary_clear(&dok->dict);\n    sparsedok_init(dok);\n}\n\n/** Create a new key from a pair of indices */\nstatic objectdokkey *sparsedok_newkey(sparsedok *dok, int i, int j) {\n    objectdokkey *key = MORPHO_MALLOC(sizeof(objectdokkey));\n\n    if (key) {\n        object_init((object *) key, OBJECT_DOKKEY);\n        key->row=i;\n        key->col=j;\n    }\n    return key;\n}\n\n/** Adds a key to a dok structure.\n * @returns the key added. */\nstatic objectdokkey *sparsedok_addkey(sparsedok *dok, int i, int j) {\n    objectdokkey *out=sparsedok_newkey(dok, i, j);\n\n    if (out) {\n        out->obj.next=(object *) dok->keys;\n        dok->keys=out;\n    }\n\n    return out;\n}\n\n/** Inserts a matrix element (i,j) -> val into a sparsedok structure\n * @returns true on success. */\nbool sparsedok_insert(sparsedok *dok, int i, int j, value val) {\n    objectdokkey key=MORPHO_STATICDOKKEY(i, j);\n    if (!dictionary_get(&dok->dict, MORPHO_OBJECT(&key), NULL)) {\n        objectdokkey *newkey=sparsedok_addkey(dok, i, j);\n        if (newkey) {\n            if (dok->nrows==0 || i>=dok->nrows) dok->nrows=i+1;\n            if (dok->ncols==0 || j>=dok->ncols) dok->ncols=j+1;\n            return dictionary_insert(&dok->dict, MORPHO_OBJECT(newkey), val);\n        }\n    } else {\n        return dictionary_insert(&dok->dict, MORPHO_OBJECT(&key), val);\n    }\n\n    return false;\n}\n\n/** Retrieves a matrix element (i,j) from a sparsedok structure\n * @returns true on success. */\nbool sparsedok_get(sparsedok *dok, int i, int j, value *val) {\n    objectdokkey key=MORPHO_STATICDOKKEY(i, j);\n    return dictionary_get(&dok->dict, MORPHO_OBJECT(&key), val);\n}\n\n/** Removes a matrix element (i,j) from a sparsedok\n * @returns true on success.\n * @warning Use sparingly as the deleted key is not recovered. */\nbool sparsedok_remove(sparsedok *dok, int i, int j, value *val) {\n    objectdokkey key=MORPHO_STATICDOKKEY(i, j);\n    return dictionary_remove(&dok->dict, MORPHO_OBJECT(&key));\n}\n\n/** Sets the dimensions of the matrix\n * @returns true if successful, or false if the dimensions are incompatible with existing matrix entries\n * @details This function is intended for use in constructing matrix. */\nbool sparsedok_setdimensions(sparsedok *dok, int nrows, int ncols) {\n    if (nrows<dok->nrows || ncols<dok->ncols) return false;\n    dok->nrows=nrows;\n    dok->ncols=ncols;\n    return true;\n}\n\n/** Expands the dimensions of a matrix\n * @returns true if successful, or false if the dimensions are incompatible with existing matrix entries\n * @details This function is intended for use in constructing matrix. */\nbool sparsedok_expanddimensions(sparsedok *dok, int nrows, int ncols) {\n    if (nrows>dok->nrows) dok->nrows=nrows;\n    if (nrows>dok->ncols) dok->ncols=ncols;\n    return true;\n}\n\n/** Prints a sparsedok matrix */\nvoid sparsedok_print(vm *v, sparsedok *dok) {\n    value out;\n    for (int i=0; i<dok->nrows; i++) {\n        morpho_printf(v, \"[ \");\n        for (int j=0; j<dok->ncols; j++) {\n            if (sparsedok_get(dok, i, j, &out)) {\n                morpho_printvalue(v, out);\n                morpho_printf(v, \" \");\n            } else {\n                morpho_printf(v, \"0 \");\n            }\n        }\n        morpho_printf(v, \"]%s\", (i<dok->nrows-1 ? \"\\n\" : \"\"));\n    }\n}\n\n/** Number of entries in a sparsedok */\nunsigned int sparsedok_count(sparsedok *dok) {\n    return dok->dict.count;\n}\n\n/** Loop over dok keys - initializer\n * @param[in] dok - the dictionary of keys to loop over\n * @returns Initial value for the loop counter */\nvoid *sparsedok_loopstart(sparsedok *dok) {\n    return dok->keys;\n}\n\n/** Loop over dok keys\n * @param[in] dok - the dictionary of keys to loop over\n * @param[in] cntr - Pointer to loop counter of type (void *)\n * @param[out] i - row index.\n * @param[out] j - column index\n * @returns true if i, j contain valid data; cntri is updated */\nbool sparsedok_loop(sparsedok *dok, void **cntr, int *i, int *j) {\n    objectdokkey *key = *cntr;\n    if (key) {\n        *i = key->row;\n        *j = key->col;\n        *cntr=key->obj.next;\n    }\n    return key;\n}\n\n/* ***************\n * Copy operations\n * *************** */\n\n/* Copies a sparsedok object */\nbool sparsedok_copy(sparsedok *src, sparsedok *dest) {\n    int i, j;\n    void *ctr = sparsedok_loopstart(src);\n    value entry;\n\n    if (!sparsedok_setdimensions(dest, src->nrows, src->ncols)) return false;\n\n    while (sparsedok_loop(src, &ctr, &i, &j)) {\n        if (sparsedok_get(src, i, j, &entry)) {\n            if (!sparsedok_insert(dest, i, j, entry)) return false;\n        }\n    }\n\n    return true;\n}\n\n/* Copies a sparsedok object with a particular destination */\nbool sparsedok_copyat(sparsedok *src, sparsedok *dest, int row0, int col0) {\n    int i, j;\n    void *ctr = sparsedok_loopstart(src);\n    value entry;\n\n    while (sparsedok_loop(src, &ctr, &i, &j)) {\n        if (sparsedok_get(src, i, j, &entry)) {\n            if (!sparsedok_insert(dest, i+row0, j+col0, entry)) return false;\n        }\n    }\n\n    return true;\n}\n\n/** Copies a dense matrix to a sparse dok */\nbool sparsedok_copymatrixat(objectmatrix *src, sparsedok *dest, int row0, int col0) {\n    double val;\n    for (int j=0; j<src->ncols; j++) {\n        for (int i=0; i<src->nrows; i++) {\n            if (!(matrix_getelement(src, i, j, &val) &&\n                  sparsedok_insert(dest, i+row0, j+col0, MORPHO_FLOAT(val)))) return false;\n        }\n    }\n\n    return true;\n}\n\n/* Copies a sparsedok object to a dense matrix */\nbool sparsedok_copytomatrix(sparsedok *src, objectmatrix *dest, int row0, int col0) {\n    int i, j;\n    void *ctr = sparsedok_loopstart(src);\n    value entry;\n\n    while (sparsedok_loop(src, &ctr, &i, &j)) {\n        if (sparsedok_get(src, i, j, &entry)) {\n            double val=0.0;\n            if (!morpho_valuetofloat(entry, &val)) return false;\n            if (!matrix_setelement(dest, i+row0, j+col0, val)) return false;\n        }\n    }\n\n    return true;\n}\n\n/* ***************************************\n * Compressed Column Storage Format\n * *************************************** */\n\n/** Initializes an empty sparseccs */\nvoid sparseccs_init(sparseccs *ccs) {\n    ccs->nentries=0;\n    ccs->nrows=0;\n    ccs->ncols=0;\n    ccs->cptr=NULL;\n    ccs->rix=NULL;\n    ccs->values=NULL;\n}\n\n/** These wrappers enable us to ensure that we're using the same allocator as CSparse */\nstatic void _ccsfree(void *p) {\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    cs_free(p);\n#else \n    MORPHO_FREE(p);\n#endif\n}\n\nstatic void *_ccsrealloc(void *p, size_t newsize) {\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    CS_INT ok;\n    return cs_realloc(p, 1, newsize, &ok);\n#else\n    return MORPHO_REALLOC(P, newsize);\n#endif\n}\n\n/** Clears all data structures associated with a sparseccs */\nvoid sparseccs_clear(sparseccs *ccs) {\n    if (ccs->cptr) _ccsfree(ccs->cptr);\n    if (ccs->rix) _ccsfree(ccs->rix);\n    if (ccs->values) _ccsfree(ccs->values);\n    sparseccs_init(ccs);\n}\n\n/** Resizes a sparseccs */\nbool sparseccs_resize(sparseccs *ccs, int nrows, int ncols, unsigned int nentries, bool values) {\n    if (ncols>ccs->ncols) {\n        ccs->cptr=_ccsrealloc(ccs->cptr, sizeof(int)*(ncols+1));\n        if (ccs->values || values) {\n            ccs->values=_ccsrealloc(ccs->values, sizeof(double)*nentries);\n            if (!ccs->values) goto sparseccs_resize_error;\n        }\n    } \n\n    ccs->rix=_ccsrealloc(ccs->rix, sizeof(int)*nentries);\n    if (!(ccs->cptr && ccs->rix)) goto sparseccs_resize_error;\n\n    ccs->ncols=ncols;\n    ccs->nrows=nrows;\n    ccs->nentries=nentries;\n    return true;\n\nsparseccs_resize_error:\n    sparseccs_clear(ccs);\n    return false;\n}\n\n/** Retrieves the row indices given a column\n * @param[in] ccs   the matrix\n * @param[in] col  column index\n * @param[out] nentries  the number of entries\n * @param[out] entries  the entries themselves\n * @param[out] values  (optional) the values */\nbool sparseccs_getrowindiceswithvalues(sparseccs *ccs, int col, int *nentries, int **entries, double **values) {\n    if (col>=ccs->ncols) return false;\n    *nentries=ccs->cptr[col+1]-ccs->cptr[col];\n    *entries=ccs->rix+ccs->cptr[col];\n    if (values) *values=ccs->values+ccs->cptr[col];\n    return true;\n}\n\n/** Wrapper to sparseccs_getrowindiceswithvalues */\nbool sparseccs_getrowindices(sparseccs *ccs, int col, int *nentries, int **entries) {\n    return sparseccs_getrowindiceswithvalues(ccs,col,nentries,entries,NULL);\n}\n\n/** Sets the row indices given a column\n * @param[in] ccs   the matrix\n * @param[in] col  column index\n * @param[out] nentries  the number of entries\n * @param[out] entries  the entries themselves\n * @warning Use with caution */\nbool sparseccs_setrowindices(sparseccs *ccs, int col, int nentries, int *entries) {\n    if (col>=ccs->ncols) return false;\n    if (nentries!=ccs->cptr[col+1]-ccs->cptr[col]) return false;\n    int *e=ccs->rix+ccs->cptr[col];\n    for (unsigned int i=0; i<nentries; i++) e[i]=entries[i];\n\n    return true;\n}\n\n/** Retrieves the indices of non-zero columns\n * @param[in] ccs   the matrix\n * @param[in] maxentries maximum number of entries\n * @param[out] nentries  the number of entries\n * @param[out] entries  the entries themselves (call with NULL to get the size of the required array) */\nbool sparseccs_getcolindices(sparseccs *ccs, int maxentries, int *nentries, int *entries) {\n    int k=0;\n\n    for (int i=0; i<ccs->ncols; i++) {\n        if (ccs->cptr[i+1]!=ccs->cptr[i]) {\n            if (entries && k<maxentries) entries[k]=i;\n            k++;\n        }\n    }\n    *nentries=k;\n\n    return true;\n}\n\n/** Retrieves the indices of all columns that contain a nonzero entry on a particular row.\n * @param[in] ccs   the matrix\n * @param[in] row  the row to index\n * @param[in] maxentries maximum number of entries\n * @param[out] nentries  the number of entries\n * @param[out] entries  the entries themselves (call with NULL to get the size of the required array) */\nbool sparseccs_getcolindicesforrow(sparseccs *ccs, int row, int maxentries, int *nentries,  int *entries) {\n    int col=0, k=0;\n\n    for (unsigned int i=0; i<ccs->nentries; i++) {\n        while (ccs->cptr[col+1]<=i) col++;\n        if (ccs->rix[i]==row) {\n            if (entries && k<maxentries) entries[k]=col;\n            k++;\n        }\n    }\n\n    *nentries=k;\n    return true;\n}\n\n/** Sets a matrix element (i,j) to be a specified value\n * @returns true if the element exists in the given sparsity structure, false otherwise. */\nbool sparseccs_set(sparseccs *ccs, int i, int j, double val) {\n    int k;\n    for (k=ccs->cptr[j]; k<ccs->cptr[j+1]; k++) {\n        if (ccs->rix[k]==i) {\n            if (ccs->values) ccs->values[k]=val;\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Retrieves a matrix element (i,j) from a sparseccs structure\n * @returns true on success. */\nbool sparseccs_get(sparseccs *ccs, int i, int j, double *val) {\n    int k;\n    for (k=ccs->cptr[j]; k<ccs->cptr[j+1]; k++) {\n        if (ccs->rix[k]==i) {\n            if (val) *val=(ccs->values ? ccs->values[k] : 1.0);\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Helper function to compare unsigned integers */\nstatic int sparseccs_compareuint(const void * a, const void * b) {\n    long int i=*(int*)a, j=*(int*)b;\n    return (int) (i-j);\n}\n\n/** Converts a DOK matrix to a CCS matrix */\nbool sparseccs_doktoccs(sparsedok *in, sparseccs *out, bool copyvals) {\n    int nentries=in->dict.count;\n\n    sparseccs_init(out);\n    if (!sparseccs_resize(out, in->nrows, in->ncols, nentries, copyvals)) return false;\n\n    /* Clear the column pointer array */\n    for (int i=0; i<in->ncols+1; i++) out->cptr[i]=0;\n\n    /* Count number of entries per column */\n    for (unsigned int i=0; i<in->dict.capacity; i++) {\n        value key=in->dict.contents[i].key;\n        if (MORPHO_ISDOKKEY(key)) out->cptr[MORPHO_GETDOKCOLWVAL(key)]++;\n    }\n\n    /* Construct the column pointer array */\n    unsigned int ptr=0;\n    for (int i=0; i<in->ncols+1; i++) {\n        int p=ptr;\n        ptr+=out->cptr[i];\n        out->cptr[i]=p;\n    }\n\n    /* Clear the row index array */\n    for (int i=0; i<nentries; i++) out->rix[i]=-1;\n\n    /* Copy entries into appropriate rowindex */\n    for (unsigned int i=0; i<in->dict.capacity; i++) {\n        value key=in->dict.contents[i].key;\n        if (MORPHO_ISDOKKEY(key)) {\n            int k=out->cptr[MORPHO_GETDOKCOLWVAL(key)];\n            while (out->rix[k]!=-1) k++;\n            out->rix[k]=MORPHO_GETDOKROWWVAL(key);\n        }\n    }\n\n    /* Sort columns */\n    for (int i=0; i<in->ncols; i++) {\n        int len=out->cptr[i+1]-out->cptr[i];\n        if (len>1) {\n            qsort(out->rix+out->cptr[i], len, sizeof(int), sparseccs_compareuint);\n        }\n    }\n\n    /* Copy over values */\n    if (copyvals) {\n        for (int i=0; i<nentries; i++) out->values[i]=0.0;\n\n        for (int j=0; j<in->ncols; j++) {\n            int len=out->cptr[j+1]-out->cptr[j];\n            for (int i=0; i<len; i++) {\n                value val;\n                if (sparsedok_get(in, out->rix[out->cptr[j]+i], j, &val)) {\n                    if (MORPHO_ISFLOAT(val)) out->values[out->cptr[j]+i]=MORPHO_GETFLOATVALUE(val);\n                    else if (MORPHO_ISINTEGER(val)) out->values[out->cptr[j]+i]=MORPHO_GETINTEGERVALUE(val);\n                }\n            }\n        }\n    }\n\n    return true;\n}\n\n/** Prints a sparsedok matrix */\nvoid sparseccs_print(vm *v, sparseccs *ccs) {\n    double val;\n    for (int i=0; i<ccs->nrows; i++) {\n        morpho_printf(v, \"[ \");\n        for (int j=0; j<ccs->ncols; j++) {\n            if (sparseccs_get(ccs, i, j, &val)) morpho_printf(v, \"%g \", val);\n            else morpho_printf(v, \"0 \");\n        }\n        morpho_printf(v, \"]%s\", (i<ccs->nrows-1 ? \"\\n\" : \"\"));\n    }\n}\n\n/** Number of entries in a sparseccs */\nunsigned int sparseccs_count(sparseccs *ccs) {\n    return ccs->nentries;\n}\n\n/** Copies one sparseccs matrix to another, reallocating as necessary */\nbool sparseccs_copy(sparseccs *src, sparseccs *dest) {\n    bool success=false;\n    if (sparseccs_resize(dest, src->nrows, src->ncols, src->nentries, src->values)) {\n        memcpy(dest->cptr, src->cptr, sizeof(int)*(src->ncols+1));\n        memcpy(dest->rix, src->rix, sizeof(int)*(src->nentries));\n        if (src->values) memcpy(dest->values, src->values, sizeof(double)*src->nentries);\n        success=true;\n    }\n    return success;\n}\n\n/** Copies a sparseccs matrix into a dok matrix at offset i0, j0 */\nbool sparseccs_copytodok(sparseccs *src, sparsedok *dest, int row0, int col0) {\n\n    for (int i=0, k=0; i<src->ncols; i++) { // Loop over columns\n        int nentries, *entries;\n        if (!sparseccs_getrowindices(src, i, &nentries, &entries)) return false;\n\n        for (int j=0; j<nentries; j++) {\n            if (!sparsedok_insert(dest, row0+entries[j], col0+i, MORPHO_FLOAT(src->values[k]))) return false;\n\n            k++;\n        }\n    }\n\n    return true;\n}\n\n/** Copies a sparseccs matrix into a dense matrix at offset i0, j0 */\nbool sparseccs_copytomatrix(sparseccs *src, objectmatrix *dest, int row0, int col0) {\n\n    for (int i=0, k=0; i<src->ncols; i++) { // Loop over columns\n        int nentries, *entries;\n        if (!sparseccs_getrowindices(src, i, &nentries, &entries)) return false;\n\n        for (int j=0; j<nentries; j++) {\n            if (!matrix_setelement(dest, entries[j]+row0, i+col0, src->values[k])) return false;\n            k++;\n        }\n    }\n\n    return true;\n}\n\n/* ***************************************\n * Object sparse interface\n * *************************************** */\n\n/** Checks whether a format is available.\n * @param sparse  the matrix to check\n * @param format format to check\n * @param force if format is unavailable, try to make it available\n * @param copyvals copy values across\n * @returns true if the format is available */\nbool sparse_checkformat(objectsparse *sparse, objectsparseformat format, bool force, bool copyvals) {\n    bool available=false;\n    switch (format) {\n        case SPARSE_DOK:\n            available=(sparse->dok.ncols>0 && sparse->dok.nrows>0)||(sparse->dok.dict.count>0);\n            break;\n        case SPARSE_CCS:\n            if (force && !sparse->ccs.cptr) {\n                available=sparseccs_doktoccs(&sparse->dok, &sparse->ccs, copyvals);\n            } else available=(sparse->ccs.cptr);\n    }\n    return available;\n}\n\n/** Removes data structures for a given format */\nvoid sparse_removeformat(objectsparse *s,  objectsparseformat format) {\n    if (format==SPARSE_DOK) {\n        sparsedok_clear(&s->dok);\n    } else {\n        sparseccs_clear(&s->ccs);\n    }\n}\n\n/* ***************************************\n * objectsparse definition\n * *************************************** */\n\nobjecttype objectsparsetype;\n\n/** Sparse object definitions */\nvoid objectsparse_printfn(object *obj, void *v) {\n    morpho_printf(v, \"<Sparse>\");\n}\n\nvoid objectsparse_markfn(object *obj, void *v) {\n    objectsparse *c = (objectsparse *) obj;\n    morpho_markdictionary(v, &c->dok.dict);}\n\nvoid objectsparse_freefn(object *obj) {\n    objectsparse *s = (objectsparse *) obj;\n    sparse_clear(s);\n}\n\nsize_t objectsparse_sizefn(object *obj) {\n    return sparse_size((objectsparse *) obj);\n}\n\nobjecttypedefn objectsparsedefn = {\n    .printfn=objectsparse_printfn,\n    .markfn=objectsparse_markfn,\n    .freefn=objectsparse_freefn,\n    .sizefn=objectsparse_sizefn,\n    .hashfn=NULL,\n    .cmpfn=NULL\n};\n\n/* ***************************************\n * objectsparse objects\n * *************************************** */\n\n/** Creates a sparse matrix object\n * @param[in] nrows } Optional number of rows and columns\n * @param[in] ncols } */\nobjectsparse *object_newsparse(int *nrows, int *ncols) {\n    objectsparse *new = (objectsparse *) object_new(sizeof(objectsparse), OBJECT_SPARSE);\n\n    if (new) {\n        sparsedok_init(&new->dok);\n        sparseccs_init(&new->ccs);\n        if (nrows) sparsedok_setdimensions(&new->dok, *nrows, *ncols);\n    }\n\n    return new;\n}\n\n/* *******************************\n * Concatenate matrices\n * ******************************* */\n\n/** Checks if the contents of dim match check; if *dim hasn't been set it is updated to match check */\nbool sparse_checkupdatedimension(int check, int *dim) {\n    if (*dim<0) *dim=check;\n    if (*dim!=check) return false;\n    return true;\n}\n\n/** Checks the dimensions of a matrix of matrices to be concatenated */\nobjectsparseerror sparse_catcheckdimensions(objectlist *in, int ndim, unsigned int *dim, int *ncols, int *nrows) {\n    for (unsigned int i=0; i<dim[0]; i++) nrows[i]=-1;\n    for (unsigned int i=0; i<dim[1]; i++) ncols[i]=-1;\n\n    for (unsigned int i=0; i<dim[0]; i++) { // Loop over rows\n        for (unsigned int j=0; j<dim[1]; j++) { // Loop over cols\n            unsigned int indx[2] = {i,j};\n            value val;\n            if (matrix_getlistelement(in, ndim, indx, &val)) {\n                if (MORPHO_ISSPARSE(val)) {\n                    objectsparse *sparse = MORPHO_GETSPARSE(val);\n                    int nr, nc;\n                    sparse_getdimensions(sparse, &nr, &nc);\n                    if (!(sparse_checkupdatedimension(nr, &nrows[i]) &&\n                          sparse_checkupdatedimension(nc, &ncols[j]))) return SPARSE_INCMPTBLDIM;\n                } else if (MORPHO_ISMATRIX(val)) {\n                    objectmatrix *matrix = MORPHO_GETMATRIX(val);\n                    if (!(sparse_checkupdatedimension(matrix->nrows, &nrows[i]) &&\n                          sparse_checkupdatedimension(matrix->ncols, &ncols[j]))) return SPARSE_INCMPTBLDIM;\n                } else if (!MORPHO_ISINTEGER(val)) {\n                    return SPARSE_INVLDINIT;\n                }\n            }\n        }\n    }\n\n    return SPARSE_OK;\n}\n\ntypedef bool (*sparse_catcopyfn) (void *out, value val, int irow, int icol);\n\n/* Copy sparse matrix entries across */\nbool sparse_catcopysparsetosparseat(objectsparse *src, int row0, int col0, objectsparse *dest) {\n    if (sparse_checkformat(src, SPARSE_CCS, false, false)) {\n        return sparseccs_copytodok(&src->ccs, &dest->dok, row0, col0);\n    } else {\n        return sparsedok_copyat(&src->dok, &dest->dok, row0, col0);\n    }\n    return false;\n}\n\n/* Copy sparse matrix entries across */\nbool sparse_catcopysparsetomatrixat(objectsparse *src, int row0, int col0, objectmatrix *dest) {\n    if (sparse_checkformat(src, SPARSE_CCS, false, false)) {\n        return sparseccs_copytomatrix(&src->ccs, dest, row0, col0);\n    } else {\n        return sparsedok_copytomatrix(&src->dok, dest, row0, col0);\n    }\n    return false;\n}\n\n/* Copies a single entry in the cat matrix */\nbool sparse_catcopyentry(void *out, value val, int irow, int icol) {\n    objectsparse *dest = out;\n\n    if (MORPHO_ISSPARSE(val)) {\n        objectsparse *sparse = MORPHO_GETSPARSE(val);\n        sparse_catcopysparsetosparseat(sparse, irow, icol, dest);\n    } else if (MORPHO_ISMATRIX(val)) {\n        objectmatrix *matrix = MORPHO_GETMATRIX(val);\n        sparsedok_copymatrixat(matrix, &dest->dok, irow, icol);\n    } else if (MORPHO_ISINTEGER(val)) {\n\n    }\n    return true;\n}\n\n/* Copies a single entry in the cat matrix */\nbool matrix_catcopyentry(void *out, value val, int irow, int icol) {\n    objectmatrix *dest = out;\n\n    if (MORPHO_ISSPARSE(val)) {\n        objectsparse *sparse = MORPHO_GETSPARSE(val);\n        if (sparse_catcopysparsetomatrixat(sparse, irow, icol, dest)!=SPARSE_OK) return false;\n    } else if (MORPHO_ISMATRIX(val)) {\n        objectmatrix *matrix = MORPHO_GETMATRIX(val);\n        if (matrix_copyat(matrix, dest, irow, icol)!=MATRIX_OK) return false;\n    } else if (MORPHO_ISINTEGER(val)) {\n\n    }\n    return true;\n}\n\n/** Sparse matrix concatenation\n    Call with dest=NULL to get size information in outrows and outcols */\nobjectsparseerror sparse_docat(objectlist *in, void *dest, sparse_catcopyfn copyfn, int *outrows, int *outcols) {\n    unsigned int dim[2] = {0,0}, ndim;\n\n    if (!matrix_getlistdimensions(in, dim, 2, &ndim) ||\n        ndim!=2) return SPARSE_INVLDINIT;\n\n    /* Keep track of rows and columns of the matrix */\n    int nrows[dim[0]], ncols[dim[1]];\n\n    objectsparseerror err = sparse_catcheckdimensions(in, ndim, dim, ncols, nrows);\n    if (err!=SPARSE_OK) return err;\n    \n    if (outrows) {\n        *outrows=0;\n        for (int i=0; i<dim[0]; i++) *outrows+=nrows[i];\n    }\n        \n    if (outcols) {\n        *outcols=0;\n        for (int i=0; i<dim[1]; i++) *outcols+=ncols[i];\n    }\n    \n    if (!dest) return SPARSE_OK;\n\n    int irow=0;\n\n    /* Now copy elements across */\n    for (unsigned int i=0; i<dim[0]; i++) { // Loop over rows\n        int icol=0;\n        for (unsigned int j=0; j<dim[1]; j++) { // Loop over columns\n            unsigned int indx[2] = {i,j};\n            value val;\n            if (matrix_getlistelement(in, ndim, indx, &val)) {\n                (*copyfn) (dest, val, irow, icol);\n            }\n            if (ncols[j]>0) icol+=ncols[j];\n        }\n        irow+=nrows[i];\n    }\n\n    return SPARSE_OK;\n}\n\n/** Veneer onto sparse_docat for sparse matrices */\nobjectsparseerror sparse_cat(objectlist *in, objectsparse *dest) {\n    return sparse_docat(in, dest, sparse_catcopyentry, &dest->dok.nrows, &dest->dok.ncols);\n}\n\n/** Veneer onto sparse_docat for dense matrices. Allocates a dense matrix of the correct size */\nobjectsparseerror sparse_catmatrix(objectlist *in, objectmatrix **out) {\n    int nrows, ncols;\n    objectmatrix *new = NULL;\n    objectsparseerror err=sparse_docat(in, NULL, matrix_catcopyentry, &nrows, &ncols);\n    \n    if (err!=SPARSE_OK) goto sparse_catmatrix_error;\n    new = object_newmatrix(nrows, ncols, true);\n    \n    err=sparse_docat(in, new, matrix_catcopyentry, NULL, NULL);\n    if (err==SPARSE_OK) *out = new;\n    \n    return err;\n    \nsparse_catmatrix_error:\n    if (new) object_free((object *) new);\n    return err;\n}\n\n/* *******************************\n * Construct sparse matrices\n * ******************************* */\n\n/** Create a sparse array from an array */\nobjectsparse *object_sparsefromarray(objectarray *array) {\n    unsigned int dim[2] = {0,0}, ndim;\n\n    if (!matrix_getarraydimensions(array, dim, 2, &ndim)) return NULL;\n\n    objectsparse *new=object_newsparse(NULL, NULL);\n\n    for (unsigned int i=0; i<dim[0]; i++) {\n        value v[3]={MORPHO_NIL, MORPHO_NIL, MORPHO_NIL};\n        for (unsigned int k=0; k<dim[1] && k<3; k++) {\n            unsigned int indx[2] = {i, k};\n            v[k]=matrix_getarrayelement(array, 2, indx);\n        }\n        if (MORPHO_ISINTEGER(v[0]) && MORPHO_ISINTEGER(v[1])) {\n            sparsedok_insert(&new->dok, MORPHO_GETINTEGERVALUE(v[0]), MORPHO_GETINTEGERVALUE(v[1]), v[2]);\n        } else {\n            sparse_clear(new);\n            MORPHO_FREE(new);\n            return false;\n        }\n    }\n\n    return new;\n}\n\n/** Create a sparse array from a list */\nobjectsparseerror object_sparsefromlist(objectlist *list, objectsparse **out) {\n    unsigned int dim[2] = {0,0}, ndim;\n    objectsparseerror err=SPARSE_OK;\n\n    if (!matrix_getlistdimensions(list, dim, 2, &ndim)) return SPARSE_INVLDINIT;\n\n    objectsparse *new=object_newsparse(NULL, NULL);\n\n    if (dim[0]>0 && dim[1]!=3) { // If this isn't a list of entries, it may be a concatenation operation\n        err=sparse_cat(list, new);\n        if (err==SPARSE_OK) goto object_sparsefromlist_succeeded;\n        goto object_sparsefromlist_cleanup;\n    }\n\n    for (unsigned int i=0; i<dim[0]; i++) {\n        value v[3]={MORPHO_NIL, MORPHO_NIL, MORPHO_NIL};\n        for (unsigned int k=0; k<dim[1] && k<3; k++) {\n            unsigned int indx[2] = {i, k};\n            matrix_getlistelement(list, 2, indx, &v[k]);\n        }\n        if (MORPHO_ISINTEGER(v[0]) && MORPHO_ISINTEGER(v[1])) {\n            sparsedok_insert(&new->dok, MORPHO_GETINTEGERVALUE(v[0]), MORPHO_GETINTEGERVALUE(v[1]), v[2]);\n        } else {\n            sparse_clear(new);\n            err=sparse_cat(list, new);\n            if (err==SPARSE_OK) goto object_sparsefromlist_succeeded;\n            goto object_sparsefromlist_cleanup;\n        }\n    }\n\nobject_sparsefromlist_succeeded:\n    *out = new;\n    return err;\n\nobject_sparsefromlist_cleanup:\n    if (new) {\n        sparse_clear(new);\n        MORPHO_FREE(new);\n    }\n\n    return err;\n}\n\n/** Convert a sparse matrix to a dense matrix */\nobjectsparseerror sparse_tomatrix(objectsparse *in, objectmatrix **out) {\n    objectsparseerror err = SPARSE_FAILED;\n    objectmatrix *new = NULL;\n\n    if (sparse_checkformat(in, SPARSE_CCS, false, false)) {\n        new=object_newmatrix(in->ccs.nrows, in->ccs.ncols, true);\n        if (!new) return SPARSE_FAILED;\n        if (sparseccs_copytomatrix(&in->ccs, new, 0, 0)) err=SPARSE_OK;\n    } else if (sparse_checkformat(in, SPARSE_DOK, false, false)) {\n        new=object_newmatrix(in->dok.nrows, in->dok.ncols, true);\n        if (!new) return SPARSE_FAILED;\n        if (sparsedok_copytomatrix(&in->dok, new, 0, 0)) err=SPARSE_OK;\n    }\n\n    // Clean up and return\n    if (err==SPARSE_OK) {\n        *out=new;\n    } else if (new) object_free((object *) new);\n\n    return err;\n}\n\n/** Clones a sparse matrix */\nobjectsparse *sparse_clone(objectsparse *s) {\n    objectsparse *new = object_newsparse(NULL, NULL);\n\n    if (new) {\n        sparsedok_copy(&s->dok, &new->dok);\n        sparseccs_copy(&s->ccs, &new->ccs);\n    }\n\n    return new;\n}\n\n/** Gets the dimension sof a sparse matrix */\nvoid sparse_getdimensions(objectsparse *s, int *nrows, int *ncols) {\n    if (s->ccs.ncols>0) {\n        if (nrows) *nrows=s->ccs.nrows;\n        if (ncols) *ncols=s->ccs.ncols;\n    } else {\n        if (nrows) *nrows=s->dok.nrows;\n        if (ncols) *ncols=s->dok.ncols;\n    }\n}\n\n/** Set an element */\nbool sparse_setelement(objectsparse *s, int row, int col, value val) {\n    if (sparse_checkformat(s, SPARSE_CCS, false, false)) {\n        if (!sparseccs_copytodok(&s->ccs, &s->dok, 0, 0)) return false;\n        sparse_removeformat(s, SPARSE_CCS);\n    }\n\n    if (sparsedok_insert(&s->dok, row, col, val)) return true;\n    return false;\n}\n\n/** Get an element\n * @param[in] s the sparse object\n * @param[in] row the row\n * @param[in] col the column\n * @param[out] val the value; pass NULL to check if an element exists */\nbool sparse_getelement(objectsparse *s, int row, int col, value *val) {\n    if (sparse_checkformat(s, SPARSE_DOK, false, false)) {\n        return sparsedok_get(&s->dok, row, col, val);\n    } else if (sparse_checkformat(s, SPARSE_CCS, false, false)) {\n        double v;\n        if (sparseccs_get(&s->ccs, row, col, &v)) {\n            if (val) *val = MORPHO_FLOAT(v);\n        }\n    }\n    return false;\n}\n\n/** Enumerate values in a sparse matrix */\nbool sparse_enumerate(objectsparse *s, int i, value *out) {\n    if (sparse_checkformat(s, SPARSE_CCS, false, false)) {\n        if (i<0) { *out=MORPHO_INTEGER(s->ccs.nentries); return true; }\n        if (i<s->ccs.nentries) { *out=MORPHO_FLOAT(s->ccs.values[i]); return true; }\n    } else if (sparse_checkformat(s, SPARSE_DOK, false, false)) {\n        if (i<0) { *out=MORPHO_INTEGER(s->dok.dict.count); return true; }\n        if (i<s->dok.dict.count) {\n            objectdokkey *key = s->dok.keys;\n            for (int k=0; k<i; k++) if (key) key=(objectdokkey *) key->obj.next;\n\n            if (key) return dictionary_get(&s->dok.dict, MORPHO_OBJECT(key), out);\n        }\n    }\n\n    return false;\n}\n\n/** Add two matrices\n * @param[in] a - sparse matrix\n * @param[in] b - sparse matrix\n * @param[in] alpha - scale for a\n * @param[in] beta - scale for b\n * @param[out] out - alpha*a+beta*b. */\nobjectsparseerror sparse_add(objectsparse *a, objectsparse *b, double alpha, double beta, objectsparse *out) {\n    if (!(sparse_checkformat(a, SPARSE_CCS, true, true) &&\n          sparse_checkformat(b, SPARSE_CCS, true, true)) ) return SPARSE_CONVFAILED;\n\n    if (a->ccs.ncols!=b->ccs.ncols || a->ccs.nrows != b->ccs.nrows) return SPARSE_INCMPTBLDIM;\n    sparsedok_clear(&out->dok);\n    sparseccs_clear(&out->ccs);\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    cs A, B;\n    sparse_ccstocsparse(&a->ccs, &A);\n    sparse_ccstocsparse(&b->ccs, &B);\n    cs *ret=cs_add(&A, &B, alpha, beta);\n    if (ret) {\n        sparse_csparsetoccs(ret, &out->ccs);\n        cs_free(ret);\n        return SPARSE_OK;\n    }\n#endif\n    return SPARSE_FAILED;\n}\n\n/** Multiply two matrices\n * @param[in] a - sparse matrix\n * @param[in] b - sparse matrix\n * @param[out] out - a*b. */\nobjectsparseerror sparse_mul(objectsparse *a, objectsparse *b, objectsparse *out) {\n    if (!(sparse_checkformat(a, SPARSE_CCS, true, true) &&\n          sparse_checkformat(b, SPARSE_CCS, true, true)) ) return SPARSE_CONVFAILED;\n    if (a->ccs.ncols!=b->ccs.nrows) return SPARSE_INCMPTBLDIM;\n    sparsedok_clear(&out->dok);\n    sparseccs_clear(&out->ccs);\n\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    cs A, B;\n    sparse_ccstocsparse(&a->ccs, &A);\n    sparse_ccstocsparse(&b->ccs, &B);\n    cs *ret=cs_multiply(&A, &B);\n    if (ret) {\n        sparse_csparsetoccs(ret, &out->ccs);\n        cs_free(ret);\n        return SPARSE_OK;\n    }\n#endif\n    return SPARSE_FAILED;\n}\n\n/** Multiply a sparse matrix a by a dense matrix b: out -> out + a*b\n * @param[in] a - sparse matrix\n * @param[in] b - dense matrix\n * @param[out] out - out + a*b. */\nobjectsparseerror sparse_mulsxd(objectsparse *a, objectmatrix *b, objectmatrix *out) {\n    if (a->ccs.ncols!=b->nrows) return SPARSE_INCMPTBLDIM;\n\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    cs A;\n    sparse_ccstocsparse(&a->ccs, &A);\n\n    for (int i=0; i<b->ncols; i++) {\n        cs_gaxpy(&A, b->elements+i*b->nrows, out->elements+i*b->nrows);\n    }\n    return SPARSE_OK;\n\n#endif\n    return SPARSE_FAILED;\n}\n\n/** Multiply a dense matrix a by a sparse matrix b: out -> out + a*b\n * @param[in] a - dense matrix\n * @param[in] b - sparse matrix\n * @param[out] out - out + a*b. */\nobjectsparseerror sparse_muldxs(objectmatrix *a, objectsparse *b, objectmatrix *out) {\n    if (!(sparse_checkformat(b, SPARSE_CCS, true, true))) return SPARSE_CONVFAILED;\n\n    if (a->ncols!=b->ccs.nrows) return SPARSE_INCMPTBLDIM;\n\n    for (unsigned int row=0; row<a->nrows; row++) {\n        for (unsigned int col=0; col<b->ccs.nrows; col++) {\n            double val, *svalues;\n            int nentries, *entries;\n            matrix_getelement(out, row, col, &val);\n\n            sparseccs_getrowindiceswithvalues(&b->ccs, col, &nentries, &entries, &svalues);\n            for (int i=0; i<nentries; i++) {\n                double ai;\n                matrix_getelement(a, row, entries[i], &ai);\n                val+=ai*svalues[i];\n            }\n\n            matrix_setelement(out, row, col, val);\n        }\n    }\n\n    return SPARSE_OK;\n}\n\n/** Scale a sparse matrix by a scalar\n * @param[in] src - sparse matrix\n * @param[in] scale - scale\n * @param[out] out - a*b. */\nobjectsparseerror sparse_scale(objectsparse *src, double scale, objectsparse *out) {\n    if (!(sparse_checkformat(src, SPARSE_CCS, true, true))) return SPARSE_CONVFAILED;\n    sparsedok_clear(&out->dok);\n    sparseccs_clear(&out->ccs);\n\n    if (!sparseccs_copy(&src->ccs, &out->ccs)) return SPARSE_FAILED;\n    cblas_dscal(out->ccs.nentries, scale, out->ccs.values, 1);\n\n    return SPARSE_OK;\n}\n\n\n/** Solve a linear system a.x = b\n * @param[in] a - sparse matrix\n * @param[in] b - dense rhs (may have more than one column)\n * @param[out] out - Solution to a.x = b. */\nobjectsparseerror sparse_div(objectsparse *a, objectmatrix *b, objectmatrix *out) {\n    if (!(sparse_checkformat(a, SPARSE_CCS, true, true))) return SPARSE_CONVFAILED;\n    if (a->ccs.ncols!=b->nrows || b->nrows!=out->nrows || b->ncols!=out->ncols) return SPARSE_INCMPTBLDIM;\n\n    if (b!=out) cblas_dcopy(b->ncols * b->nrows, b->elements, 1, out->elements, 1);\n\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    cs A;\n    sparse_ccstocsparse(&a->ccs, &A);\n    int ret=false;\n    if (a->ccs.ncols==a->ccs.nrows) {\n        ret=cs_lusol(0, &A, out->elements, MORPHO_EPS);\n    } else {\n        ret=cs_qrsol(0, &A, out->elements);\n    }\n\n    if (ret) return SPARSE_OK;\n#endif\n\n    return SPARSE_FAILED;\n}\n\n/** Transpose a sparse matrix\n * @param[in] a - sparse matrix\n * @param[out] out - transpose(A). */\nobjectsparseerror sparse_transpose(objectsparse *a, objectsparse *out) {\n    if (!(sparse_checkformat(a, SPARSE_CCS, true, true)) ) return SPARSE_CONVFAILED;\n    sparsedok_clear(&out->dok);\n    sparseccs_clear(&out->ccs);\n\n#ifdef MORPHO_LINALG_USE_CSPARSE\n    cs A;\n    sparse_ccstocsparse(&a->ccs, &A);\n    cs *ret=cs_transpose(&A, true);\n    if (ret) {\n        sparse_csparsetoccs(ret, &out->ccs);\n        cs_free(ret);\n        return SPARSE_OK;\n    }\n#endif\n    return SPARSE_FAILED;\n}\n\n/** Clears any data attached to a sparse matrix */\nvoid sparse_clear(objectsparse *a) {\n    sparsedok_clear(&a->dok);\n    sparseccs_clear(&a->ccs);\n}\n\n/** Calculate the size of a sparse matrix structure */\nsize_t sparse_size(objectsparse *a) {\n    return sizeof(objectsparse)+\n           a->dok.dict.capacity*sizeof(dictionaryentry) +\n           sizeof(int)*(a->ccs.ncols+1) +\n           sizeof(int)*(a->ccs.nentries) +\n           ( a->ccs.values ? sizeof(double)*(a->ccs.nentries) : 0);\n}\n\n/* ***************************************\n * Sparse builtin class\n * *************************************** */\n\nvoid sparse_raiseerror(vm *v, objectsparseerror err) {\n    switch(err) {\n        case SPARSE_OK: break;\n        case SPARSE_INCMPTBLDIM: morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); break;\n        case SPARSE_CONVFAILED: morpho_runtimeerror(v, SPARSE_CONVFAILEDERR); break;\n        case SPARSE_FAILED: morpho_runtimeerror(v, SPARSE_OPFAILEDERR); break;\n        case SPARSE_INVLDINIT: morpho_runtimeerror(v, SPARSE_INVLDARRAYINIT); break;\n    }\n}\n\n/** Constructs a Sparse object */\nvalue sparse_constructor(vm *v, int nargs, value *args) {\n    int nrows, ncols;\n    objectsparse *new=NULL;\n    value out=MORPHO_NIL;\n\n    if ( nargs==2 &&\n         MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n         MORPHO_ISINTEGER(MORPHO_GETARG(args, 1)) ) {\n        nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1));\n        new=object_newsparse(&nrows, &ncols);\n    } else if (nargs==1 &&\n               MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        ncols = 1;\n        new=object_newsparse(&nrows, &ncols);\n    } else if (nargs==1 &&\n               MORPHO_ISARRAY(MORPHO_GETARG(args, 0))) {\n        new=object_sparsefromarray(MORPHO_GETARRAY(MORPHO_GETARG(args, 0)));\n       if (!new) morpho_runtimeerror(v, SPARSE_INVLDARRAYINIT);\n    } else if (nargs==1 &&\n               MORPHO_ISLIST(MORPHO_GETARG(args, 0))) {\n        objectsparseerror err = object_sparsefromlist(MORPHO_GETLIST(MORPHO_GETARG(args, 0)), &new);\n\n        if (!new) sparse_raiseerror(v, err);\n    } else if (nargs==0) {\n        new = object_newsparse(NULL, NULL);\n    } else {\n        morpho_runtimeerror(v, SPARSE_CONSTRUCTOR);\n    }\n\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\n/** Retrieve a matrix element */\nvalue Sparse_getindex(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    unsigned int indx[2]={0,0};\n    value out = MORPHO_FLOAT(0.0);\n\n    if (array_valuelisttoindices(nargs, args+1, indx)) {\n        sparse_getelement(s, indx[0], indx[1], &out);\n    } else morpho_runtimeerror(v, MATRIX_INVLDINDICES);\n\n    return out;\n}\n\n/** Set a matrix element */\nvalue Sparse_setindex(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    unsigned int indx[2]={0,0};\n\n    if (array_valuelisttoindices(nargs-1, args+1, indx)) {\n        size_t osize = sparse_size(s);\n        if (!sparse_setelement(s, indx[0], indx[1], args[nargs])) {\n            morpho_runtimeerror(v, SPARSE_SETFAILED);\n        }\n        size_t nsize = sparse_size(s);\n        if (osize!=nsize) {\n            morpho_resizeobject(v, (object *) s, osize, nsize);\n        }\n    } else morpho_runtimeerror(v, MATRIX_INVLDINDICES);\n\n    return MORPHO_NIL;\n}\n\n/** Enumerate protocol */\nvalue Sparse_enumerate(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1) {\n        if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n            int i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n\n            sparse_enumerate(s, i, &out);\n        }\n    }\n\n    return out;\n}\n\n/** Print a sparse matrix */\nvalue Sparse_print(vm *v, int nargs, value *args) {\n    value self = MORPHO_SELF(args);\n    if (!MORPHO_ISSPARSE(self)) return Object_print(v, nargs, args);\n    \n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n\n    if (sparse_checkformat(s, SPARSE_CCS, false, false)) {\n        sparseccs_print(v, &s->ccs);\n    } else if (sparse_checkformat(s, SPARSE_DOK, false, false)) {\n        sparsedok_print(v, &s->dok);\n    }\n\n    return MORPHO_NIL;\n}\n\n/** Add two sparse matrices */\nvalue Sparse_add(vm *v, int nargs, value *args) {\n    objectsparse *a=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) {\n        objectsparse *b=MORPHO_GETSPARSE(MORPHO_GETARG(args, 0));\n\n        objectsparse *new = object_newsparse(NULL, NULL);\n        if (new) {\n            size_t asize=sparse_size(a), bsize=sparse_size(b);\n            \n            objectsparseerror err =sparse_add(a, b, 1.0, 1.0, new);\n            \n            morpho_resizeobject(v, (object *) a, asize, sparse_size(a));\n            morpho_resizeobject(v, (object *) b, bsize, sparse_size(b));\n            \n            if (err==SPARSE_OK) {\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            } else {\n                morpho_freeobject(MORPHO_OBJECT(new));\n                sparse_raiseerror(v, err);\n            }\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    }\n\n    return out;\n}\n\n/** Subtract sparse matrices */\nvalue Sparse_sub(vm *v, int nargs, value *args) {\n    objectsparse *a=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) {\n        objectsparse *b=MORPHO_GETSPARSE(MORPHO_GETARG(args, 0));\n\n        objectsparse *new = object_newsparse(NULL, NULL);\n        if (new) {\n            size_t asize=sparse_size(a), bsize=sparse_size(b);\n            \n            objectsparseerror err =sparse_add(a, b, 1.0, -1.0, new);\n            \n            morpho_resizeobject(v, (object *) a, asize, sparse_size(a));\n            morpho_resizeobject(v, (object *) b, bsize, sparse_size(b));\n            \n            if (err==SPARSE_OK) {\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            } else {\n                sparse_raiseerror(v, err);\n            }\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    }\n\n    return out;\n}\n\n/** Multiply sparse matrices */\nvalue Sparse_mul(vm *v, int nargs, value *args) {\n    objectsparse *a=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    size_t asize = sparse_size(a);\n    objectsparse *new = NULL;\n    value out=MORPHO_NIL;\n    objectsparseerror err = SPARSE_OK;\n\n    if (nargs==1) {\n        if (MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) {\n            objectsparse *b=MORPHO_GETSPARSE(MORPHO_GETARG(args, 0));\n            size_t bsize=sparse_size(b);\n            \n            new = object_newsparse(NULL, NULL);\n            if (new) {\n                err=sparse_mul(a, b, new);\n                morpho_resizeobject(v, (object *) b, bsize, sparse_size(b)); // Check for size change\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        } else if (MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n            if (sparse_checkformat(a, SPARSE_CCS, true, true)) {\n                objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n                \n                objectmatrix *out=object_newmatrix(a->ccs.nrows, b->ncols, true);\n                new = (objectsparse *) out; // Munge type to ensure binding/deallocation\n                \n                if (out) {\n                    err=sparse_mulsxd(a, b, out);\n                } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n            } else err=SPARSE_CONVFAILED;\n        } else if (MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n            double scale;\n            if (!morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) return MORPHO_NIL;\n\n            new = object_newsparse(NULL, NULL);\n            if (new) {\n                err=sparse_scale(a, scale, new);\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        }\n    }\n\n    morpho_resizeobject(v, (object *) a, asize, sparse_size(a)); // In case we caused a size change\n    \n    if (err==SPARSE_OK && new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else {\n        sparse_raiseerror(v, err);\n        if (new) object_free((object *) new);\n    }\n\n    return out;\n}\n\n/** Multiplication on the right */\nvalue Sparse_mulr(vm *v, int nargs, value *args) {\n    objectsparse *b=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    objectsparseerror err = SPARSE_OK;\n\n    if (nargs==1) {\n        if (MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n            objectmatrix *a=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n            int ncols;\n            sparse_getdimensions(b, NULL, &ncols);\n\n            objectmatrix *new=object_newmatrix(a->nrows, ncols, true);\n\n            if (new) {\n                err=sparse_muldxs(a, b, new);\n                if (err==SPARSE_OK) {\n                    out=MORPHO_OBJECT(new);\n                    morpho_bindobjects(v, 1, &out);\n                } else {\n                    sparse_raiseerror(v, err);\n                    if (new) object_free((object *) new);\n                }\n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        } else if (MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) {\n            return Sparse_mul(v, nargs, args); // Redirect to regular multiplication\n        }\n    }\n\n    return out;\n}\n\n/** Sparse rhs not implemented */\nvalue Sparse_div(vm *v, int nargs, value *args) {\n    return MORPHO_NIL;\n}\n\n/** Solve a linear system b/ A where A is sparse */\nvalue Sparse_divr(vm *v, int nargs, value *args) {\n    objectsparse *a=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) {\n        objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0));\n\n        objectmatrix *new = object_newmatrix(b->nrows, b->ncols, false);\n        if (new) {\n            size_t asize=sparse_size(a);\n            objectsparseerror err =sparse_div(a, b, new);\n            morpho_resizeobject(v, (object *) a, asize, sparse_size(a));\n            \n            if (err==SPARSE_OK) {\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            } else {\n                sparse_raiseerror(v, err);\n            }\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    }\n\n    return out;\n}\n\n/** Multiply sparse matrices */\nvalue Sparse_transpose(vm *v, int nargs, value *args) {\n    objectsparse *a=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    objectsparse *new = object_newsparse(NULL, NULL);\n    if (new) {\n        size_t asize=sparse_size(a);\n        objectsparseerror err = sparse_transpose(a, new);\n        morpho_resizeobject(v, (object *) a, asize, sparse_size(a));\n        \n        if (err==SPARSE_OK) {\n            out=MORPHO_OBJECT(new);\n            morpho_bindobjects(v, 1, &out);\n        } else {\n            sparse_raiseerror(v, err);\n        }\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n\n    return out;\n}\n\n/** Clone a sparse matrix */\nvalue Sparse_clone(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out = MORPHO_NIL;\n    objectsparse *new=sparse_clone(s);\n\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    }\n\n    return out;\n}\n\n/** Count number of elements */\nvalue Sparse_count(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out = MORPHO_INTEGER(0);\n\n    if (sparse_checkformat(s, SPARSE_DOK, false, false)) {\n        out=MORPHO_INTEGER(sparsedok_count(&s->dok));\n    } else if (sparse_checkformat(s, SPARSE_CCS, false, false)) {\n        out=MORPHO_INTEGER(sparseccs_count(&s->ccs));\n    }\n\n    return out;\n}\n\n/** Sparse dimensions */\nvalue Sparse_dimensions(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value dim[2];\n    value out=MORPHO_NIL;\n    int nrows, ncols;\n\n    sparse_getdimensions(s, &nrows, &ncols);\n    dim[0]=MORPHO_INTEGER(nrows);\n    dim[1]=MORPHO_INTEGER(ncols);\n\n    objectlist *new=object_newlist(2, dim);\n    if (new) {\n        out=MORPHO_OBJECT(new);\n        morpho_bindobjects(v, 1, &out);\n    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n\n    return out;\n}\n\n/** Gets a column of a Sparse matrix */\nvalue Sparse_getcolumn(vm *v, int nargs, value *args) {\n    value out = MORPHO_NIL;\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    \n    if (nargs==1 &&\n        MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n        \n        if (!sparse_checkformat(s, SPARSE_CCS, true, true)) {\n            morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n            return MORPHO_NIL;\n        }\n        \n        if (col<s->ccs.ncols) {\n            int ncols=1, nentries=0, *entries=NULL;\n            double *values;\n            objectsparse *new=object_newsparse(&s->ccs.nrows, &ncols);\n            \n            if (new) {\n                sparseccs_getrowindiceswithvalues(&s->ccs, col, &nentries, &entries, &values);\n                \n                if (nentries>0) {\n                    if (sparseccs_resize(&new->ccs, s->ccs.nrows, 1, nentries, true)) {\n                        new->ccs.cptr[0]=0;\n                        new->ccs.cptr[1]=nentries;\n                        \n                        for (int i=0; i<nentries; i++) {\n                            new->ccs.rix[i]=entries[i];\n                            new->ccs.values[i]=values[i];\n                        }\n                    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n                }\n                \n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n                \n            } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n        } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n    } else morpho_runtimeerror(v, MATRIX_SETCOLARGS);\n    \n    return out;\n}\n\n/** Get the row indices given a column */\nvalue Sparse_rowindices(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==1 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) {\n        if (sparse_checkformat(s, SPARSE_CCS, true, true)) {\n            int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            int nentries=0, *entries=NULL;\n\n            if (col<s->ccs.ncols) {\n                if (sparseccs_getrowindices(&s->ccs, col, &nentries, &entries)) {\n                    objectlist *new = object_newlist(nentries, NULL);\n                    if (new) {\n                        for (int i=0; i<nentries; i++) list_append(new, MORPHO_INTEGER(entries[i]));\n\n                        out=MORPHO_OBJECT(new);\n                        morpho_bindobjects(v, 1, &out);\n                    } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n                }\n            } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n        }\n    }\n\n    return out;\n}\n\n/** Get the row indices given a column */\nvalue Sparse_setrowindices(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    if (nargs==2 && MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) &&\n        MORPHO_ISLIST(MORPHO_GETARG(args, 1))) {\n        size_t ssize=sparse_size(s);\n        if (sparse_checkformat(s, SPARSE_CCS, true, true)) {\n            morpho_resizeobject(v, (object *) s, ssize, sparse_size(s));\n            int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0));\n            objectlist *list = MORPHO_GETLIST(MORPHO_GETARG(args, 1));\n            int nentries=list_length(list);\n            int entries[nentries];\n\n            if (col<s->ccs.ncols) {\n                for (int i=0; i<nentries; i++) {\n                    value entry;\n                    if (list_getelement(list, i, &entry) &&\n                        MORPHO_ISINTEGER(entry)) {\n                        entries[i]=MORPHO_GETINTEGERVALUE(entry);\n                    } else { morpho_runtimeerror(v, MATRIX_INVLDINDICES); return MORPHO_NIL; }\n                }\n\n                if (!sparseccs_setrowindices(&s->ccs, col, nentries, entries)) {\n                    morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES);\n                }\n\n            } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS);\n        }\n    }\n\n    return out;\n}\n\n/** Get the column indices */\nvalue Sparse_colindices(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n    \n    size_t ssize=sparse_size(s);\n    if (sparse_checkformat(s, SPARSE_CCS, true, true)) {\n        morpho_resizeobject(v, (object *) s, ssize, sparse_size(s));\n        \n        int ncols=0;\n        varray_int cols;\n        varray_intinit(&cols);\n        varray_intresize(&cols, s->ccs.ncols);\n        if (sparseccs_getcolindices(&s->ccs, s->ccs.ncols, &ncols, cols.data)) {\n            objectlist *new=object_newlist(ncols, NULL);\n            if (new) {\n                for (int i=0; i<ncols; i++) new->val.data[i]=MORPHO_INTEGER(cols.data[i]);\n                new->val.count=ncols;\n                out=MORPHO_OBJECT(new);\n                morpho_bindobjects(v, 1, &out);\n            }\n        }\n\n        varray_intclear(&cols);\n    }\n\n    return out;\n}\n\n/** Get a list of indices */\nvalue Sparse_indices(vm *v, int nargs, value *args) {\n    objectsparse *s=MORPHO_GETSPARSE(MORPHO_SELF(args));\n    value out=MORPHO_NIL;\n\n    size_t ssize=sparse_size(s);\n    if (sparse_checkformat(s, SPARSE_DOK, true, true)) {\n        morpho_resizeobject(v, (object *) s, ssize, sparse_size(s));\n        objectlist *list=object_newlist(s->dok.dict.count, NULL);\n        if (list) {\n            for (objectdokkey *key=s->dok.keys; key!=NULL; key=(objectdokkey *) key->obj.next) {\n                objectlist *entry=object_newlist(2, NULL);\n                if (entry) {\n                    list_append(entry, MORPHO_INTEGER(key->row));\n                    list_append(entry, MORPHO_INTEGER(key->col));\n                    list_append(list, MORPHO_OBJECT(entry));\n                } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n            }\n            /* Temporarily append a self reference so everything is in one place to bind... */\n            list_append(list, MORPHO_OBJECT(list));\n            morpho_bindobjects(v, list->val.count, list->val.data);\n            list->val.count--; // And pop it back off\n            out = MORPHO_OBJECT(list);\n        } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED);\n    }\n\n    return out;\n}\n\nMORPHO_BEGINCLASS(Sparse)\nMORPHO_METHOD(MORPHO_GETINDEX_METHOD, Sparse_getindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SETINDEX_METHOD, Sparse_setindex, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Sparse_enumerate, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_PRINT_METHOD, Sparse_print, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_ADD_METHOD, Sparse_add, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_SUB_METHOD, Sparse_sub, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MUL_METHOD, Sparse_mul, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_MULR_METHOD, Sparse_mulr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_DIVR_METHOD, Sparse_divr, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_TRANSPOSE_METHOD, Sparse_transpose, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_COUNT_METHOD, Sparse_count, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_DIMENSIONS_METHOD, Sparse_dimensions, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SPARSE_ROWINDICES_METHOD, Sparse_rowindices, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SPARSE_SETROWINDICES_METHOD, Sparse_setrowindices, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MATRIX_GETCOLUMN_METHOD, Sparse_getcolumn, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SPARSE_COLINDICES_METHOD, Sparse_colindices, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(MORPHO_CLONE_METHOD, Sparse_clone, BUILTIN_FLAGSEMPTY),\nMORPHO_METHOD(SPARSE_INDICES_METHOD, Sparse_indices, BUILTIN_FLAGSEMPTY)\nMORPHO_ENDCLASS\n\n/* ***************************************\n * Initialization\n * *************************************** */\n\nvoid sparse_initialize(void) {\n    objectdokkeytype=object_addtype(&objectdokkeydefn);\n    objectsparsetype=object_addtype(&objectsparsedefn);\n\n    builtin_addfunction(SPARSE_CLASSNAME, sparse_constructor, MORPHO_FN_CONSTRUCTOR);\n\n    objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME);\n    value objclass = builtin_findclass(MORPHO_OBJECT(&objname));\n    \n    value sparseclass=builtin_addclass(SPARSE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Sparse), objclass);\n    object_setveneerclass(OBJECT_SPARSE, sparseclass);\n\n    morpho_defineerror(SPARSE_CONSTRUCTOR, ERROR_HALT, SPARSE_CONSTRUCTOR_MSG);\n    morpho_defineerror(SPARSE_SETFAILED, ERROR_HALT, SPARSE_SETFAILED_MSG);\n    morpho_defineerror(SPARSE_INVLDARRAYINIT, ERROR_HALT, SPARSE_INVLDARRAYINIT_MSG);\n    morpho_defineerror(SPARSE_CONVFAILEDERR, ERROR_HALT, SPARSE_CONVFAILEDERR_MSG);\n    morpho_defineerror(SPARSE_OPFAILEDERR, ERROR_HALT, SPARSE_OPFAILEDERR_MSG);\n\n    //sparse_test();\n}\n\n#endif\n"
  },
  {
    "path": "src/linalg/sparse.h",
    "content": "/** @file sparse.h\n *  @author T J Atherton\n *\n *  @brief Veneer class over the objectsparse type that provides sparse matrices\n */\n\n#ifndef sparse_h\n#define sparse_h\n\n#include \"build.h\"\n#ifdef MORPHO_INCLUDE_SPARSE\n\n#include <stdio.h>\n#include \"object.h\"\n#include \"morpho.h\"\n#include \"matrix.h\"\n\n/* -------------------------------------------------------\n * Sparse objects\n * ------------------------------------------------------- */\n\nextern objecttype objectdokkeytype;\n#define OBJECT_DOKKEY objectdokkeytype\n\n/** The dictionary of keys format uses this special object type to store indices, enabling use of the existing dictionary type.\n    @warning These are for internal use only and should never be  returned to user code */\ntypedef struct {\n    object obj;\n    unsigned int row;\n    unsigned int col;\n} objectdokkey;\n\n/** Create */\n#define MORPHO_STATICDOKKEY(i,j)      { .obj.type=OBJECT_DOKKEY, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .row=i, .col=j }\n\n/** Tests whether an object is a dok key */\n#define MORPHO_ISDOKKEY(val) object_istype(val, OBJECT_DOKKEY)\n\n/** Gets the object as a dok key */\n#define MORPHO_GETDOKKEY(val)   ((objectdokkey *) MORPHO_GETOBJECT(val))\n\n/** Gets the row and column from a objectdokkey */\n#define MORPHO_GETDOKKEYROW(objptr)    ((unsigned int) (objptr)->row)\n#define MORPHO_GETDOKKEYCOL(objptr)    ((unsigned int) (objptr)->col)\n\n#define MORPHO_GETDOKROWWVAL(val)    ((unsigned int) (MORPHO_GETDOKKEY(val)->row))\n#define MORPHO_GETDOKCOLWVAL(val)    ((unsigned int) (MORPHO_GETDOKKEY(val)->col))\n\nDECLARE_VARRAY(dokkey, objectdokkey);\n\ntypedef struct {\n    int nrows;\n    int ncols;\n    dictionary dict;\n    objectdokkey *keys;\n} sparsedok;\n\ntypedef struct {\n    int nentries;\n    int nrows;\n    int ncols;\n    int *cptr; // Pointers to column entries\n    int *rix; // Row indices\n    double *values; // Values\n} sparseccs;\n\nextern objecttype objectsparsetype;\n#define OBJECT_SPARSE objectsparsetype\n\ntypedef struct {\n    object obj;\n    sparsedok dok;\n    sparseccs ccs;\n} objectsparse;\n\n/** Tests whether an object is a sparse matrix */\n#define MORPHO_ISSPARSE(val) object_istype(val, OBJECT_SPARSE)\n\n/** Gets the object as a sparse matrix */\n#define MORPHO_GETSPARSE(val)   ((objectsparse *) MORPHO_GETOBJECT(val))\n\n/** @brief Use to create static sparse matrices on the C stack. Note that the entries should be initialized */\n#define MORPHO_STATICSPARSE()      { .obj.type=OBJECT_SPARSE, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL }\n\nobjectsparse *object_newsparse(int *nrows, int *ncols);\nobjectsparse *sparse_sparsefromarray(objectarray *array);\n\n/* -------------------------------------------------------\n * Sparse veneer class\n * ------------------------------------------------------- */\n\n#define SPARSE_CLASSNAME \"Sparse\"\n\n#define SPARSE_ROWINDICES_METHOD \"rowindices\"\n#define SPARSE_SETROWINDICES_METHOD \"setrowindices\"\n#define SPARSE_COLINDICES_METHOD \"colindices\"\n#define SPARSE_INDICES_METHOD \"indices\"\n\n/* -------------------------------------------------------\n * Sparse errors\n * ------------------------------------------------------- */\n\n#define SPARSE_CONSTRUCTOR                \"SprsCns\"\n#define SPARSE_CONSTRUCTOR_MSG            \"Sparse() should be called either with dimensions or an array initializer.\"\n\n#define SPARSE_SETFAILED                  \"SprsSt\"\n#define SPARSE_SETFAILED_MSG              \"Attempt to set sparse matrix element failed.\"\n\n#define SPARSE_INVLDARRAYINIT             \"SprsInvldInit\"\n#define SPARSE_INVLDARRAYINIT_MSG         \"Invalid initializer passed to Sparse().\"\n\n#define SPARSE_CONVFAILEDERR              \"SprsCnvFld\"\n#define SPARSE_CONVFAILEDERR_MSG          \"Sparse format conversion failed.\"\n\n#define SPARSE_OPFAILEDERR                \"SprsOpFld\"\n#define SPARSE_OPFAILEDERR_MSG            \"Sparse matrix operation failed.\"\n\n/* -------------------------------------------------------\n * Sparse interface\n * ------------------------------------------------------- */\n\n/* ***************************************\n * Dictionary of keys format\n * *************************************** */\n\nvoid sparsedok_init(sparsedok *dok);\nvoid sparsedok_clear(sparsedok *dok);\nbool sparsedok_insert(sparsedok *dok, int i, int j, value val);\nbool sparsedok_get(sparsedok *dok, int i, int j, value *val);\nbool sparsedok_remove(sparsedok *dok, int i, int j, value *val);\nbool sparsedok_setdimensions(sparsedok *dok, int nrows, int ncols);\nunsigned int sparsedok_count(sparsedok *dok);\nvoid *sparsedok_loopstart(sparsedok *dok);\nbool sparsedok_loop(sparsedok *dok, void **cntr, int *i, int *j);\nbool sparsedok_copy(sparsedok *src, sparsedok *dest);\nbool sparsedok_copyat(sparsedok *src, sparsedok *dest, int row0, int col0);\nbool sparsedok_copymatrixat(objectmatrix *src, sparsedok *dest, int row0, int col0);\nbool sparsedok_copytomatrix(sparsedok *src, objectmatrix *dest, int row0, int col0);\nvoid sparsedok_print(vm *v, sparsedok *dok);\n\n/* ***************************************\n * Compressed Column Storage Format\n * *************************************** */\n\nvoid sparseccs_init(sparseccs *ccs);\nvoid sparseccs_clear(sparseccs *ccs);\nbool sparseccs_resize(sparseccs *ccs, int nrows, int ncols, unsigned int nentries, bool values);\nbool sparseccs_get(sparseccs *ccs, int i, int j, double *val);\n\nbool sparseccs_getrowindices(sparseccs *ccs, int col, int *nentries, int **entries);\nbool sparseccs_getrowindiceswithvalues(sparseccs *ccs, int col, int *nentries, int **entries, double **vals);\nbool sparseccs_setrowindices(sparseccs *ccs, int col, int nentries, int *entries);\nbool sparseccs_getcolindices(sparseccs *ccs, int maxentries, int *nentries, int *entries);\nbool sparseccs_getcolindicesforrow(sparseccs *ccs, int row, int maxentries, int *nentries, int *entries);\nbool sparseccs_doktoccs(sparsedok *in, sparseccs *out, bool copyvals);\nbool sparseccs_copy(sparseccs *src, sparseccs *dest);\nbool sparseccs_copytodok(sparseccs *src, sparsedok *dest, int row0, int col0);\nbool sparseccs_copytomatrix(sparseccs *src, objectmatrix *dest, int row0, int col0);\nvoid sparseccs_print(vm *v, sparseccs *ccs);\n\ntypedef enum { SPARSE_DOK, SPARSE_CCS } objectsparseformat;\n\ntypedef enum { SPARSE_OK, SPARSE_INCMPTBLDIM, SPARSE_INVLDINIT, SPARSE_CONVFAILED, SPARSE_FAILED } objectsparseerror;\n\n/* ***************************************\n * Generic sparse functions\n * *************************************** */\n\nvoid sparse_raiseerror(vm *v, objectsparseerror err);\n\nbool sparse_checkformat(objectsparse *sparse, objectsparseformat format, bool force, bool copyvals);\n\nobjectsparseerror sparse_tomatrix(objectsparse *in, objectmatrix **out);\nobjectsparse *sparse_clone(objectsparse *s);\nbool sparse_setelement(objectsparse *matrix, int row, int col, value value);\nbool sparse_getelement(objectsparse *matrix, int row, int col, value *value);\nvoid sparse_getdimensions(objectsparse *s, int *nrows, int *ncols);\n\nobjectsparseerror sparse_add(objectsparse *a, objectsparse *b, double alpha, double beta, objectsparse *out);\nobjectsparseerror sparse_mul(objectsparse *a, objectsparse *b, objectsparse *out);\nobjectsparseerror sparse_mulsxd(objectsparse *a, objectmatrix *b, objectmatrix *out);\nobjectsparseerror sparse_muldxs(objectmatrix *a, objectsparse *b, objectmatrix *out);\nobjectsparseerror sparse_transpose(objectsparse *a, objectsparse *out);\n\nvoid sparse_clear(objectsparse *a);\nsize_t sparse_size(objectsparse *a);\n\nobjectsparseerror sparse_cat(objectlist *in, objectsparse *dest);\nobjectsparseerror sparse_catmatrix(objectlist *in, objectmatrix **out);\n\n/* ***************************************\n * Sparse class methods\n * *************************************** */\n\nvalue Sparse_divr(vm *v, int nargs, value *args);\n\n/** Intialization */\nvoid sparse_initialize(void);\n\n#endif\n\n#endif /* sparse_h */\n"
  },
  {
    "path": "src/morpho.h",
    "content": "/** @file morpho.h\n *  @author T J Atherton\n *\n *  @brief Define public interface to Morpho\n */\n\n#ifndef morpho_h\n#define morpho_h\n\n#include \"build.h\"\n#include \"value.h\"\n#include \"error.h\"\n#include \"dictionary.h\"\n#include \"version.h\"\n\n/* **********************************************************************\n* VM types\n* ********************************************************************** */\n\n#ifndef MORPHO_CORE\ntypedef void vm;\ntypedef void program;\ntypedef void compiler;\n#endif\n\n/* **********************************************************************\n* Standard methods\n* ********************************************************************** */\n\n#define MORPHO_INITIALIZER_METHOD \"init\"\n\n#define MORPHO_GETINDEX_METHOD \"index\"\n#define MORPHO_SETINDEX_METHOD \"setindex\"\n#define MORPHO_TOSTRING_METHOD \"tostring\"\n#define MORPHO_FORMAT_METHOD \"format\"\n\n#define MORPHO_ASSIGN_METHOD \"assign\"\n#define MORPHO_ADD_METHOD \"add\"\n#define MORPHO_ADDR_METHOD \"addr\" \n#define MORPHO_SUB_METHOD \"sub\"\n#define MORPHO_SUBR_METHOD \"subr\"\n#define MORPHO_MUL_METHOD \"mul\"\n#define MORPHO_MULR_METHOD \"mulr\"\n#define MORPHO_DIV_METHOD \"div\"\n#define MORPHO_DIVR_METHOD \"divr\"\n#define MORPHO_POW_METHOD \"pow\"\n#define MORPHO_POWR_METHOD \"powr\"\n#define MORPHO_ACC_METHOD \"acc\"\n#define MORPHO_SUM_METHOD \"sum\"\n\n#define MORPHO_CONTAINS_METHOD \"contains\"\n\n#define MORPHO_UNION_METHOD \"union\"\n#define MORPHO_INTERSECTION_METHOD \"intersection\"\n#define MORPHO_DIFFERENCE_METHOD \"difference\"\n\n#define MORPHO_ROLL_METHOD \"roll\"\n#define MORPHO_JOIN_METHOD \"join\"\n\n#define MORPHO_CLASS_METHOD \"clss\"\n#define MORPHO_SUPER_METHOD \"superclass\"\n#define MORPHO_SERIALIZE_METHOD \"serialize\"\n#define MORPHO_HAS_METHOD \"has\"\n#define MORPHO_RESPONDSTO_METHOD \"respondsto\"\n#define MORPHO_INVOKE_METHOD \"invoke\"\n#define MORPHO_CLONE_METHOD \"clone\"\n\n#define MORPHO_ENUMERATE_METHOD \"enumerate\"\n#define MORPHO_COUNT_METHOD \"count\"\n#define MORPHO_CLONE_METHOD \"clone\"\n#define MORPHO_PRINT_METHOD \"prnt\"\n#define MORPHO_SAVE_METHOD \"save\"\n\n/* Non-standard methods */\n#define MORPHO_APPEND_METHOD \"append\"\n#define MORPHO_LINEARIZATION_METHOD \"linearization\"\n\n#define MORPHO_THROW_METHOD \"throw\"\n#define MORPHO_WARNING_METHOD \"warning\"\n\nextern value initselector;\nextern value indexselector;\nextern value setindexselector;\nextern value addselector;\nextern value subselector;\nextern value mulselector;\nextern value divselector;\nextern value printselector;\nextern value enumerateselector;\nextern value countselector;\nextern value cloneselector;\n\n/* **********************************************************************\n* Public interfaces\n* ********************************************************************** */\n\n/* Version checking */\nvoid morpho_version(version *v);\n\n/* Error handling */\nvoid morpho_writeerrorwithid(error *err, errorid id, char *file, int line, int posn, ...);\nvoid morpho_defineerror(errorid id, errorcategory cat, char *message);\nerrorid morpho_geterrorid(error *err);\n\n/* Programs */\nprogram *morpho_newprogram(void);\nvoid morpho_freeprogram(program *p);\n\n/* Optimizers */\ntypedef bool (optimizerfn) (program *in);\nvoid morpho_setoptimizer(optimizerfn *optimizer);\n\n/* Virtual machine */\nvm *morpho_newvm(void);\nvoid morpho_freevm(vm *v);\n\n/* Bind new objects to the virtual machine */\nvoid morpho_bindobjects(vm *v, int nobj, value *obj);\nvalue morpho_wrapandbind(vm *v, object *obj);\n\n/* Interact with the garbage collector in an object definition */\nvoid morpho_markobject(void *v, object *obj);\nvoid morpho_markvalue(void *v, value val);\nvoid morpho_markvarrayvalue(void *v, varray_value *array);\nvoid morpho_markdictionary(void *v, dictionary *dict);\nvoid morpho_searchunmanagedobject(void *v, object *obj);\nbool morpho_ismanagedobject(object *obj); \n\n/* Tell the VM that the size of an object has changed */\nvoid morpho_resizeobject(vm *v, object *obj, size_t oldsize, size_t newsize);\n\n/* Temporarily retain objects across multiple calls into the VM */\nint morpho_retainobjects(vm *v, int nobj, value *obj);\nvoid morpho_releaseobjects(vm *v, int handle);\n\n/* Raise runtime errors and warnings */\nvoid morpho_warning(vm *v, error *err);\nvoid morpho_error(vm *v, error *err);\n\nvoid morpho_runtimeerror(vm *v, errorid id, ...);\nvoid morpho_runtimewarning(vm *v, errorid id, ...);\n\n/* Compilation */\ncompiler *morpho_newcompiler(program *out);\nvoid morpho_freecompiler(compiler *c);\nbool morpho_compile(char *in, compiler *c, bool optimize, error *err);\nconst char *morpho_compilerrestartpoint(compiler *c);\nvoid morpho_resetentry(program *p);\n\n/* Interpreting */\nbool morpho_run(vm *v, program *p);\nbool morpho_profile(vm *v, program *p);\nbool morpho_debug(vm *v, program *p);\nbool morpho_lookupmethod(value obj, value label, value *method);\nbool morpho_countparameters(value f, int *nparams);\nbool morpho_call(vm *v, value fn, int nargs, value *args, value *ret);\nbool morpho_invoke(vm *v, value obj, value method, int nargs, value *args, value *ret);\nerror *morpho_geterror(vm *v);\n\n/* I/O */\nint morpho_printf(vm *v, char *format, ...);\nvoid morpho_printvalue(vm *v, value val);\nint morpho_readline(vm *v, varray_char *buffer);\n\n/* Stack trace */\nvoid morpho_stacktrace(vm *v);\n\n/* Disassembler */\nvoid morpho_disassemble(vm *v, program *code, int *matchline);\n\n/* Multithreading */\nvoid morpho_setthreadnumber(int nthreads);\nint morpho_threadnumber(void);\n\n/* Initialization and finalization */\ntypedef void (*morpho_finalizefn) (void);\nvoid morpho_addfinalizefn(morpho_finalizefn finalizefn);\nvoid morpho_setbaseclass(value clss);\nvoid morpho_initialize(void);\nvoid morpho_finalize(void);\nvoid morpho_setargs(int argc, const char * argv[]); // Pass arguments to morpho\n\n/* Obtain and use subkernels [for internal use only] */\nbool vm_subkernels(vm *v, int nkernels, vm **subkernels);\nvoid vm_releasesubkernel(vm *subkernel);\nvoid vm_cleansubkernel(vm *subkernel);\n\n/* Thread local storage [for internal use only] */\nint vm_addtlvar(void);\nbool vm_settlvar(vm *v, int handle, value val);\nbool vm_gettlvar(vm *v, int handle, value *out);\n\n#endif /* morpho_h */\n"
  },
  {
    "path": "src/support/CMakeLists.txt",
    "content": "target_sources(morpho\n    PRIVATE\n        common.c      common.h\n        extensions.c  extensions.h\n        format.c      format.h\n        lex.c         lex.h\n        memory.c      memory.h\n        parse.c       parse.h\n        platform.c    platform.h\n        random.c      random.h\n        resources.c   resources.h\n        threadpool.c  threadpool.h\n)\n\ntarget_sources(morpho\n    INTERFACE\n    FILE_SET public_headers\n    TYPE HEADERS\n    FILES\n        common.h\n        extensions.h\n        format.h\n        lex.h\n        memory.h\n        parse.h\n        random.h\n        resources.h\n        threadpool.h\n)\n"
  },
  {
    "path": "src/support/common.c",
    "content": "/** @file common.c\n *  @author T J Atherton\n *\n *  @brief Utility functions for the Morpho VM\n */\n\n#include <stdio.h>\n#include <math.h>\n#include <float.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"common.h\"\n\n/* **********************************************************************\n* Printing\n* ********************************************************************** */\n \n/** @brief Prints a value\n * @param v The value to print */\nvoid morpho_printvalue(vm *v, value val) {\n    if (MORPHO_ISFLOAT(val)) {\n        morpho_printf(v, \"%g\", MORPHO_GETFLOATVALUE(val));\n        return;\n    } else {\n        switch (MORPHO_GETTYPE(val)) {\n            case VALUE_NIL:\n                morpho_printf(v, MORPHO_NILSTRING);\n                return;\n            case VALUE_BOOL:\n                morpho_printf(v, \"%s\", (MORPHO_GETBOOLVALUE(val) ? MORPHO_TRUESTRING : MORPHO_FALSESTRING));\n                return;\n            case VALUE_INTEGER:\n                morpho_printf(v, \"%i\", MORPHO_GETINTEGERVALUE(val));\n                return;\n            case VALUE_OBJECT:\n                object_print(v, val);\n                return;\n            default:\n                return;\n        }\n    }\n}\n\n/** @brief Prints a value to a buffer */\n#define MORPHO_TOSTRINGTMPBUFFERSIZE   64\nbool morpho_printtobuffer(vm *v, value val, varray_char *buffer) {\n    bool success=false;\n    char tmp[MORPHO_TOSTRINGTMPBUFFERSIZE];\n    int nv;\n\n    if (MORPHO_ISSTRING(val)) {\n        objectstring *s = MORPHO_GETSTRING(val);\n        success=varray_charadd(buffer, s->string, (int) s->length);\n    } else if (MORPHO_ISCLASS(val)) {\n        objectclass *klass = MORPHO_GETCLASS(val);\n        varray_charwrite(buffer, '@');\n        success=morpho_printtobuffer(v, klass->name, buffer);\n    } else if (MORPHO_ISOBJECT(val)) {\n        objectclass *klass = morpho_lookupclass(val);\n\n        if (klass) {\n            objectstring str = MORPHO_STATICSTRING(MORPHO_TOSTRING_METHOD);\n            value label = MORPHO_OBJECT(&str);\n            value method, ret;\n\n            if (morpho_lookupmethod(val, label, &method) &&\n                morpho_invoke(v, val, method, 0, NULL, &ret)) {\n                if (MORPHO_ISSTRING(ret)) {\n                    success=varray_charadd(buffer, MORPHO_GETCSTRING(ret), (int) MORPHO_GETSTRINGLENGTH(ret));\n                }\n            } else {\n                varray_charwrite(buffer, '<');\n                success=morpho_printtobuffer(v, klass->name, buffer);\n                varray_charwrite(buffer, '>');\n            }\n        } else if (MORPHO_ISBUILTINFUNCTION(val)) {\n            objectbuiltinfunction *fn = MORPHO_GETBUILTINFUNCTION(val);\n            varray_charadd(buffer, \"<fn \", 4);\n            success=morpho_printtobuffer(v, fn->name, buffer);\n            varray_charwrite(buffer, '>');\n        }\n    } else if (MORPHO_ISFLOAT(val)) {\n        nv=snprintf(tmp, MORPHO_TOSTRINGTMPBUFFERSIZE, \"%g\", MORPHO_GETFLOATVALUE(val));\n        success=varray_charadd(buffer, tmp, nv);\n    } else if (MORPHO_ISINTEGER(val)) {\n        nv=snprintf(tmp, MORPHO_TOSTRINGTMPBUFFERSIZE, \"%i\", MORPHO_GETINTEGERVALUE(val));\n        success=varray_charadd(buffer, tmp, nv);\n    } else if (MORPHO_ISBOOL(val)) {\n        nv=snprintf(tmp, MORPHO_TOSTRINGTMPBUFFERSIZE, \"%s\", (MORPHO_ISTRUE(val) ? MORPHO_TRUESTRING : MORPHO_FALSESTRING));\n        success=varray_charadd(buffer, tmp, nv);\n    } else if (MORPHO_ISNIL(val)) {\n        nv=snprintf(tmp, MORPHO_TOSTRINGTMPBUFFERSIZE, \"%s\", MORPHO_NILSTRING);\n        success=varray_charadd(buffer, tmp, nv);\n    }\n    \n    return success; \n}\n\n/** @brief Concatenates a sequence of values as a string */\nvalue morpho_concatenate(vm *v, int nval, value *val) {\n    varray_char buffer;\n    varray_charinit(&buffer);\n\n    for (unsigned int i=0; i<nval; i++) {\n        morpho_printtobuffer(v, val[i], &buffer);\n    }\n\n    value out=object_stringfromcstring(buffer.data, buffer.count);\n\n    varray_charclear(&buffer);\n\n    return out;\n}\n\n/** @brief   Duplicates a string.\n *  @param   string String to duplicate\n *  @warning Caller must call MALLOC_FREE on the allocated string */\nchar *morpho_strdup(char *string) {\n    size_t len = strlen(string) + 1;\n    char* output = (char *) MORPHO_MALLOC((len + 1) * sizeof(char));\n    if (output) memcpy(output, string, len);\n\n    return output;\n}\n\n/* **********************************************************************\n* UTF8 support\n* ********************************************************************** */\n\n/** @brief Returns the number of bytes in the next character of a given utf8 string\n    @returns number of bytes */\nint morpho_utf8numberofbytes(const char *string) {\n    uint8_t byte = * ((uint8_t *) string);\n\n    if ((byte & 0xc0) == 0x80) return 0; // In the middle of a utf8 string\n\n    // Get the number of bytes from the first character\n    if ((byte & 0xf8) == 0xf0) return 4;\n    if ((byte & 0xf0) == 0xe0) return 3;\n    if ((byte & 0xe0) == 0xc0) return 2;\n    return 1;\n}\n\n/** Decodes a utf8 encoded character pointed to by c into an int */\nint morpho_utf8toint(const char *c) {\n    unsigned int ret = -1;\n    int nbytes=morpho_utf8numberofbytes(c);\n    switch (nbytes) {\n        case 1: ret=(c[0] & 0x7f); break;\n        case 2: ret=((c[0] & 0x1f)<<6) | (c[1] & 0x3f); break;\n        case 3: ret=((c[0] & 0x0f)<<12) | ((c[1] & 0x3f)<<6) | (c[2] & 0x3f); break;\n        case 4: ret=((c[0] & 0x0f)<<18) | ((c[1] & 0x3f)<<12) | ((c[2] & 0x3f)<<6) | (c[3] & 0x3f) ; break;\n        default: break;\n    }\n    \n    return ret;\n}\n\n/** Encodes a unicode character c into a utf8 encoded string, returning the number of bytes written.\n  @param[in] c - character to encode\n  @param[out] out - buffer to hold string, which must be at least 4 bytes\n  @returns the number of bytes written, including 0 on failure */\nint morpho_encodeutf8(int c, char *out) {\n    if (c<=0x7f) { // 1 byte unicode -> ascii\n        out[0]=c; // b 0XXXXXXX\n        return 1;\n    } else if (c<=0x07ff) { // 2 byte\n        out[0]=(char) (((c >>  6) & 0x1f) | 0xc0); // b 110XXXXX\n        out[1]=(char) (((c >>  0) & 0x3f) | 0x80); // b 10XXXXXX\n        return 2;\n    } else if (c<=0xffff) { // 3 byte\n        out[0]=(char) (((c >> 12) & 0x0f) | 0xe0); // b 1110XXXX\n        out[1]=(char) (((c >>  6) & 0x3f) | 0x80); // b 10XXXXXX\n        out[2]=(char) (((c >>  0) & 0x3f) | 0x80); // b 10XXXXXX\n        return 3;\n    } else if (c<=0x10ffff) {\n        out[0]=(char) (((c >> 18) & 0x07) | 0xf0); // b 11110XXX\n        out[1]=(char) (((c >> 12) & 0x3f) | 0x80); // b 10XXXXXX\n        out[2]=(char) (((c >>  6) & 0x3f) | 0x80); // b 10XXXXXX\n        out[3]=(char) (((c >>  0) & 0x3f) | 0x80); // b 10XXXXXX\n        return 4;\n    }\n    return 0;\n}\n\n/* **********************************************************************\n* Other utility functions\n* ********************************************************************** */\n\n/** @brief Computes the nearest power of 2 above an integer\n * @param   n An integer\n * @returns Nearest power of 2 above n\n * See: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float */\nunsigned int morpho_powerof2ceiling(unsigned int n) {\n    n--;\n    n |= n >> 1;\n    n |= n >> 2;\n    n |= n >> 4;\n    n |= n >> 8;\n    n |= n >> 16;\n    n++;\n\n    return n;\n}\n\n/** Count the number of fixed parameters in a callable object\n * @param[in] f - the function or callable object\n * @param[out] nparams - number of parameters; -1 if unknown\n * @returns true on success, false if f is not callable*/\nbool morpho_countparameters(value f, int *nparams) {\n    value g = f;\n    bool success=false;\n\n    if (MORPHO_ISINVOCATION(g)) { // Unpack invocation\n        objectinvocation *inv = MORPHO_GETINVOCATION(g);\n        g=inv->method;\n    }\n\n    if (MORPHO_ISCLOSURE(g)) { // Unpack closure\n        objectclosure *cl = MORPHO_GETCLOSURE(g);\n        g=MORPHO_OBJECT(cl->func);\n    }\n\n    if (MORPHO_ISFUNCTION(g)) {\n        objectfunction *fun = MORPHO_GETFUNCTION(g);\n        *nparams=fun->nargs;\n        success=true;\n    } else if (MORPHO_ISBUILTINFUNCTION(g)) {\n        *nparams = -1;\n        success=true;\n    }\n\n    return success;\n}\n\n/** Initialize tuple generator\n @param[in] nval - number of values\n @param[in] n - n-tuples to generate\n @param[in] c - workspace: supply an unsigned integer array of size 2xn  */\nvoid morpho_tuplesinit(unsigned int nval, unsigned int n, unsigned int *c, tuplemode mode) {\n    unsigned int *counter=c, *cmax=c+n; // Counters\n    for (unsigned int i=0; i<n; i++) {\n        counter[i]=(mode == MORPHO_SETMODE ? i : 0 );\n        cmax[i]=(mode == MORPHO_SETMODE ? nval-n+i : nval-1);\n    }\n}\n\n/** Generate n-tuples of unique elements indep of ordering from a list of values\n @param[in] nval - number of values\n @param[in] list - list of values\n @param[in] n - n-tuples to generate\n @param[in] c - workspace: supply an unsigned integer array of size 2xn;\n @param[out] tuple - generated tuple\n @returns true if we returned a valid tuple; false if we're done */\nbool morpho_tuples(unsigned int nval, value *list, unsigned int n, unsigned int *c, tuplemode mode, value *tuple) {\n    unsigned int *counter=c, *cmax=c+n; // Counters\n    int k;\n\n    if (counter[0]>cmax[0]) return false; // Done\n\n    // Generate tuple from counter\n    for (unsigned int i=0; i<n; i++) tuple[i]=list[counter[i]];\n\n    // Increment counters\n    counter[n-1]++; // Increment last counter\n    for (k=n-1; k>0 && counter[k]>cmax[k]; k--) counter[k-1]++; // Carry\n\n    if (k<n-1) {\n        if (mode==MORPHO_TUPLEMODE) for (unsigned int i=k+1; i<n; i++) counter[i]=0;\n        if (mode==MORPHO_SETMODE) for (unsigned int i=k+1; i<n; i++) counter[i]=counter[i-1]+1;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "src/support/common.h",
    "content": "/** @file common.h\n *  @author T J Atherton\n *\n *  @brief Morpho virtual machine\n */\n\n#ifndef common_h\n#define common_h\n\n#include <stddef.h>\n#include <stdarg.h>\n#include <math.h>\n#include \"value.h\"\n#include \"object.h\"\n#include \"classes.h\"\n\n#define MORPHO_NILSTRING   \"nil\"\n#define MORPHO_TRUESTRING  \"true\"\n#define MORPHO_FALSESTRING \"false\"\n\n/* -----------------------------------------\n * VM Callback functions\n * ----------------------------------------- */\n\ntypedef enum {\n    MORPHO_INPUT_KEYPRESS,\n    MORPHO_INPUT_LINE\n} morphoinputmode;\n\n/* Callback function used to obtain input from stdin */\ntypedef void (*morphoinputfn) (vm *v, void *ref, morphoinputmode mode, varray_char *str);\n\n/* Callback function used to print text to stdout */\ntypedef void (*morphoprintfn) (vm *v, void *ref, char *str);\n\n/* Callback function used to output a warning */\ntypedef void (*morphowarningfn) (vm *v, void *ref, error *warning);\n\n/* Callback function used to enter debugger */\ntypedef void (*morphodebuggerfn) (vm *v, void *ref);\n\nvoid morpho_setwarningfn(vm *v, morphowarningfn warningfn, void *ref);\nvoid morpho_setprintfn(vm *v, morphoprintfn printfn, void *ref);\nvoid morpho_setinputfn(vm *v, morphoinputfn inputfn, void *ref);\nvoid morpho_setdebuggerfn(vm *v, morphodebuggerfn debuggerfn, void *ref);\n\n/* -----------------------------------------\n * Functions and macros for comparing values\n * ----------------------------------------- */\n\n/** @brief Promotes l and r to types that can be compared\n * @param l value to compare\n * @param r value to compare */\n/*#define MORPHO_CMPPROMOTETYPE(l, r) \\\n    if (!morpho_ofsametype(l, r)) { \\\n        if (MORPHO_ISINTEGER(l) && MORPHO_ISFLOAT(r)) { \\\n            l = MORPHO_INTEGERTOFLOAT(l); \\\n        } else if (MORPHO_ISFLOAT(l) && MORPHO_ISINTEGER(r)) { \\\n            r = MORPHO_INTEGERTOFLOAT(r); \\\n        } \\\n    }*/\n\n/** Check if a value is callable */\nstatic inline bool morpho_iscallable(value a) {\n    return (MORPHO_ISFUNCTION(a) ||\n            MORPHO_ISBUILTINFUNCTION(a) ||\n            MORPHO_ISMETAFUNCTION(a) ||\n            MORPHO_ISINVOCATION(a) ||\n            MORPHO_ISCLOSURE(a) ||\n            MORPHO_ISCLASS(a));\n}\n\n#define MORPHO_ISCALLABLE(x) (morpho_iscallable(x))\n\nbool morpho_printtobuffer(vm *v, value val, varray_char *buffer);\nvalue morpho_concatenate(vm *v, int nval, value *val);\n\nchar *morpho_strdup(char *string);\n\nint morpho_utf8numberofbytes(const char *string);\nint morpho_utf8toint(const char *c);\nint morpho_encodeutf8(int c, char *out);\n\nunsigned int morpho_powerof2ceiling(unsigned int n);\n\n#ifdef MORPHO_DEBUG\nvoid morpho_unreachable(const char *explanation);\n#endif\n\ntypedef enum {\n    MORPHO_TUPLEMODE, // Generates tuples (all combinations of n elements)\n    MORPHO_SETMODE // Generates sets (unique elements and indep of order)\n} tuplemode;\n\nvoid morpho_tuplesinit(unsigned int nval, unsigned int n, unsigned int *c, tuplemode mode);\nbool morpho_tuples(unsigned int nval, value *list, unsigned int n, unsigned int *c, tuplemode mode, value *tuple);\n\n#endif /* common_h */\n"
  },
  {
    "path": "src/support/extensions.c",
    "content": "/** @file extensions.c\n *  @author T J Atherton\n *\n *  @brief Morpho extensions\n */\n\n/* **********************************************************************\n* Extensions\n* ********************************************************************** */\n\n#include <string.h>\n\n#include \"varray.h\"\n#include \"value.h\"\n#include \"common.h\"\n#include \"object.h\"\n#include \"builtin.h\"\n#include \"resources.h\"\n#include \"extensions.h\"\n#include \"platform.h\"\n\n/* -------------------------------------------------------\n * Extension structure\n * ------------------------------------------------------- */\n\ntypedef struct {\n    value name;\n    value path;\n    value functiontable;\n    value classtable;\n    MorphoDLHandle handle;\n} extension;\n\nDECLARE_VARRAY(extension, extension)\nDEFINE_VARRAY(extension, extension)\n\nvarray_extension extensionlist; // List of loaded extensions\n\n/* -------------------------------------------------------\n * Extension interface\n * ------------------------------------------------------- */\n\n/** Open the dynamic library associated with an extension */\nbool extension_dlopen(extension *e) {\n    if (e->handle) return true; // Prevent multiple loads\n    if (MORPHO_ISSTRING(e->path)) e->handle=platform_dlopen(MORPHO_GETCSTRING(e->path));\n    return e->handle;\n}\n\n/** Close the dynamic library associated with an extension */\nvoid extension_dlclose(extension *e) {\n    if (e->handle) platform_dlclose(e->handle);\n    e->handle=NULL;\n}\n\n/** Initializes an extension structure with empty values */\nvoid extension_init(extension *e) {\n    e->name=MORPHO_NIL;\n    e->path=MORPHO_NIL;\n    e->functiontable=MORPHO_NIL;\n    e->classtable=MORPHO_NIL;\n    e->handle=NULL;\n}\n\n/** Clears an extension structure */\nvoid extension_clear(extension *e) {\n    if (MORPHO_ISOBJECT(e->name)) morpho_freeobject(e->name);\n    if (MORPHO_ISOBJECT(e->path)) morpho_freeobject(e->path);\n    \n    // The functions and classes are freed from the builtin_objects list. \n    if (MORPHO_ISOBJECT(e->functiontable)) morpho_freeobject(e->functiontable);\n    if (MORPHO_ISOBJECT(e->classtable)) morpho_freeobject(e->classtable);\n    \n    extension_dlclose(e);\n    extension_init(e);\n}\n\n/** Initializes an extension structure with a name and path, creating associated data structures */\nbool extension_initwithname(extension *e, char *name, char *path) {\n    extension_init(e);\n    e->name=object_stringfromcstring(name, strlen(name));\n    e->path=object_stringfromcstring(path, strlen(path));\n    \n    objectdictionary *functiontable = object_newdictionary(),\n                     *classtable = object_newdictionary();\n    \n    if (functiontable) e->functiontable=MORPHO_OBJECT(functiontable);\n    if (classtable) e->classtable=MORPHO_OBJECT(classtable);\n    e->handle=NULL;\n    \n    if (!MORPHO_ISSTRING(e->name) ||\n        !MORPHO_ISSTRING(e->path) ||\n        !MORPHO_ISDICTIONARY(e->functiontable) ||\n        !MORPHO_ISDICTIONARY(e->classtable)) {\n        extension_clear(e);\n        return false;\n    }\n    return true;\n}\n\n/** Trys to locate a function with NAME_FN in extension e, and calls it if found */\nbool extension_call(extension *e, const char *name, const char *fn) {\n    void (*fptr) (void);\n    size_t size = strlen(name) + strlen(fn) + 2;\n    char fnname[size];\n    strncpy(fnname, name, size);\n    strncat(fnname, \"_\", size);\n    strncat(fnname, fn, size);\n    \n    fptr = platform_dlsym(e->handle, fnname);\n    if (fptr) (*fptr) ();\n    return fptr;\n}\n\n/** Finds the path for an extension using the resource finder */\nbool extension_find(char *name, value *path) {\n    return morpho_findresource(MORPHO_RESOURCE_EXTENSION, name, path);\n}\n\n/** Checks if an extension is already loaded;  returns it in out if found */\nbool extension_isloaded(value path, extension *out) {\n    for (int i=0; i<extensionlist.count; i++) {\n        extension *e = &extensionlist.data[i];\n        \n        if (MORPHO_ISEQUAL(path, e->path)) {\n            if (out) *out = *e;\n            return true;\n        }\n    }\n    return false;\n}\n\n/** Call the extension's initializer */\nbool extension_initialize(extension *e) {\n    dictionary *ofunc=builtin_getfunctiontable(),\n               *oclss=builtin_getclasstable();\n    \n    builtin_setfunctiontable(MORPHO_GETDICTIONARYSTRUCT(e->functiontable));\n    builtin_setclasstable(MORPHO_GETDICTIONARYSTRUCT(e->classtable));\n    \n    bool success=extension_call(e, MORPHO_GETCSTRING(e->name), MORPHO_EXTENSIONINITIALIZE);\n    \n    builtin_setfunctiontable(ofunc);\n    builtin_setclasstable(oclss);\n    \n    return success; \n}\n\n/** Call the extension's finalizer */\nbool extension_finalize(extension *e) {\n    return extension_call(e, MORPHO_GETCSTRING(e->name), MORPHO_EXTENSIONFINALIZE);\n}\n\n/** Load an extension, optionally returning a dictionary of functions and classes defined by the extension */\nbool extension_load(char *name, dictionary **functiontable, dictionary **classtable) {\n    value path;\n    if (!extension_find(name, &path)) return false;\n    \n    bool success=false;\n    \n    extension e;\n    extension_init(&e);\n    \n    if (extension_isloaded(path, &e)) {\n        success=true;\n    } else if (extension_initwithname(&e, name, MORPHO_GETCSTRING(path)) &&\n               extension_dlopen(&e)) {\n        success=extension_initialize(&e);\n        if (success) varray_extensionwrite(&extensionlist, e);\n    }\n    \n    if (success) {\n        if (functiontable) *functiontable = MORPHO_GETDICTIONARYSTRUCT(e.functiontable);\n        if (classtable) *classtable = MORPHO_GETDICTIONARYSTRUCT(e.classtable);\n    } else extension_clear(&e);\n    \n    morpho_freeobject(path);\n    \n    return success;\n}\n\n/* -------------------------------------------------------\n * Extensions initialization/finalization\n * ------------------------------------------------------- */\n\nvoid extensions_initialize(void) {\n    varray_extensioninit(&extensionlist);\n \n    morpho_addfinalizefn(extensions_finalize);\n}\n\nvoid extensions_finalize(void) {\n    for (int i=0; i<extensionlist.count; i++) {\n        extension *e = &extensionlist.data[i];\n        extension_finalize(e);\n        extension_clear(e);\n    }\n    varray_extensionclear(&extensionlist);\n}\n"
  },
  {
    "path": "src/support/extensions.h",
    "content": "/** @file extensions.h\n *  @author T J Atherton\n *\n *  @brief Morpho extensions\n */\n\n/** Extensions are libraries written in C, or a langauge that links with C, that are loaded dynamically at runtime using dlopen().\n * As these are loaded, they may defined functions and classes that can then be used by morpho programs */\n\n#ifndef extensions_h\n#define extensions_h\n\n#include <stdbool.h>\n\n#define MORPHO_EXTENSIONINITIALIZE \"initialize\" // Function to call upon initialization\n#define MORPHO_EXTENSIONFINALIZE \"finalize\"     // Function to call upon finalization\n\nbool extension_load(char *name, dictionary **functiontable, dictionary **classtable);\n\nvoid extensions_initialize(void);\nvoid extensions_finalize(void);\n\n#endif /* extensions_h */\n"
  },
  {
    "path": "src/support/format.c",
    "content": "/** @file format.c\n *  @author T J Atherton\n *\n *  @brief Formatting of values\n*/\n\n#include <string.h>\n#include <ctype.h>\n#include <stdio.h>\n\n#include \"value.h\"\n#include \"format.h\"\n#include \"varray.h\"\n\n#define SPRINTFBUFFER 255\n#define ERROR_CHECK(f) if (!(f)) return false;\n\n/* **********************************************************************\n * Format utility functions\n * ********************************************************************** */\n\n#define UNSET -1\n\ntypedef struct fformat {\n    int width;\n    int precision;\n    char type;\n} format;\n\n/** Parses a format string\n @param[in] formatstring - format string to parse\n @param[in] valid - valid type characters */\nbool _format_parse(char *formatstring, char *validtypes, char **endptr, format *f) {\n    f->width=UNSET;\n    f->precision=UNSET;\n    f->type=' ';\n    \n    char *c = formatstring;\n    \n    if (*c=='%') c++;\n    else return false;\n    \n    if (isdigit(*c)) { // Field width\n        f->width = (int) strtol(c, &c, 10);\n    }\n    \n    if (*c=='.') { // Precision\n        c++;\n        if (isdigit(*c)) {\n            f->precision = (int) strtol(c, &c, 10);\n        } else return false;\n    }\n    \n    if (!strchr(validtypes, *c)) return false; // Check the type is valid\n    f->type=*c;\n    c++;\n    \n    if (endptr) *endptr = c;\n    \n    return true;\n}\n\n/** Prints a value to a buffer using a format specifier */\nbool _format_printtobuffer(value v, format *f, varray_char *out) {\n    if (!(MORPHO_ISFLOAT(v) || MORPHO_ISINTEGER(v))) return false;\n    \n    char format[SPRINTFBUFFER];\n    char buffer[SPRINTFBUFFER];\n    \n    char *c = format;\n    *c='%'; c++;\n    if (f->width>=0) c+=snprintf(c, format+SPRINTFBUFFER-c, \"%i\", f->width);\n    if (f->precision>=0) c+=snprintf(c, format+SPRINTFBUFFER-c, \".%i\", f->precision);\n    *c=f->type; c++;\n    *c='\\0';\n    \n    int nchars=0;\n    \n    if (MORPHO_ISFLOAT(v)) nchars=snprintf(buffer, SPRINTFBUFFER, format, MORPHO_GETFLOATVALUE(v));\n    else nchars=snprintf(buffer, SPRINTFBUFFER, format, MORPHO_GETINTEGERVALUE(v));\n    \n    varray_charadd(out, buffer, nchars);\n    \n    return true;\n}\n\n/* **********************************************************************\n * Format public functions\n * ********************************************************************** */\n\n/** Prints a quantity to a buffer */\nbool format_printtobuffer(value v, char *formatstring, varray_char *out) {\n    char *validtypes=FORMAT_INTTYPES;\n    if (MORPHO_ISFLOAT(v)) validtypes=FORMAT_FLOATTYPES;\n    \n    for (char *c = formatstring; *c!='\\0'; ) { // Loop over format string\n        if (*c!='%') { // Output any characters unconnected to the format\n            varray_charwrite(out, *c);\n            c++;\n        } else {\n            format f;\n            ERROR_CHECK(_format_parse(c, validtypes, &c, &f));\n            ERROR_CHECK(_format_printtobuffer(v, &f, out));\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "src/support/format.h",
    "content": "/** @file format.h\n *  @author T J Atherton \n *\n *  @brief Formatting of values\n*/\n\n#ifndef format_h\n#define format_h\n\n#define FORMAT_FLOATTYPES \"efgEG\"\n#define FORMAT_INTTYPES \"ioxX\"\n\nbool format_printtobuffer(value v, char *format, varray_char *out);\n\n#endif /* format_h */\n"
  },
  {
    "path": "src/support/lex.c",
    "content": "/** @file lex.c\n *  @author T J Atherton \n *\n *  @brief Lexer\n*/\n\n#include <string.h>\n#include <ctype.h>\n\n#include \"lex.h\"\n\nextern tokendefn standardtokens[];\nextern int nstandardtokens;\n\n/* **********************************************************************\n * Lexer library functions\n * ********************************************************************** */\n\n/* -------------------------------------------------------\n * Comparison functions for token definitions\n * ------------------------------------------------------- */\n\n/** Compare two token definitions */\nint _lex_tokndefncmp(const void *ldefn, const void *rdefn) {\n    tokendefn *a = (tokendefn *) ldefn;\n    tokendefn *b = (tokendefn *) rdefn;\n    \n    return strcmp(a->string, b->string);\n}\n\n/** Compare a string  with the contents of a token definition */\nint _lex_tokendefnwithstringcmp(const void *lstr, const void *rdefn) {\n    char *a = (char *) lstr;\n    tokendefn *b = (tokendefn *) rdefn;\n    \n    return strcmp(a, b->string);\n}\n\n/** Compare the contents of a token with the contents of a token definition */\nint _lex_tokendefnwithtokencmp(const void *ltok, const void *rdefn) {\n    token *tok = (token *) ltok;\n    tokendefn *b = (tokendefn *) rdefn;\n    \n    // Compare token with token definition\n    int cmp = strncmp(tok->start, b->string, tok->length);\n    \n    // If we see a match, ensure that we're not simply matching the initial part of the definition.\n    if (cmp==0 && b->string[tok->length]!='\\0') cmp = -b->string[tok->length]; // Mimic behavior of strcmp\n    \n    return cmp;\n}\n\n/** Compare a character with the first character of a token definition */\nint _lex_tokendefnwithtokenfirstcharcmp(const void *l, const void *rdefn) {\n    char *c = (char *) l;\n    tokendefn *b = (tokendefn *) rdefn;\n    \n    return *c -  b->string[0];\n}\n\n\nDEFINE_VARRAY(tokendefn, tokendefn);\n\n/* -------------------------------------------------------\n * Library functions to support writing custom lexers\n * ------------------------------------------------------- */\n\n/** @brief Records a token\n *  @param[in]  l     The lexer in use\n *  @param[in]  type  Type of token to record\n *  @param[out] tok   Token structure to fill out */\nvoid lex_recordtoken(lexer *l, tokentype type, token *tok) {\n    tok->type=type;\n    tok->start=l->start;\n    tok->length=(int) (l->current - l->start);\n    tok->line=l->line;\n    tok->posn=l->posn - tok->length;\n}\n\n/** @brief Advances the lexer by one character, returning the character */\nchar lex_advance(lexer *l) {\n    char c = *(l->current);\n    l->current++;\n    l->posn++;\n    return c;\n}\n\n/** @brief Advances the lexer by n characters, returning the last character */\nchar lex_advanceby(lexer *l, size_t n) {\n    l->current+=n;\n    l->posn+=n;\n    return *(l->current-1);\n}\n\n/** @brief Reverses the current character in the lexer by one. */\nbool lex_back(lexer *l) {\n    if (l->current==l->start) return false;\n    l->current--;\n    l->posn--;\n    return true;\n}\n\n/** @brief Checks if we're at the end of the string. Doesn't advance. */\nbool lex_isatend(lexer *l) {\n    return (*(l->current) == '\\0');\n}\n\n/** @brief Checks if a character is alphanumeric or underscore. */\nbool lex_isalpha(char c) {\n    return (isalpha(c) || (c=='_'));\n}\n\n/** @brief Checks if a character is a digit.  */\nbool lex_isdigit(char c) {\n    return isdigit(c);\n}\n\n/** @brief Checks if a character is whitespace.\n    @warning: The morpho lexer does not consider newlines to be whitespace. */\nbool lex_isspace(char c) {\n    return (c==' ') || (c=='\\t') || (c=='\\n') || (c=='\\r');\n}\n\n/** @brief Returns the next character */\nchar lex_peek(lexer *l) {\n    return *(l->current);\n}\n\n/** @brief Returns n characters ahead. Caller should check that this is meaningfull. */\nchar lex_peekahead(lexer *l, int n) {\n    return *(l->current + n);\n}\n\n/** @brief Returns the previous character */\nchar lex_peekprevious(lexer *l) {\n    if (l->current==l->start) return '\\0';\n    return *(l->current - 1);\n}\n\n/** @brief Advance line counter */\nvoid lex_newline(lexer *l) {\n    l->line++; l->posn=0;\n}\n\n/** @brief Attempts to find a matching token for the current token.\n *  @param[in] l The lexer in use\n *  @param[out] defn Type of token, if found\n *  @returns true if the token matched, false if not */\nbool lex_matchtoken(lexer *l, tokendefn **defn) {\n    token tok = { .start = l->start, .length = (int) (l->current - l->start) };\n\n    tokendefn *def = bsearch(&tok, l->defns, l->ndefns, sizeof(tokendefn),\n                              _lex_tokendefnwithtokencmp);\n    \n    if (def && defn) *defn = def;\n    \n    return def;\n}\n\n/** @brief Attempts to identify a token from the current point, advances if it finds one.\n *  @param[in] l The lexer in use\n *  @param[out] defn Type of token, if found\n *  @returns true if the token matched, false if not */\nbool lex_identifytoken(lexer *l, tokendefn **defn) {\n    char c = lex_peek(l);\n    \n    // Match first character\n    tokendefn *def = bsearch(&c, l->defns, l->ndefns, sizeof(tokendefn),\n                              _lex_tokendefnwithtokenfirstcharcmp);\n    if (!def) return false;\n    \n    tokendefn *last = l->defns+l->ndefns-1;\n    // Now find the last definition that matches this character\n    while (def<last && (def+1)->string[0]==c) def++;\n    \n    // Test each in turn, working backwards to match the longest token we can\n    for (; def->string[0]==c && def>=l->defns; def--) {\n        size_t len=strlen(def->string);\n        if (strncmp(def->string, l->current, len)==0) {\n            lex_advanceby(l, len);\n            if (defn) *defn = def;\n            return true;\n        }\n    }\n    \n    return false;\n}\n\n/* **********************************************************************\n * Morpho lexer\n * ********************************************************************** */\n\n// Process functions we'll be using\nbool lex_string(lexer *l, token *tok, error *err);\nbool lex_processnewline(lexer *l, token *tok, error *err);\nbool lex_processinterpolation(lexer *l, token *tok, error *err);\n\n/* -------------------------------------------------------\n * Morpho token definitions\n * ------------------------------------------------------- */\n\ntokendefn standardtokens[] = {\n    { \"(\",          TOKEN_LEFTPAREN         , NULL },\n    { \")\",          TOKEN_RIGHTPAREN        , NULL },\n    { \"[\",          TOKEN_LEFTSQBRACKET     , NULL },\n    { \"]\",          TOKEN_RIGHTSQBRACKET    , NULL },\n    { \"{\",          TOKEN_LEFTCURLYBRACKET  , NULL },\n    { \"}\",          TOKEN_RIGHTCURLYBRACKET , lex_processinterpolation },\n    { \";\",          TOKEN_SEMICOLON         , NULL },\n    { \":\",          TOKEN_COLON             , NULL },\n    { \",\",          TOKEN_COMMA             , NULL },\n    { \"^\",          TOKEN_CIRCUMFLEX        , NULL },\n    { \"?\",          TOKEN_QUESTION          , NULL },\n    { \"@\",          TOKEN_AT                , NULL },\n    { \"#\",          TOKEN_HASH              , NULL },\n    { \".\",          TOKEN_DOT               , NULL },\n    { \"..\",         TOKEN_DOTDOT            , NULL },\n    { \"...\",        TOKEN_DOTDOTDOT         , NULL },\n    { \"+\",          TOKEN_PLUS              , NULL },\n    { \"+=\",         TOKEN_PLUSEQ            , NULL },\n    { \"-\",          TOKEN_MINUS             , NULL },\n    { \"-=\",         TOKEN_MINUSEQ           , NULL },\n    { \"*\",          TOKEN_STAR              , NULL },\n    { \"*=\",         TOKEN_STAREQ            , NULL },\n    { \"/\",          TOKEN_SLASH             , NULL },\n    { \"/=\",         TOKEN_SLASHEQ           , NULL },\n    { \"==\",         TOKEN_EQ                , NULL },\n    { \"=\",          TOKEN_EQUAL             , NULL },\n    { \"!\",          TOKEN_EXCLAMATION       , NULL },\n    { \"!=\",         TOKEN_NEQ               , NULL },\n    { \"<\",          TOKEN_LT                , NULL },\n    { \"<=\",         TOKEN_LTEQ              , NULL },\n    { \">\",          TOKEN_GT                , NULL },\n    { \">=\",         TOKEN_GTEQ              , NULL },\n    { \"&\",          TOKEN_AMP               , NULL },\n    { \"&&\",         TOKEN_DBLAMP            , NULL },\n    { \"|\",          TOKEN_VBAR              , NULL },\n    { \"||\",         TOKEN_DBLVBAR           , NULL },\n    { \"\\\"\",         TOKEN_QUOTE             , lex_string },\n    { \"\\n\",         TOKEN_NEWLINE           , lex_processnewline },\n    { \"and\",        TOKEN_DBLAMP            , NULL },\n    { \"as\",         TOKEN_AS                , NULL },\n    { \"break\",      TOKEN_BREAK             , NULL },\n    { \"class\",      TOKEN_CLASS             , NULL },\n    { \"continue\",   TOKEN_CONTINUE          , NULL },\n    { \"catch\",      TOKEN_CATCH             , NULL },\n    { \"do\",         TOKEN_DO                , NULL },\n    { \"else\",       TOKEN_ELSE              , NULL },\n    { \"false\",      TOKEN_FALSE             , NULL },\n    { \"for\",        TOKEN_FOR               , NULL },\n    { \"fn\",         TOKEN_FUNCTION          , NULL },\n    { \"help\",       TOKEN_QUESTION          , NULL },\n    { \"if\",         TOKEN_IF                , NULL },\n    { \"in\",         TOKEN_IN                , NULL },\n    { \"is\",         TOKEN_IS                , NULL },\n    { \"import\",     TOKEN_IMPORT            , NULL },\n    { \"im\",         TOKEN_IMAG              , NULL },\n    { \"nil\",        TOKEN_NIL               , NULL },\n    { \"or\",         TOKEN_DBLVBAR           , NULL },\n    { \"print\",      TOKEN_PRINT             , NULL },\n    { \"return\",     TOKEN_RETURN            , NULL },\n    { \"self\",       TOKEN_SELF              , NULL },\n    { \"super\",      TOKEN_SUPER             , NULL },\n    { \"true\",       TOKEN_TRUE              , NULL },\n    { \"try\",        TOKEN_TRY               , NULL },\n    { \"var\",        TOKEN_VAR               , NULL },\n    { \"while\",      TOKEN_WHILE             , NULL },\n    { \"with\",       TOKEN_WITH              , NULL },\n    { \"\",           TOKEN_NONE              , NULL }  // Token list should be terminated by an empty token\n};\n\nint nstandardtokens;\n\n/* -------------------------------------------------------\n * Morpho lexing functions\n * ------------------------------------------------------- */\n\n/** @brief Skips multiline comments\n * @param[in]  l    the lexer\n * @param[out] tok  token record to fill out (if necessary)\n * @param[out] err  error struct to fill out on errors\n * @returns true on success, false if an error occurs */\nbool lex_skipmultilinecomment(lexer *l, token *tok, error *err) {\n    unsigned int level=0;\n    unsigned int startline = l->line, startpsn = l->posn;\n    \n    do {\n        char c = lex_peek(l);\n        switch (c) {\n            case '\\0':\n                /* If we come to the end of the file, the token is marked as incomplete. */\n                morpho_writeerrorwithid(err, LEXER_UNTERMINATEDCOMMENT, NULL, startline, startpsn);\n                lex_recordtoken(l, TOKEN_INCOMPLETE, tok);\n                return false;\n            case '\\n':\n                /* Advance the line counter. */\n                lex_newline(l);\n                break;\n            case '/':\n                if (lex_peekahead(l, 1)=='*') {\n                    level++; lex_advance(l);\n                }\n                break;\n            case '*':\n                if (lex_peekahead(l, 1)=='/') {\n                    level--; lex_advance(l);\n                }\n                break;\n            default:\n                break;\n        }\n        /* Now advance the counter */\n        lex_advance(l);\n    } while (level>0);\n    \n    return true;\n}\n\n/** @brief Skips comments\n *  @param[in]  l    the lexer\n *  @param[out] tok  token record to fill out (if necessary)\n *  @param[out] err  error struct to fill out on errors\n *  @returns true on success, false if an error occurs */\nbool lex_skipcomment(lexer *l, token *tok, error *err) {\n    char c = lex_peekahead(l, 1);\n    if (c == '/') {\n        while (lex_peek(l) != '\\n' && !lex_isatend(l)) lex_advance(l);\n        return true;\n    } else if (c == '*') {\n        return lex_skipmultilinecomment(l, tok, err);\n    }\n    return false;\n}\n\n/** @brief Detect and skip a shebang line\n *  @param[in]  l    the lexer\n *  @param[out] tok  token record to fill out (if necessary)\n *  @param[out] err  error struct to fill out on errors\n *  @returns true on success, false if an error occurs */\nbool lex_skipshebang(lexer *l) {\n    if (lex_peek(l)=='#' && lex_peekahead(l, 1)=='!') {\n        while (lex_peek(l) != '\\n' && !lex_isatend(l)) lex_advance(l);\n    }\n    return true;\n}\n\n/** @brief Skips whitespace\n *  @param[in]  l    the lexer\n *  @param[out] tok  token record to fill out (if necessary)\n *  @param[out] err  error struct to fill out on errors\n *  @returns true on success, false if an error occurs */\nbool lex_skipwhitespace(lexer *l, token *tok, error *err) {\n    do {\n        switch (lex_peek(l)) {\n            case ' ':\n            case '\\t':\n            case '\\r':\n                lex_advance(l);\n                break;\n            case '/':\n                if (!lex_skipcomment(l, tok, err)) return true;\n                break;\n            default:\n                return true;\n        }\n    } while (true);\n    return true;\n}\n\n/** @brief Lex strings\n *  @param[in]  l    the lexer\n *  @param[out] tok  token record to fill out\n *  @param[out] err  error struct to fill out on errors\n *  @returns true on success, false if an error occurs */\nbool lex_string(lexer *l, token *tok, error *err) {\n    unsigned int startline = l->line, startpsn = l->posn;\n    \n    char first = lex_peekprevious(l);\n    \n    while (lex_peek(l) != '\"' && !lex_isatend(l)) {\n        if (lex_peek(l) == '\\n') lex_newline(l);\n        \n        /* Detect string interpolation */\n        if (l->stringinterpolation && lex_peek(l) == '$' && lex_peekahead(l, 1) == '{') {\n            lex_advance(l); lex_advance(l);\n            lex_recordtoken(l, TOKEN_INTERPOLATION, tok);\n            if (first=='\"') l->interpolationlevel++;\n            return true;\n        }\n\n        /* Detect an escaped character */\n        if (lex_peek(l)=='\\\\') {\n            lex_advance(l);\n        }\n        \n        lex_advance(l);\n    }\n    \n    if (lex_isatend(l)) {\n        /* Unterminated string */\n        morpho_writeerrorwithid(err, LEXER_UNTERMINATEDSTRING, NULL, startline, startpsn);\n        lex_recordtoken(l, TOKEN_INCOMPLETE, tok);\n        return false;\n    }\n    \n    lex_advance(l); /* Closing quote */\n    \n    if (l->stringinterpolation && l->interpolationlevel>0 && first=='}') l->interpolationlevel--;\n    \n    lex_recordtoken(l, TOKEN_STRING, tok);\n    return true;\n}\n\n/** @brief Lex numbers\n *  @param[in]  l    the lexer\n *  @param[out] tok  token record to fill out\n *  @param[out] err  error struct to fill out on errors\n *  @returns true on success, false if an error occurs */\nbool lex_number(lexer *l, token *tok, error *err) {\n    tokentype type=l->inttype;\n    while (lex_isdigit(lex_peek(l))) lex_advance(l);\n    \n    /* Fractional part */\n    char next = '\\0';\n    if (lex_peek(l)!='\\0') next=lex_peekahead(l, 1); // Prevent looking beyond buffer\n    if (lex_peek(l) == '.' && (lex_isdigit(next) || lex_isspace(next) || next=='\\0') ) {\n        type=l->flttype;\n        lex_advance(l); /* Consume the '.' */\n        while (lex_isdigit(lex_peek(l))) lex_advance(l);\n    }\n    \n    /* Exponent */\n    if (lex_peek(l) == 'e' || lex_peek(l) == 'E') {\n        type=l->flttype;\n        lex_advance(l); /* Consume the 'e' */\n        \n        /* Optional sign */\n        if (lex_peek(l) == '+' || lex_peek(l) == '-') lex_advance(l);\n        \n        /* Exponent digits */\n        while (lex_isdigit(lex_peek(l))) lex_advance(l);\n    }\n    \n    /* Imaginary Numbers */\n    if (lex_peek(l) =='i' && lex_peekahead(l, 1) == 'm'){\n        /* mark this as an imaginary number*/\n        type = l->imagtype;\n        lex_advance(l); /* Consume the 'i' */\n        lex_advance(l); /* Consume the 'm' */\n    }\n    \n    lex_recordtoken(l, type, tok);\n    \n    return true;\n}\n\n/** @brief Checks whether a symbol matches a given string, and if so records a token\n *  @param[in]  l      the lexer\n *  @param[in]  start  offset to start comparing from\n *  @param[in]  length length to compare\n *  @param[in]  match  string to match with\n *  @param[in]  type   token type to use if the match is successful\n *  @returns type or symboltype if the match was not successful */\ntokentype lex_checksymbol(lexer *l, int start, int length, char *match, tokentype type) {\n    int toklength = (int) (l->current - l->start);\n    int expectedlength = start + length;\n    \n    /* Compare, but don't bother calling memcmp if the lengths are different */\n    if ((toklength == expectedlength) && (memcmp(l->start+start, match, length) == 0))\n            return type;\n    \n    return l->symboltype;\n}\n\ntokentype lex_typeforsymboltoken(lexer *l) {\n    tokentype t = l->symboltype;\n    tokendefn *def;\n    \n    if (lex_matchtoken(l, &def)) t = def->type;\n    \n    return t;\n}\n\n/** @brief Lex symbols\n *  @param[in]  l    the lexer\n *  @param[out] tok  token record to fill out\n *  @param[out] err  error struct to fill out on errors\n *  @returns true on success, false if an error occurs */\nbool lex_symbol(lexer *l, token *tok, error *err) {\n    while (lex_isalpha(lex_peek(l)) || lex_isdigit(lex_peek(l))) lex_advance(l);\n    \n    tokentype typ = l->symboltype;\n    if (l->matchkeywords) typ = lex_typeforsymboltoken(l);\n    \n    lex_recordtoken(l, typ, tok);\n    \n    return true;\n}\n\n/* -------------------------------------------------------\n * Morpho preprocessing functions\n * ------------------------------------------------------- */\n\n/** @brief Process function for newline tokens */\nbool lex_preprocess(lexer *l, token *tok, error *err) {\n    char c = lex_peek(l);\n    if (lex_isalpha(c)) return lex_symbol(l, tok, err);\n    if (lex_isdigit(c)) return lex_number(l, tok, err);\n    return false;\n}\n\n/** @brief Process function for newline tokens */\nbool lex_processnewline(lexer *l, token *tok, error *err) {\n    lex_newline(l);\n    return true;\n}\n\n/** @brief Process function for interpolation tokens */\nbool lex_processinterpolation(lexer *l, token *tok, error *err) {\n    if (l->stringinterpolation && l->interpolationlevel>0) {\n        return lex_string(l, tok, err);\n    }\n    return true;\n}\n\n/* **********************************************************************\n * Initialize/clear a lexer\n * ********************************************************************** */\n\n/** @brief Initializes a lexer with a given starting point\n *  @param l     The lexer to initialize\n *  @param start Starting point to lex from\n *  @param line  The current line number */\nvoid lex_init(lexer *l, const char *start, int line) {\n    l->current=start;\n    l->start=start;\n    l->line=line;\n    l->posn=0;\n    l->matchkeywords=true;\n    l->stringinterpolation=true;\n    l->interpolationlevel=0;\n    l->prefn=lex_preprocess;\n    l->whitespacefn=lex_skipwhitespace;\n    l->eoftype=TOKEN_EOF;\n    l->inttype=TOKEN_INTEGER;\n    l->flttype=TOKEN_NUMBER;\n    l->imagtype=TOKEN_IMAG;\n    l->symboltype=TOKEN_SYMBOL;\n    l->defns=standardtokens;   // Use the standard morpho tokens by default\n    l->ndefns=nstandardtokens;\n    varray_tokendefninit(&l->defnstore); // Alternative definitions will be held here\n}\n\n/** @brief Clears a lexer */\nvoid lex_clear(lexer *l) {\n    l->current=NULL;\n    l->start=NULL;\n    l->posn=0;\n    l->interpolationlevel=0;\n    varray_tokendefnclear(&l->defnstore);\n}\n\n/* **********************************************************************\n * Configure the lexer\n * ********************************************************************** */\n\n/** Sets the lexer to use a specific set of token definitions\n * @param[in]  l    the lexer\n * @param[out] defns  List of token definitons, terminated by a null or null length string\n * @warning: The lexer does not duplicate the token definition strings, so these should be preserved. */\nvoid lex_settokendefns(lexer *l, tokendefn *defns) {\n    int n;\n    for (n=0; ; n++) if (defns[n].string == NULL || strlen(defns[n].string)==0) break;\n    \n    l->defnstore.count=0;\n    varray_tokendefnadd(&l->defnstore, defns, n);\n    \n    l->defns=l->defnstore.data;\n    l->ndefns=n;\n    \n    qsort(l->defns, l->ndefns, sizeof(tokendefn), _lex_tokndefncmp);\n}\n\n/** @brief Sets the token type representing End Of File */\nvoid lex_seteof(lexer *l, tokentype eoftype) {\n    l->eoftype = eoftype;\n}\n\n/** @brief Gets the token type representing End Of File */\ntokentype lex_eof(lexer *l) {\n    return l->eoftype;\n}\n\n/** @brief Sets the token type representing integers, floats and complex */\nvoid lex_setnumbertype(lexer *l, tokentype inttype, tokentype flttype, tokentype imagtype) {\n    l->inttype=inttype;\n    l->flttype=flttype;\n    l->imagtype=imagtype;\n}\n\n/** @brief Sets the token type representing symbols */\nvoid lex_setsymboltype(lexer *l, tokentype symboltype) {\n    l->symboltype=symboltype;\n}\n\n/** @brief Gets the token type representing symbols */\ntokentype lex_symboltype(lexer *l) {\n    return l->symboltype;\n}\n\n/** @brief Choose whether the lexer should perform string interpolation. */\nvoid lex_setstringinterpolation(lexer *l, bool interpolation) {\n    l->stringinterpolation=interpolation;\n}\n\n/** @brief Choose whether the lexer should attempt to match keywords or simply return them as symbols. */\nvoid lex_setmatchkeywords(lexer *l, bool match) {\n    l->matchkeywords=match;\n}\n\n/** @brief Choose whether the lexer should attempt to match keywords or simply return them as symbols. */\nbool lex_matchkeywords(lexer *l) {\n    return l->matchkeywords;\n};\n\n/** @brief Provide a processing function to skip whitespace and comments. */\nvoid lex_setwhitespacefn(lexer *l, processtokenfn whitespacefn) {\n    l->whitespacefn = whitespacefn;\n}\n\n/** @brief Provide a processing function to identify tokens prior to matching. */\nvoid lex_setprefn(lexer *l, processtokenfn prefn) {\n    l->prefn = prefn;\n}\n\n/* **********************************************************************\n * Lexer public interface\n * ********************************************************************** */\n\n/** @brief Checks if a token contains a keyword */\nbool lex_tokeniskeyword(lexer *l, token *tok) {\n    if (tok->type==TOKEN_SYMBOL) return false;\n    return lex_isalpha(tok->start[0]);\n}\n\n/** @brief Identifies the next token\n *  @param[in]  l     The lexer in use\n *  @param[out] err   An error block to fill out on an error\n *  @param[out] tok   Token structure to fill out\n *  @returns true on success or false on failure  */\nbool lex(lexer *l, token *tok, error *err) {\n    bool success=false;\n    \n    // Handle leading whitespace\n    if (l->whitespacefn) {\n        if (!((l->whitespacefn) (l, tok, err))) return false;\n    }\n    \n    // Set beginning of the token\n    l->start=l->current;\n    \n    // Check whether we're at the end of the source string\n    if (lex_isatend(l)) {\n        lex_recordtoken(l, l->eoftype, tok);\n        return true;\n    }\n    \n    // If the lexer has a prefn, call that and check whether it handled the token.\n    if (l->prefn) {\n        success=(l->prefn) (l, tok, err);\n        if (err->cat!=ERROR_NONE) return false; // It raised an error, so should return\n        if (success) return true;\n    }\n    \n    tokendefn *defn=NULL;\n    if (lex_identifytoken(l, &defn)) {\n        lex_recordtoken(l, defn->type, tok);\n    } else {\n        morpho_writeerrorwithid(err, LEXER_UNRECOGNIZEDTOKEN, NULL, l->line, l->posn);\n        return false;\n    }\n    \n    // If the token type provides a process function, call it\n    if (defn->processfn) return (defn->processfn) (l, tok, err);\n\n    return true;\n}\n\n/* **********************************************************************\n * Initialization/finalization\n * ********************************************************************** */\n\n/** @brief Initialization/finalization */\nvoid lex_initialize(void) {\n    // Ensure standardtokens is sorted; this is then used by default to reduce cost of initializing a lexer.\n    int n;\n    for (n=0; ; n++) if (standardtokens[n].string == NULL || strlen(standardtokens[n].string)==0) break;\n    qsort(standardtokens, n, sizeof(tokendefn), _lex_tokndefncmp);\n    \n    // Retain the number of standardtokens\n    nstandardtokens = n;\n    \n    /* Lexer errors */\n    morpho_defineerror(LEXER_UNRECOGNIZEDTOKEN, ERROR_LEX, LEXER_UNRECOGNIZEDTOKEN_MSG);\n    morpho_defineerror(LEXER_UNTERMINATEDCOMMENT, ERROR_LEX, LEXER_UNTERMINATEDCOMMENT_MSG);\n    morpho_defineerror(LEXER_UNTERMINATEDSTRING, ERROR_LEX, LEXER_UNTERMINATEDSTRING_MSG);\n}\n"
  },
  {
    "path": "src/support/lex.h",
    "content": "/** @file lex.h\n *  @author T J Atherton and others (see below)\n *\n *  @brief Lexer \n*/\n\n#ifndef lex_h\n#define lex_h\n\n#include <stdio.h>\n#include \"varray.h\"\n#include \"error.h\"\n\n/** The lexer breaks an input stream into tokens, classifying them as it goes. */\n\ntypedef struct slexer lexer;\n\n/* -------------------------------------------------------\n * Tokens\n * ------------------------------------------------------- */\n\n#define TOKEN_NONE -1\n\n/** Token types are left as a generic int to facilitate reprogrammability in the future */\ntypedef int tokentype;\n\n/** A token */\ntypedef struct {\n    tokentype type; /** Type of the token */\n    const char *start; /** Start of the token */\n    unsigned int length; /** Its length */\n    /* Position of the token in the source */\n    int line; /** Source line */\n    int posn; /** Character position of the end of the token */\n} token;\n\n/** Literal for a blank token */\n#define TOKEN_BLANK ((token) {.type=TOKEN_NONE, .start=NULL, .length=0, .line=0, .posn=0} )\n\n/* -------------------------------------------------------\n * Token processing functions\n * ------------------------------------------------------- */\n\n/** Token processing functions are called after recording it. */\ntypedef bool (* processtokenfn) (lexer *l, token *tok, error *err);\n\n/* -------------------------------------------------------\n * Token definitions\n * ------------------------------------------------------- */\n\ntypedef struct {\n    char *string; // String defining the token\n    tokentype type; // Token type\n    processtokenfn processfn; // Optional processfunction to call\n} tokendefn;\n\nDECLARE_VARRAY(tokendefn, tokendefn);\n\n/* -------------------------------------------------------\n * Lexer data structure\n * ------------------------------------------------------- */\n\n/** @brief Store the current configuration of a lexer */\nstruct slexer {\n    const char* start; /** Starting point to lex */\n    const char* current; /** Current point */\n    int line; /** Line number */\n    int posn; /** Character position in line */\n    \n    bool matchkeywords; /** Whether to match keywords or not; default is true */\n    bool stringinterpolation; /** Whether to perform string interpolation */\n    processtokenfn whitespacefn; /** Called to skip whitespace */\n    processtokenfn prefn; /** Called before attempting to match the token list */\n    \n    tokentype eoftype; /** End of file marker */\n    tokentype inttype; /** Integers */\n    tokentype flttype; /** Floats */\n    tokentype imagtype; /** Imaginary numbers */\n    tokentype symboltype; /** Symbol numbers */\n    \n    int interpolationlevel; /** Level of string interpolation */\n    \n    tokendefn *defns; /** Pointer to token defintions in use */\n    int ndefns; /** Number of token defintions in use */\n    \n    varray_tokendefn defnstore; /** Used to hold custom tokens */\n} ;\n\n/* -------------------------------------------------------\n * Morpho token types\n * ------------------------------------------------------- */\n\n/** Enum listing standard token types. Each token will be mapped to a parserule in the parser */\nenum {\n    /* New line */\n    TOKEN_NEWLINE,\n    \n    /* Question mark */\n    TOKEN_QUESTION,\n    \n    /* Literals */\n    TOKEN_STRING,\n    TOKEN_INTERPOLATION,\n    TOKEN_INTEGER,\n    TOKEN_NUMBER,\n    TOKEN_SYMBOL,\n    \n    /* Brackets */\n    TOKEN_LEFTPAREN, TOKEN_RIGHTPAREN,\n    TOKEN_LEFTSQBRACKET, TOKEN_RIGHTSQBRACKET,\n    TOKEN_LEFTCURLYBRACKET, TOKEN_RIGHTCURLYBRACKET,\n    \n    /* Delimiters */\n    TOKEN_COLON, TOKEN_SEMICOLON, TOKEN_COMMA,\n    \n    /* Operators */\n    TOKEN_PLUS, TOKEN_MINUS, TOKEN_STAR, TOKEN_SLASH, TOKEN_CIRCUMFLEX,\n    TOKEN_PLUSPLUS, TOKEN_MINUSMINUS,\n    TOKEN_PLUSEQ, TOKEN_MINUSEQ, TOKEN_STAREQ, TOKEN_SLASHEQ,\n    TOKEN_HASH,\n    TOKEN_AT,\n    \n    /* Other symbols */\n    TOKEN_QUOTE,\n    TOKEN_DOT,\n    TOKEN_DOTDOT,\n    TOKEN_DOTDOTDOT,\n    TOKEN_EXCLAMATION, TOKEN_AMP, TOKEN_VBAR, TOKEN_DBLAMP, TOKEN_DBLVBAR,\n    TOKEN_EQUAL,\n    TOKEN_EQ, TOKEN_NEQ,\n    TOKEN_LT, TOKEN_GT,\n    TOKEN_LTEQ, TOKEN_GTEQ,\n    \n    /* Keywords */\n    TOKEN_TRUE, TOKEN_FALSE, TOKEN_NIL,\n    TOKEN_SELF, TOKEN_SUPER, TOKEN_IMAG,\n    TOKEN_PRINT, TOKEN_VAR,\n    TOKEN_IF, TOKEN_ELSE, TOKEN_IN,\n    TOKEN_WHILE, TOKEN_FOR, TOKEN_DO, TOKEN_BREAK, TOKEN_CONTINUE,\n    TOKEN_FUNCTION, TOKEN_RETURN, TOKEN_CLASS,\n    TOKEN_IMPORT, TOKEN_AS, TOKEN_IS, TOKEN_WITH,\n    TOKEN_TRY, TOKEN_CATCH,\n    \n    /* Shebangs at start of script */\n    TOKEN_SHEBANG,\n    \n    /* Errors and other statuses */\n    TOKEN_INCOMPLETE,\n    TOKEN_EOF\n};\n\n/* -------------------------------------------------------\n * Lex error messages\n * ------------------------------------------------------- */\n\n#define LEXER_UNRECOGNIZEDTOKEN         \"UnrgnzdTkn\"\n#define LEXER_UNRECOGNIZEDTOKEN_MSG     \"Unrecognized token.\"\n\n#define LEXER_UNTERMINATEDCOMMENT       \"UntrmComm\"\n#define LEXER_UNTERMINATEDCOMMENT_MSG   \"Unterminated multiline comment '/*'.\"\n\n#define LEXER_UNTERMINATEDSTRING        \"UntrmStrng\"\n#define LEXER_UNTERMINATEDSTRING_MSG    \"Unterminated string.\"\n\n/* -------------------------------------------------------\n * Library functions to support customizable lexers\n * ------------------------------------------------------- */\n\nbool lex_findtoken(lexer *l, tokendefn **defn);\nbool lex_matchtoken(lexer *l, tokendefn **defn);\nvoid lex_recordtoken(lexer *l, tokentype type, token *tok);\nchar lex_advance(lexer *l);\nbool lex_back(lexer *l);\nbool lex_isatend(lexer *l);\nbool lex_isalpha(char c);\nbool lex_isdigit(char c);\nbool lex_isspace(char c);\nchar lex_peek(lexer *l);\nchar lex_peekahead(lexer *l, int n);\nchar lex_peekprevious(lexer *l);\nvoid lex_newline(lexer *l);\nbool lex_skipshebang(lexer *l);\n\n/* -------------------------------------------------------\n * Lex interface\n * ------------------------------------------------------- */\n\n// Initialize and clear a lexer structure\nvoid lex_init(lexer *l, const char *start, int line);\nvoid lex_clear(lexer *l);\n\n// Configure lexer\nvoid lex_settokendefns(lexer *l, tokendefn *defns);\nvoid lex_seteof(lexer *l, tokentype eoftype);\ntokentype lex_eof(lexer *l);\nvoid lex_setnumbertype(lexer *l, tokentype inttype, tokentype flttype, tokentype imagtype);\nvoid lex_setsymboltype(lexer *l, tokentype symboltype);\ntokentype lex_symboltype(lexer *l);\nvoid lex_setstringinterpolation(lexer *l, bool interpolation);\nvoid lex_setmatchkeywords(lexer *l, bool match);\nbool lex_matchkeywords(lexer *l);\nvoid lex_setwhitespacefn(lexer *l, processtokenfn whitespacefn);\nvoid lex_setprefn(lexer *l, processtokenfn prefn);\n\n// Get information about a token\nbool lex_tokeniskeyword(lexer *l, token *tok);\n\n// Obtain the next token\nbool lex(lexer *l, token *tok, error *err);\n\n// Initialization/finalization\nvoid lex_initialize(void);\nvoid lex_finalize(void);\n\n#endif /* lex_h */\n"
  },
  {
    "path": "src/support/memory.c",
    "content": "/** @file memory.c\n *  @author T J Atherton\n *\n *  @brief Morpho memory allocator\n*/\n\n#include \"memory.h\"\n\n/** @brief Generic allocator function\n *  @param old      A previously allocated pointer, or NULL to allocate new memory\n *  @param oldsize  The previously allocated size\n *  @param newsize  New size to allocate\n *  @returns A pointer to allocated memory, or NULL on failure.\n */\nvoid *morpho_allocate(void *old, size_t oldsize, size_t newsize) {\n    if (newsize == 0) {\n        free(old);\n        return NULL;\n    }\n\n    return realloc(old, newsize);\n}\n"
  },
  {
    "path": "src/support/memory.h",
    "content": "/** @file memory.h\n *  @author T J Atherton\n *\n *  @brief Morpho memory allocator\n*/\n\n#ifndef memory_h\n#define memory_h\n\n#include <stdlib.h>\n\n/** Macro to redirect malloc through our memory management */\n#define MORPHO_MALLOC(size) morpho_allocate(NULL, 0, size)\n\n/** Macro to redirect free through our memory management */\n#define MORPHO_FREE(x) morpho_allocate(x, 0, 0)\n\n/** Macro to redirect realloc through our memory management */\n#define MORPHO_REALLOC(x, size) morpho_allocate(x, 0, size)\n\nvoid *morpho_allocate(void *old, size_t oldsize, size_t newsize);\n\n#endif /* memory_h */\n"
  },
  {
    "path": "src/support/parse.c",
    "content": "/** @file parse.c\n *  @author T J Atherton \n *\n *  @brief Parser\n*/\n\n#include <string.h>\n#include <float.h>\n#include <limits.h>\n#include <errno.h>\n#include <ctype.h>\n#include \"parse.h\"\n#include \"object.h\"\n#include \"common.h\"\n#include \"cmplx.h\"\n#include \"syntaxtree.h\"\n\n/** Varrays of parse rules */\nDEFINE_VARRAY(parserule, parserule)\n\n/** Macro to check return of a bool function */\n#define PARSE_CHECK(f) if (!(f)) return false;\n\n/* **********************************************************************\n * Parser utility functions\n * ********************************************************************** */\n\n/** @brief Fills out the error record\n *  @param p        the parser\n *  @param use_prev use the previous token? [this is the more typical usage]\n *  @param id       error id\n *  @param ...      additional data for sprintf. */\nvoid parse_error(parser *p, bool use_prev, errorid id, ... ) {\n    va_list args;\n    token *tok = (use_prev ? &p->previous : &p->current);\n    \n    /** Only return the first error that occurs */\n    if (ERROR_FAILED(*p->err)) return;\n    \n    va_start(args, id);\n    morpho_writeerrorwithid(p->err, id, NULL, tok->line, tok->posn, args);\n    va_end(args);\n}\n\n/** @brief Advance the parser by one token\n *  @param   p the parser in use.\n *  @returns true on success, false otherwise */\nbool parse_advance(parser *p) {\n    lexer *l = p->lex;\n    \n    p->previous=p->current;\n    p->nl=false;\n    \n    for (;;) {\n        if (!lex(l, &p->current, p->err)) return false;\n        \n        /* Skip any newlines encountered */\n        if (p->skipnewline && p->current.type==p->toknewline) {\n            p->nl=true;\n            continue;\n        } else break;\n    }\n    \n    return ERROR_SUCCEEDED(*p->err);\n}\n\n/** Saves the state of the parser and attached lexer */\nvoid parse_savestate(parser *p, parser *op, lexer *ol) {\n    *ol = *p->lex; // Save the state of the parser and lexer\n    *op = *p;\n}\n\n/** Restores the parser from a saved position.\n @warning: You must take care to ensure no new objects have been allocated prior to calling this. */\nvoid parse_restorestate(parser *op, lexer *ol, parser *out) {\n    *out = *op;\n    *out->lex = *ol;\n}\n\n/** @brief Continues parsing while tokens have a lower or equal precendece than a specified value.\n *  @param   p    the parser in use\n *  @param   precendence precedence value to keep below or equal to\n *  @returns syntaxtreeindx for the expression parsed */\nbool parse_precedence(parser *p, precedence prec, void *out) {\n    parsefunction prefixrule=NULL, infixrule=NULL;\n    \n    PARSE_CHECK(parse_advance(p));\n    \n    parserule *rule = parse_getrule(p, p->previous.type);\n    if (rule) prefixrule = rule->prefix;\n    \n    if (!rule || !prefixrule) {\n        parse_error(p, true, PARSE_EXPECTEXPRESSION);\n        return false;\n    }\n    \n    PARSE_CHECK(prefixrule(p, out));\n    \n    /* Now keep parsing while the tokens have lower precedence */\n    rule=parse_getrule(p, p->current.type);\n    while (rule!=NULL && prec <= rule->precedence) {\n        /* Break if a newline is encountered before a function call */\n        if (p->current.type==TOKEN_LEFTPAREN && p->nl) break;\n        \n        PARSE_CHECK(parse_advance(p));\n        \n        infixrule = parse_getrule(p, p->previous.type)->infix;\n        if (infixrule) {\n            PARSE_CHECK(infixrule(p, out))\n        } else UNREACHABLE(\"No infix rule defined for this token type [check parser definition table].\");\n        \n        rule=parse_getrule(p, p->current.type);\n    }\n\n    return true;\n}\n\n/** Checks whether the current token matches a specified tokentype */\nbool parse_checktoken(parser *p, tokentype type) {\n    return p->current.type==type;\n}\n\n/** Checks whether the current token matches any of the specified tokentypes */\nbool parse_checktokenmulti(parser *p, int n, tokentype *type) {\n    for (int i=0; i<n; i++) {\n        if (p->current.type==type[i]) return true;\n    }\n    \n    return false;\n}\n\n/** Checks whether the current token matches a given type and advances if so. */\nbool parse_checktokenadvance(parser *p, tokentype type) {\n    PARSE_CHECK(parse_checktoken(p, type));\n    PARSE_CHECK(parse_advance(p));\n    return true;\n}\n\n/** Checks whether the current token is a keyword */\nbool parse_checktokeniskeywordadvance(parser *p) {\n    PARSE_CHECK(lex_tokeniskeyword(p->lex, &p->current));\n    PARSE_CHECK(parse_advance(p));\n    return true;\n}\n\n/** @brief Checks if the next token has the required type and advance if it does, otherwise generates an error.\n *  @param   p    the parser in use\n *  @param   type type to check\n *  @param   id   error id to generate if the token doesn't match\n *  @returns true on success */\nbool parse_checkrequiredtoken(parser *p, tokentype type, errorid id) {\n    if (parse_checktoken(p, type)) {\n        PARSE_CHECK(parse_advance(p));\n        return true;\n    }\n        \n    if (id!=ERROR_NONE) parse_error(p, false, id);\n    return false;\n}\n\n/** @brief Checks if the next token has a specific type and if it does generates an error.\n *  @param   p    the parser in use\n *  @param   type type to check\n *  @param   id   error id to generate if the token is found doesn't match\n *  @returns true if the disallowed token was found */\nbool parse_checkdisallowedtoken(parser *p, tokentype type, errorid id) {\n    if (parse_checktoken(p, type)) {\n        parse_error(p, true, id);\n        return true;\n    }\n    return false;\n}\n\n/** Converts a hex string to a character code, outputting it into a varray\n    @param[in] p - the current parser\n    @param[in] codestr - code string to parse\n    @param[in] nhex - number of hex characters to parse\n    @param[in] raw - returns a raw ascii byte, rather than the utf8 encoded character\n    @param[out] out - characters are added\n    @returns true on success, false on failure */\nbool parse_codepointfromhex(parser *p, const char *codestr, int nhex, bool raw, varray_char *out) {\n    char in[nhex+1];\n    \n    for (int j=0; j<nhex; j++) {\n        if (isxdigit(codestr[j])) {\n            in[j]=codestr[j];\n        } else {\n            parse_error(p, true, PARSE_INVLDUNCD);\n            return false;\n        }\n    }\n    in[nhex]='\\0';\n    \n    long codept = strtol(in, NULL, 16);\n    char buffer[4];\n    int nchars=1;\n    if (!raw) {\n        nchars = morpho_encodeutf8((int) codept, buffer);\n    } else {\n        buffer[0] = (char) codept;\n    }\n    \n    if (!varray_charadd(out, buffer, nchars)) {\n        parse_error(p, true, ERROR_ALLOCATIONFAILED);\n        return false;\n    }\n    return true;\n}\n\n/** Turn a token into a string, parsing escape characters */\nbool parse_stringfromtoken(parser *p, unsigned int start, unsigned int length, value *out) {\n    bool success=false;\n    const char *input=p->previous.start;\n    varray_char str;\n    varray_charinit(&str);\n    \n    for (unsigned int i=start, nbytes; i<length; i+=nbytes) {\n        nbytes=morpho_utf8numberofbytes(&input[i]);\n        if (!nbytes) return false;\n        \n        if (nbytes>1) { // Unicode literals\n            varray_charadd(&str, (char *) &input[i], nbytes);\n        } else if (input[i]=='\\n') { // Newlines are ok \n            varray_charwrite(&str, input[i]);\n        } else if (iscntrl((unsigned char) input[i])) { // Unescaped control codes are not\n            parse_error(p, true, PARSE_UNESCPDCTRL);\n            goto parse_stringfromtokencleanup;\n        } else if (nbytes==1 && input[i]=='\\\\') { // Escape sequence\n            i++;\n            switch (input[i]) {\n                case 'b': varray_charwrite(&str, '\\b'); break;\n                case 'f': varray_charwrite(&str, '\\f'); break;\n                case 'n': varray_charwrite(&str, '\\n'); break;\n                case 'r': varray_charwrite(&str, '\\r'); break;\n                case 't': varray_charwrite(&str, '\\t'); break;\n                case 'u':\n                    if (!parse_codepointfromhex(p, &input[i+1], 4, false, &str)) goto parse_stringfromtokencleanup;\n                    i+=4;\n                    break;\n                case 'U':\n                    if (!parse_codepointfromhex(p, &input[i+1], 8, false, &str)) goto parse_stringfromtokencleanup;\n                    i+=8;\n                    break;\n                case 'x':\n                    if (!parse_codepointfromhex(p, &input[i+1], 2, true, &str)) goto parse_stringfromtokencleanup;\n                    i+=2;\n                    break;\n                default:\n                    varray_charwrite(&str, input[i]); break;\n            }\n        } else varray_charwrite(&str, input[i]); // Any other single character\n    }\n    \n    success=true;\n    if (out) {\n        *out = object_stringfromvarraychar(&str);\n        if (!(MORPHO_ISSTRING(*out))) parse_error(p, true, ERROR_ALLOCATIONFAILED);\n    }\n    \nparse_stringfromtokencleanup:\n    varray_charclear(&str);\n    \n    return success;\n}\n\n/** Parses the previous token into a value with no processing. */\nvalue parse_tokenasstring(parser *p) {\n    value s = object_stringfromcstring(p->previous.start, p->previous.length);\n    if (MORPHO_ISNIL(s)) parse_error(p, true, ERROR_ALLOCATIONFAILED, OBJECT_SYMBOLLABEL);\n\n    return s;\n}\n\n/** Parses the next token as a symbol regardless of whether it is a keyword; returns true on success */\nbool parse_tokenassymbol(parser *p) {\n    bool oldmatch = lex_matchkeywords(p->lex);\n    \n    lex_setmatchkeywords(p->lex, false);\n    bool success=parse_checktokenadvance(p, lex_symboltype(p->lex));\n    lex_setmatchkeywords(p->lex, oldmatch); // Restore state of lexer\n    \n    return success;\n}\n\n/** Adds a node to the syntax tree. */\nbool parse_addnode(parser *p, syntaxtreenodetype type, value content, token *tok, syntaxtreeindx left, syntaxtreeindx right, syntaxtreeindx *out) {\n    syntaxtree *tree = (syntaxtree *) p->out;\n    \n    if (!syntaxtree_addnode(tree, type, content, tok->line, tok->posn, left, right, out)) {\n        parse_error(p, true, ERROR_ALLOCATIONFAILED);\n        return false;\n    }\n    \n    p->left=*out; /* Record this for a future infix operator to catch */\n    \n    return true;\n}\n\n/** Retrieve a syntaxtree node from an index. */\nsyntaxtreenode *parse_lookupnode(parser *p, syntaxtreeindx i) {\n    return syntaxtree_nodefromindx((syntaxtree *) p->out, i);\n}\n\n/** Checks whether a long created by strtol is within range. Raises a parse error if not and returns false. */\nbool parse_validatestrtol(parser *p, long f) {\n    if ( ((f==LONG_MAX || f==LONG_MIN) && errno==ERANGE) || // Check for underflow or overflow\n        f>INT_MAX || f<INT_MIN) {\n        parse_error(p, true, PARSE_VALRANGE);\n        return false;\n    }\n    return true;\n}\n\n/** Checks whether a double created by strtod is within range. Raises a parse error if not and returns false. */\nbool parse_validatestrtod(parser *p, double f) {\n    if ( errno==ERANGE && (f==HUGE_VAL || f==-HUGE_VAL || f==DBL_MIN) ) {\n        parse_error(p, true, PARSE_VALRANGE);\n        return false;\n    }\n    return true;\n}\n\n/** Converts a token to an integer, returning true on success */\nbool parse_tokentointeger(parser *p, long *i) {\n    *i = strtol(p->previous.start, NULL, 10);\n    return parse_validatestrtol(p, *i);\n}\n\n/** Converts an token to a double, returning true on success */\nbool parse_tokentodouble(parser *p, double *x) {\n    *x = strtod(p->previous.start, NULL);\n    return parse_validatestrtod(p, *x);\n}\n\n/** Increments the recursion depth counter. If it exceeds PARSE_RECURSIONLIMIT an error is generated */\nbool parse_incrementrecursiondepth(parser *p) {\n    if (!(p->recursiondepth<p->maxrecursiondepth)) {\n        parse_error(p, false, PARSE_RCRSNLMT);\n        return false;\n    }\n        \n    p->recursiondepth++;\n    return true;\n}\n\n/** Decrements the recursion depth counter. */\nbool parse_decrementrecursiondepth(parser *p) {\n    if (p->recursiondepth>0) p->recursiondepth--;\n    return false;\n}\n\n/** Adds an object to the parser */\nvoid parse_addobject(parser *p, value obj) {\n    varray_valuewrite(&p->objects, obj);\n}\n\n/** Frees objects generated by the parser */\nvoid parse_freeobjects(parser *p) {\n    for (unsigned int i=0; i<p->objects.count; i++) morpho_freeobject(p->objects.data[i]);\n}\n\n/** Clears the object list */\nvoid parse_clearobjects(parser *p) {\n    varray_valueclear(&p->objects);\n}\n\n/* ------------------------------------------\n * Parser implementation functions (parselets)\n * ------------------------------------------- */\n\nbool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, void *out);\nbool parse_variable(parser *p, errorid id, void *out);\nbool parse_statementterminator(parser *p);\nbool parse_checkstatementterminator(parser *p);\nbool parse_synchronize(parser *p);\n\n/* ------------------------------------------\n * Utility functions for this parser\n * ------------------------------------------- */\n\n/** @brief Parses a list of expressions\n * @param[in]  p     the parser\n * @param[in]  rightdelimiter  token type that denotes the end of the arguments list\n * @param[out] nel the number of elements\n * @details Note that the arguments are output in reverse order, i.e. the\n *          first argument is deepest in the tree. */\nbool parse_expressionlist(parser *p, tokentype rightdelimiter, unsigned int *nel, void *out) {\n    syntaxtreeindx prev=SYNTAXTREE_UNCONNECTED, current=SYNTAXTREE_UNCONNECTED;\n    token start = p->current;\n    unsigned int n=0;\n    \n    if (!parse_checktoken(p, rightdelimiter)) {\n        do {\n            PARSE_CHECK(parse_pseudoexpression(p, &current));\n            PARSE_CHECK(parse_addnode(p, NODE_ARGLIST, MORPHO_NIL, &start, prev, current, &current));\n            prev = current;\n            n++;\n        } while (parse_checktokenadvance(p, TOKEN_COMMA));\n    }\n    \n    /* Output the number of args */\n    if (nel) *nel=n;\n    \n    *((syntaxtreeindx *) out)=current;\n    \n    return true;\n}\n\n/** @brief Parses an argument list\n * @param[in]  p     the parser\n * @param[in]  rightdelimiter  token type that denotes the end of the arguments list\n * @param[out] nargs the number of arguments\n * @param[out] out the syntaxtreeindex, updated\n * @returns true on success\n * @details Note that the arguments are output in reverse order, i.e. the\n *          first argument is deepest in the tree. */\nbool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, void *out) {\n    syntaxtreeindx prev=SYNTAXTREE_UNCONNECTED, current=SYNTAXTREE_UNCONNECTED;\n    token start = p->current;\n    unsigned int n=0;\n    bool varg=false;\n    \n    if (!parse_checktoken(p, rightdelimiter)) {\n        do {\n            bool vargthis = false;\n            if (parse_checktokenadvance(p, TOKEN_DOTDOTDOT)) {\n                // If we are trying to index something\n                // then ... represents an open range\n                if (rightdelimiter == TOKEN_RIGHTSQBRACKET) {\n                    \n                } else if (varg) {\n                    parse_error(p, true, PARSE_ONEVARPR);\n                    return false;\n                }\n                varg = true; vargthis = true;\n            }\n            \n            if (parse_checktokenadvance(p, TOKEN_SYMBOL)) {\n                PARSE_CHECK(parse_symbol(p, &current));\n                \n                if (parse_checktokenadvance(p, TOKEN_SYMBOL)) { // If two symbols in a row, then the first is the type\n                    syntaxtreeindx label;\n                    PARSE_CHECK(parse_symbol(p, &label));\n                    \n                    PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, current, label, &current));\n                } else if (parse_checktokenadvance(p, TOKEN_DOT)) { // Symbol followed by dot is a type in a namespace\n                    syntaxtreeindx type, label;\n                    \n                    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, PARSE_SYMBLEXPECTED));\n                    PARSE_CHECK(parse_symbol(p, &type));\n                    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, PARSE_SYMBLEXPECTED));\n                    PARSE_CHECK(parse_symbol(p, &label));\n                    \n                    PARSE_CHECK(parse_addnode(p, NODE_DOT, MORPHO_NIL, &start, current, type, &current));\n                    PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, current, label, &current));\n                } else if (parse_checktokenadvance(p, TOKEN_EQUAL)) { // Symbol followed by equals is an optional argument\n                    syntaxtreeindx val;\n                    PARSE_CHECK(parse_pseudoexpression(p, &val));\n                    \n                    PARSE_CHECK(parse_addnode(p, NODE_ASSIGN, MORPHO_NIL, &start, current, val, &current));\n                }\n            } else PARSE_CHECK(parse_pseudoexpression(p, &current));\n\n            if (vargthis) PARSE_CHECK(parse_addnode(p, NODE_RANGE, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, current, &current));\n            \n            n++;\n            \n            PARSE_CHECK(parse_addnode(p, NODE_ARGLIST, MORPHO_NIL, &start, prev, current, &current));\n            \n            prev = current;\n        } while (parse_checktokenadvance(p, TOKEN_COMMA));\n    }\n    \n    /* Output the number of args */\n    if (nargs) *nargs=n;\n    \n    *((syntaxtreeindx *) out)=current;\n    \n    return true;\n}\n\n/** Parses a variable name, or raises and error if a symbol isn't found */\nbool parse_variable(parser *p, errorid id, void *out) {\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, id));\n    return parse_symbol(p, out);\n}\n\n/** Parses a reference that could be a symbol or a namespace.symbol reference */\nbool parse_reference(parser *p, errorid errid, void *out) {\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, errid));\n    \n    syntaxtreeindx symbol, selector=SYNTAXTREE_UNCONNECTED;\n    PARSE_CHECK(parse_symbol(p, &symbol));\n    \n    if (parse_checktokenadvance(p, TOKEN_DOT)) {\n        PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, errid));\n        PARSE_CHECK(parse_symbol(p, &selector));\n    }\n    \n    if (selector!=SYNTAXTREE_UNCONNECTED) {\n        PARSE_CHECK(parse_addnode(p, NODE_DOT, MORPHO_NIL, &p->previous, symbol, selector, &symbol));\n    }\n    \n    *((syntaxtreeindx *) out)=symbol;\n    \n    return true;\n}\n\n/** Parse a statement terminator  */\nbool parse_statementterminator(parser *p) {\n    if (parse_checktoken(p, TOKEN_SEMICOLON)) {\n        PARSE_CHECK(parse_advance(p));\n    } else if (p->nl || parse_checktoken(p, TOKEN_EOF) || parse_checktoken(p, TOKEN_RIGHTCURLYBRACKET)) {\n    } else if (parse_checktoken(p, TOKEN_IN) || parse_checktoken(p, TOKEN_ELSE)) {\n    } else {\n        parse_error(p, true, PARSE_MISSINGSEMICOLONEXP);\n        return false;\n    }\n    return true;\n}\n\n/** Checks whether a possible statement terminator is next */\nbool parse_checkstatementterminator(parser *p) {\n    return (parse_checktoken(p, TOKEN_SEMICOLON)\n            || (p->nl)\n            || parse_checktoken(p, TOKEN_EOF)\n            || parse_checktoken(p, TOKEN_RIGHTCURLYBRACKET)\n            || parse_checktoken(p, TOKEN_IN)\n            ) ;\n}\n\n/** @brief Keep parsing til the end of a statement boundary. */\nbool parse_synchronize(parser *p) {\n    while (p->current.type!=TOKEN_EOF) {\n        /** Align */\n        if (p->previous.type == TOKEN_SEMICOLON) return true;\n        switch (p->current.type) {\n            case TOKEN_PRINT:\n            case TOKEN_IF:\n            case TOKEN_WHILE:\n            case TOKEN_FOR:\n            case TOKEN_DO:\n            case TOKEN_BREAK:\n            case TOKEN_CONTINUE:\n            case TOKEN_RETURN:\n            case TOKEN_TRY:\n                \n            case TOKEN_CLASS:\n            case TOKEN_FUNCTION:\n            case TOKEN_VAR:\n                return true;\n            default:\n                ;\n        }\n        \n        PARSE_CHECK(parse_advance(p));\n    }\n\n    return true;\n}\n\n/* ------------------------------------------\n * Basic literals\n * ------------------------------------------- */\n\n/** Parses nil */\nbool parse_nil(parser *p, void *out) {\n    return parse_addnode(p, NODE_NIL,\n        MORPHO_NIL, &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses an integer */\nbool parse_integer(parser *p, void *out) {\n    long f;\n    PARSE_CHECK(parse_tokentointeger(p, &f));\n    \n    return parse_addnode(p, NODE_INTEGER, MORPHO_INTEGER(f), &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses a number */\nbool parse_number(parser *p, void *out) {\n    double f;\n    PARSE_CHECK(parse_tokentodouble(p, &f));\n    \n    return parse_addnode(p, NODE_FLOAT, MORPHO_FLOAT(f), &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parse a complex number */\nbool parse_complex(parser *p, void *out) {\n    double f;\n    if (p->previous.length==2) { // just a bare im symbol\n        f = 1;\n    } else {\n        PARSE_CHECK(parse_tokentodouble(p, &f));\n    }\n    value c = MORPHO_OBJECT(object_newcomplex(0,f));\n    parse_addobject(p, c);\n    return parse_addnode(p, NODE_IMAG, c, &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses a bool */\nbool parse_bool(parser *p, void *out) {\n    return parse_addnode(p, NODE_BOOL,\n        MORPHO_BOOL((p->previous.type==TOKEN_TRUE ? true : false)), &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses a self token */\nbool parse_self(parser *p, void *out) {\n    return parse_addnode(p, NODE_SELF, MORPHO_NIL, &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses a super token */\nbool parse_super(parser *p, void *out) {\n    if (!parse_checktoken(p, TOKEN_DOT)) {\n        parse_error(p, false, PARSE_EXPECTDOTAFTERSUPER);\n        return false;\n    }\n\n    return parse_addnode(p, NODE_SUPER, MORPHO_NIL, &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses a symbol */\nbool parse_symbol(parser *p, void *out) {\n    value s = object_stringfromcstring(p->previous.start, p->previous.length);\n    parse_addobject(p, s);\n    if (MORPHO_ISNIL(s)) {\n        parse_error(p, true, ERROR_ALLOCATIONFAILED, OBJECT_SYMBOLLABEL);\n        return false;\n    }\n\n    return parse_addnode(p, NODE_SYMBOL, s, &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parses a string */\nbool parse_string(parser *p, void *out) {\n    value s;\n    PARSE_CHECK(parse_stringfromtoken(p, 1, p->previous.length-1, &s));\n    parse_addobject(p, s);\n    \n    return parse_addnode(p, NODE_STRING, s, &p->previous, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** @brief: Parses a dictionary.\n * @details Dictionaries are a list of key/value pairs,  { key : value, key: value } */\nbool parse_dictionary(parser *p, void *out) {\n    syntaxtreeindx last=SYNTAXTREE_UNCONNECTED;\n    parse_addnode(p, NODE_DICTIONARY, MORPHO_NIL, &p->current, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, &last);\n    \n    while (!parse_checktoken(p, TOKEN_RIGHTCURLYBRACKET) &&\n           !parse_checktoken(p, TOKEN_EOF)) {\n        syntaxtreeindx key, val, pair;\n        token tok=p->current; // Keep track of the token that corresponds to each key/value pair\n        \n        /* Parse the key/value pair */\n        PARSE_CHECK(parse_expression(p, &key));\n        PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_COLON, PARSE_DCTSPRTR));\n        PARSE_CHECK(parse_expression(p, &val));\n        \n        /* Create an entry node */\n        PARSE_CHECK(parse_addnode(p, NODE_DICTENTRY, MORPHO_NIL, &tok, key, val, &pair));\n        \n        /* These are linked into a chain of dictionary nodes */\n        PARSE_CHECK(parse_addnode(p, NODE_DICTIONARY, MORPHO_NIL, &tok, last, pair, &last));\n        \n        if (!parse_checktoken(p, TOKEN_RIGHTCURLYBRACKET)) {\n            PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_COMMA, PARSE_MSSNGCOMMA));\n        }\n    };\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTCURLYBRACKET, PARSE_DCTTRMNTR));\n    \n    *((syntaxtreeindx *) out) = last;\n    \n    return true;\n}\n\n/** Parses a string interpolation. */\nbool parse_interpolation(parser *p, void *out) {\n    token tok = p->previous;\n    \n    /* First copy the string */\n    value s;\n    PARSE_CHECK(parse_stringfromtoken(p, 1, tok.length-2, &s));\n    parse_addobject(p, s);\n    \n    syntaxtreeindx left=SYNTAXTREE_UNCONNECTED, right=SYNTAXTREE_UNCONNECTED;\n    \n    if (!(parse_checktoken(p, TOKEN_STRING) &&\n        *p->current.start=='}')) {\n        PARSE_CHECK(parse_expression(p, &left));\n    }\n    \n    if (parse_checktokenadvance(p, TOKEN_STRING)) {\n        if (!parse_string(p, &right)) return false;\n    } else if (parse_checktokenadvance(p, TOKEN_INTERPOLATION)) {\n        if (!parse_interpolation(p, &right)) return false;\n    } else {\n        parse_error(p, false, PARSE_INCOMPLETESTRINGINT);\n        return false;\n    }\n    \n    return parse_addnode(p, NODE_INTERPOLATION, s, &tok, left, right, (syntaxtreeindx *) out);\n}\n\n/** Helper function to parse a tuple\n @param[in] p - current parser\n @param[in] start - token for starting '(' of the tuple\n @param[in] first - syntax tree index of first expression, already parsed\n @param[out] out - syntax tree entry of the output tuple node\n @returns true on success */\nbool parse_tuple(parser *p, token *start, syntaxtreeindx first, void *out) {\n    syntaxtreeindx prev=first, current=SYNTAXTREE_UNCONNECTED;\n    // First entry was already parsed by calling function\n    PARSE_CHECK(parse_addnode(p, NODE_ARGLIST, MORPHO_NIL, &p->previous, prev, current, &current));\n    \n    if (!parse_checktoken(p, TOKEN_RIGHTPAREN)) {\n        do {\n            PARSE_CHECK(parse_pseudoexpression(p, &current));\n            PARSE_CHECK(parse_addnode(p, NODE_ARGLIST, MORPHO_NIL, &p->previous, prev, current, &current));\n            prev = current;\n        } while (parse_checktokenadvance(p, TOKEN_COMMA));\n    }\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_MSSNGSQBRC));\n\n    return parse_addnode(p, NODE_TUPLE, MORPHO_NIL, start, SYNTAXTREE_UNCONNECTED, current, out);\n}\n\n/** Parses an expression in parentheses */\nbool parse_grouping(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx new;\n    PARSE_CHECK(parse_pseudoexpression(p, &new));\n    \n    syntaxtreenode *node = parse_lookupnode(p, new);\n    \n    // Detect a tuple from a comma after the first expression or if the\n    // grouping encloses a tuple.\n    if (parse_checktokenadvance(p, TOKEN_COMMA) ||\n        node->type==NODE_TUPLE) {\n        return parse_tuple(p, &start, new, out);\n    }\n    \n    PARSE_CHECK(parse_addnode(p, NODE_GROUPING, MORPHO_NIL, &p->previous, new, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out));\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_MISSINGPARENTHESIS));\n    return true;\n}\n\n/** Parse a unary operator */\nbool parse_unary(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreenodetype nodetype=NODE_LEAF;\n    \n    /* Determine which operator */\n    switch (start.type) {\n        case TOKEN_MINUS: nodetype = NODE_NEGATE; break;\n        case TOKEN_EXCLAMATION: nodetype = NODE_NOT; break;\n        case TOKEN_AT: nodetype = NODE_BREAKPOINT; break;\n        default:\n            UNREACHABLE(\"unhandled unary operator [Check parser definition table]\");\n    }\n    \n    /* Now add this node */\n    syntaxtreeindx right;\n    PARSE_CHECK(parse_precedence(p, PREC_UNARY, &right));\n    return parse_addnode(p, nodetype, MORPHO_NIL, &start, right, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parse a binary operator */\nbool parse_binary(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreenodetype nodetype=NODE_LEAF;\n    enum {LEFT, RIGHT} assoc = LEFT; /* for left associative operators */\n    \n    /* Determine which operator */\n    switch (start.type) {\n        case TOKEN_EQUAL:\n            nodetype = NODE_ASSIGN;\n            assoc = RIGHT;\n            break;\n            \n        case TOKEN_PLUS:        nodetype = NODE_ADD; break;\n        case TOKEN_MINUS:       nodetype = NODE_SUBTRACT; break;\n        case TOKEN_STAR:        nodetype = NODE_MULTIPLY; break;\n        case TOKEN_SLASH:       nodetype = NODE_DIVIDE; break;\n        case TOKEN_CIRCUMFLEX:\n            nodetype = NODE_POW;\n            assoc = RIGHT; \n            break;\n        \n        case TOKEN_EQ:          nodetype = NODE_EQ; break;\n        case TOKEN_NEQ:         nodetype = NODE_NEQ; break;\n        case TOKEN_LT:          nodetype = NODE_LT; break;\n        case TOKEN_GT:          nodetype = NODE_GT; break;\n        case TOKEN_LTEQ:        nodetype = NODE_LTEQ; break;\n        case TOKEN_GTEQ:        nodetype = NODE_GTEQ; break;\n            \n        case TOKEN_DOT:         nodetype = NODE_DOT; break;\n            \n        case TOKEN_DBLAMP:      nodetype = NODE_AND; break;\n        case TOKEN_DBLVBAR:     nodetype = NODE_OR; break;\n        default:\n            UNREACHABLE(\"unhandled binary operator [Check parser definition table]\");\n    }\n    \n    parserule *rule=parse_getrule(p, start.type);\n    syntaxtreeindx left=p->left;\n    syntaxtreeindx right=SYNTAXTREE_UNCONNECTED;\n    \n    /* Check if we have a right hand side. */\n    if (parse_checktoken(p, TOKEN_EOF)) {\n        parse_error(p, true, PARSE_INCOMPLETEEXPRESSION);\n        return false;\n    } else {\n        if (nodetype==NODE_ASSIGN &&\n            parse_checktokenadvance(p, TOKEN_FUNCTION)) {\n            PARSE_CHECK(parse_anonymousfunction(p, &right));\n        } else if (nodetype==NODE_DOT &&\n                   parse_checktokeniskeywordadvance(p)) {\n            PARSE_CHECK(parse_symbol(p, &right));\n        } else {\n            PARSE_CHECK(parse_precedence(p, rule->precedence + (assoc == LEFT ? 1 : 0), &right));\n        }\n    }\n    \n    /* Now add this node */\n    return parse_addnode(p, nodetype, MORPHO_NIL, &start, left, right, (syntaxtreeindx *) out);\n}\n\n/** Parse ternary operator */\nbool parse_ternary(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx cond=p->left;\n    syntaxtreeindx left, right, outcomes;\n    PARSE_CHECK(parse_expression(p, &left));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_COLON, PARSE_TRNRYMSSNGCOLON));\n    PARSE_CHECK(parse_expression(p, &right));\n    \n    PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, left, right, &outcomes));\n    return parse_addnode(p, NODE_TERNARY, MORPHO_NIL, &start, cond, outcomes, (syntaxtreeindx *) out);\n}\n\n/** Parse operators like +=, -=, *= etc. */\nbool parse_assignby(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreenodetype nodetype=NODE_LEAF;\n    \n    /* Determine which operator */\n    switch (start.type) {\n        case TOKEN_PLUSEQ:      nodetype = NODE_ADD; break;\n        case TOKEN_MINUSEQ:     nodetype = NODE_SUBTRACT; break;\n        case TOKEN_STAREQ:      nodetype = NODE_MULTIPLY; break;\n        case TOKEN_SLASHEQ:     nodetype = NODE_DIVIDE; break;\n        default:\n            UNREACHABLE(\"unhandled assignment operator [Check parser definition table]\");\n    }\n    \n    parserule *rule=parse_getrule(p, start.type);\n    syntaxtreeindx left=p->left;\n    syntaxtreeindx right=SYNTAXTREE_UNCONNECTED;\n    \n    /* Check if we have a right hand side. */\n    if (parse_checktoken(p, TOKEN_EOF)) {\n        parse_error(p, true, PARSE_INCOMPLETEEXPRESSION);\n        return false;\n    } else {\n        PARSE_CHECK(parse_precedence(p, rule->precedence, &right));\n    }\n    \n    if (!parse_addnode(p, nodetype, MORPHO_NIL, &start, left, right, &right)) return false;\n    \n    /* Now add this node */\n    return parse_addnode(p, NODE_ASSIGN, MORPHO_NIL, &start, left, right, (syntaxtreeindx *) out);\n}\n\n/** Parses a range */\nbool parse_range(parser *p, void *out) {\n    token start = p->previous;\n    bool inclusive = (start.type==TOKEN_DOTDOT);\n    \n    syntaxtreeindx left=p->left;\n    syntaxtreeindx right;\n    \n    PARSE_CHECK(parse_expression(p, &right));\n    syntaxtreeindx new;\n    PARSE_CHECK(parse_addnode(p, (inclusive ? NODE_INCLUSIVERANGE : NODE_RANGE), MORPHO_NIL, &start, left, right, &new));\n    \n    if (parse_checktokenadvance(p, TOKEN_COLON)) { // Wrap in an outer NODE_RANGE\n        syntaxtreeindx step;\n        PARSE_CHECK(parse_expression(p, &step));\n        \n        PARSE_CHECK(parse_addnode(p, NODE_RANGE, MORPHO_NIL, &start, new, step, &new));\n    }\n    \n    *((syntaxtreeindx *) out) = new;\n    return true;\n}\n\n/** Parse a function call */\nbool parse_call(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx left=p->left;\n    syntaxtreeindx right;\n    unsigned int nargs;\n    \n    PARSE_CHECK(parse_expressionlist(p, TOKEN_RIGHTPAREN, &nargs, &right));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_CALLRGHTPARENMISSING));\n    \n    return parse_addnode(p, NODE_CALL, MORPHO_NIL, &start, left, right, out);\n}\n\n/** Parse index\n        index\n       /          \\\n  symbol         indices\n */\nbool parse_index(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx left=p->left;\n    syntaxtreeindx right;\n    unsigned int nindx;\n    \n    PARSE_CHECK(parse_expressionlist(p, TOKEN_RIGHTSQBRACKET, &nindx, &right));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTSQBRACKET, PARSE_CALLRGHTPARENMISSING));\n    \n    return parse_addnode(p, NODE_INDEX, MORPHO_NIL, &start, left, right, (syntaxtreeindx *) out);\n}\n\n/** Parse list  */\nbool parse_list(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx right;\n    PARSE_CHECK(parse_expressionlist(p, TOKEN_RIGHTSQBRACKET, NULL, &right));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTSQBRACKET, PARSE_MSSNGSQBRC));\n\n    return parse_addnode(p, NODE_LIST, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, right, out);\n}\n\n/** Parses an anonymous function */\nbool parse_anonymousfunction(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx args=SYNTAXTREE_UNCONNECTED,\n                   body=SYNTAXTREE_UNCONNECTED;\n    \n    /* Parameter list */\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTPAREN, PARSE_FNLEFTPARENMISSING));\n    PARSE_CHECK(parse_arglist(p, TOKEN_RIGHTPAREN, NULL, &args));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_FNRGHTPARENMISSING));\n    \n    /* Function body */\n    if (parse_checktokenadvance(p, TOKEN_LEFTCURLYBRACKET)) { // fn (x) { ... }\n        PARSE_CHECK(parse_blockstatement(p, &body));\n    } else {\n        PARSE_CHECK(parse_expression(p, &body)); // Short form: fn (x) x\n        PARSE_CHECK(parse_addnode(p, NODE_RETURN, MORPHO_NIL, &start, body, SYNTAXTREE_UNCONNECTED, &body));\n    }\n    \n    return parse_addnode(p, NODE_FUNCTION, MORPHO_NIL, &start, args, body, out);\n}\n\n/** @brief: Parses a switch block\n * @details Switch blocks are key/statement pairs. Each pair is stored in a NODE_DICTIONARY list */\nbool parse_switch(parser *p, void *out) {\n    syntaxtreeindx last=SYNTAXTREE_UNCONNECTED;\n    \n    while(!parse_checktokenadvance(p, TOKEN_RIGHTCURLYBRACKET) && !parse_checktoken(p, TOKEN_EOF)) {\n        syntaxtreeindx key, statements, pair;\n        token tok=p->current; // Keep track of the token that corresponds to each key/value pair\n        \n        /* Parse the key/value pair */\n        PARSE_CHECK(parse_expression(p, &key));\n        PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_COLON, PARSE_SWTCHSPRTR));\n        tokentype terminators[] = { TOKEN_STRING, TOKEN_INTEGER, TOKEN_NUMBER, TOKEN_TRUE, TOKEN_FALSE, TOKEN_NIL, TOKEN_RIGHTCURLYBRACKET };\n        PARSE_CHECK(parse_declarationmulti(p, 7, terminators, &statements));\n        \n        /* Create an entry node */\n        PARSE_CHECK(parse_addnode(p, NODE_DICTENTRY, MORPHO_NIL, &tok, key, statements, &pair));\n        \n        /* These are linked into a chain of dictionary nodes */\n        PARSE_CHECK(parse_addnode(p, NODE_DICTIONARY, MORPHO_NIL, &tok, last, pair, &last));\n    };\n    \n    *((syntaxtreeindx *) out) = last;\n    \n    return true;\n}\n\n/* -------------------------------\n * Declarations\n * ------------------------------- */\n\n/** Parses a variable declaration */\nbool parse_vardeclaration(parser *p, void *out) {\n    syntaxtreeindx symbol, initializer, new=SYNTAXTREE_UNCONNECTED, last=SYNTAXTREE_UNCONNECTED;\n    \n    do {\n        token start = p->previous;\n        \n        PARSE_CHECK(parse_variable(p, PARSE_VAREXPECTED, &symbol));\n        \n        if (parse_checktokenadvance(p, TOKEN_LEFTSQBRACKET)) {\n            if (!parse_index(p, &symbol)) return false;\n        }\n        \n        if (parse_checktokenadvance(p, TOKEN_EQUAL)) {\n            PARSE_CHECK(parse_pseudoexpression(p, &initializer));\n        } else initializer=SYNTAXTREE_UNCONNECTED;\n        \n        PARSE_CHECK(parse_addnode(p, NODE_DECLARATION, MORPHO_NIL, &start, symbol, initializer, &new));\n        \n        if (last!=SYNTAXTREE_UNCONNECTED) {\n            PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, last, new, &new));\n        }\n        \n        last=new;\n    } while (parse_checktokenadvance(p, TOKEN_COMMA));\n    \n    PARSE_CHECK(parse_statementterminator(p));\n    \n    *((syntaxtreeindx *) out) = new;\n    \n    return true;\n}\n\n/** Parses a possible typed var declaration */\nbool parse_typedvardeclaration(parser *p, void *out) {\n    syntaxtreeindx new=SYNTAXTREE_UNCONNECTED;\n    token start = p->previous;\n    \n    lexer ol; // Store the state of the parser\n    parser op;\n    parse_savestate(p, &op, &ol);\n    parse_advance(p);\n    \n    if (parse_checktoken(p, TOKEN_SYMBOL)) { // It is a typed variable declaration\n        syntaxtreeindx type=SYNTAXTREE_UNCONNECTED, var=SYNTAXTREE_UNCONNECTED;\n        PARSE_CHECK(parse_symbol(p, &type));\n        PARSE_CHECK(parse_vardeclaration(p, &var));\n        PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, type, var, &new));\n    } else if (parse_checktokenadvance(p, TOKEN_DOT) && // Check that we actually match a var declaration\n               parse_checktokenadvance(p, TOKEN_SYMBOL) &&\n               parse_checktokenadvance(p, TOKEN_SYMBOL)) {\n        parse_restorestate(&op, &ol, p); // Match successful, so rewind the parser\n        parse_advance(p);\n        \n        syntaxtreeindx namespace=SYNTAXTREE_UNCONNECTED, type=SYNTAXTREE_UNCONNECTED, var=SYNTAXTREE_UNCONNECTED;\n        \n        PARSE_CHECK(parse_symbol(p, &namespace));\n        parse_advance(p);\n        parse_advance(p); // Advance over TOKEN_DOT\n        \n        PARSE_CHECK(parse_symbol(p, &type));\n        PARSE_CHECK(parse_vardeclaration(p, &var));\n        \n        PARSE_CHECK(parse_addnode(p, NODE_DOT, MORPHO_NIL, &start, namespace, type, &type));\n        PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, type, var, &new));\n    } else { // Perhaps it was really an expression statement\n        parse_restorestate(&op, &ol, p);\n        PARSE_CHECK(parse_statement(p, &new));\n    }\n    \n    *((syntaxtreeindx *) out) = new;\n    \n    return true;\n}\n\n/** Parses a function declaration */\nbool parse_functiondeclaration(parser *p, void *out) {\n    value name=MORPHO_NIL;\n    token start = p->previous;\n    syntaxtreeindx args=SYNTAXTREE_UNCONNECTED,\n                   body=SYNTAXTREE_UNCONNECTED;\n    \n    /* Function name */\n    if (parse_checktokenadvance(p, TOKEN_SYMBOL) ||\n        parse_checktokeniskeywordadvance(p)) {\n        name=parse_tokenasstring(p);\n        parse_addobject(p, name);\n    } else {\n        parse_error(p, false, PARSE_FNNAMEMISSING);\n        return false;\n    }\n    \n    /* Parameter list */\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTPAREN, PARSE_FNLEFTPARENMISSING));\n    PARSE_CHECK(parse_arglist(p, TOKEN_RIGHTPAREN, NULL, &args));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_FNRGHTPARENMISSING));\n    \n    /* Function body */\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTCURLYBRACKET, PARSE_FNLEFTCURLYMISSING));\n    PARSE_CHECK(parse_blockstatement(p, &body));\n    \n    return parse_addnode(p, NODE_FUNCTION, name, &start, args, body, (syntaxtreeindx *) out);\n}\n\n/* Parses a class declaration */\nbool parse_classdeclaration(parser *p, void *out) {\n    value name=MORPHO_NIL;\n    syntaxtreeindx sclass=SYNTAXTREE_UNCONNECTED;\n    token start = p->previous;\n    \n    /* Class name */\n    if (parse_checktokenadvance(p, TOKEN_SYMBOL)) {\n        name=parse_tokenasstring(p);\n        parse_addobject(p, name);\n    } else {\n        parse_error(p, false, PARSE_EXPECTCLASSNAME);\n        return false;\n    }\n    \n    /* Extract a superclass name */\n    if (parse_checktokenadvance(p, TOKEN_LT) || parse_checktokenadvance(p, TOKEN_IS)) {\n        PARSE_CHECK(parse_reference(p, PARSE_EXPECTSUPER, &sclass));\n    }\n    \n    if (parse_checktokenadvance(p, TOKEN_WITH)) {\n        do {\n            syntaxtreeindx smixin;\n            PARSE_CHECK(parse_reference(p, PARSE_EXPECTMIXIN, &smixin));\n            PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &p->previous, smixin, sclass, &sclass)); // Mixins end up being recorded in reverse order\n            \n        } while (parse_checktokenadvance(p, TOKEN_COMMA));\n    }\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTCURLYBRACKET, PARSE_CLASSLEFTCURLYMISSING));\n    /* Method declarations */\n    syntaxtreeindx last=SYNTAXTREE_UNCONNECTED, current=SYNTAXTREE_UNCONNECTED;\n    \n    while (!parse_checktoken(p, TOKEN_RIGHTCURLYBRACKET) && !parse_checktoken(p, TOKEN_EOF)) {\n        PARSE_CHECK(parse_functiondeclaration(p, &current));\n        \n        /* If we now have more than one node, insert a sequence node */\n        if (last!=SYNTAXTREE_UNCONNECTED) {\n            PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, last, current, &current));\n        }\n        \n        last = current;\n    }\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTCURLYBRACKET, PARSE_CLASSRGHTCURLYMISSING));\n    \n    return parse_addnode(p, NODE_CLASS, name, &start, sclass, current, (syntaxtreeindx *) out);\n}\n\n/** Parse an import qualifier list. This list consists of elements introduced by for and as in any order.\n */\nbool parse_importqualifierlist(parser *p, void *out) {\n    syntaxtreeindx list=SYNTAXTREE_UNCONNECTED;\n    bool asincluded=false; // Only one as is allowed\n    \n    while (!parse_checkstatementterminator(p) && !parse_checktoken(p, TOKEN_COMMA)) {\n        if (parse_checktokenadvance(p, TOKEN_AS)) {\n            if (asincluded) { // Only one 'as' allowed per import\n                parse_error(p, true, PARSE_IMPORTMLTPLAS);\n                return false;\n            }\n            \n            if (parse_checktokenadvance(p, TOKEN_SYMBOL)) {\n                syntaxtreeindx symbl;\n                PARSE_CHECK(parse_symbol(p, &symbl));\n                PARSE_CHECK(parse_addnode(p, NODE_AS, MORPHO_NIL, &p->previous, symbl, list, &list));\n            } else {\n                parse_error(p, true, PARSE_IMPORTASSYMBL);\n                return false;\n            }\n            \n            asincluded=true;\n        } else if (parse_checktokenadvance(p, TOKEN_FOR)) {\n            do {\n                if (parse_checktokenadvance(p, TOKEN_SYMBOL)) {\n                    syntaxtreeindx symbl;\n                    PARSE_CHECK(parse_symbol(p, &symbl));\n                    PARSE_CHECK(parse_addnode(p, NODE_FOR, MORPHO_NIL, &p->previous, symbl, list, &list));\n                } else {\n                    parse_error(p, true, PARSE_IMPORTFORSYMBL);\n                    return false;\n                }\n            } while (parse_checktokenadvance(p, TOKEN_COMMA));\n        } else {\n            parse_error(p, true, PARSE_IMPORTUNEXPCTDTOK);\n            return false;\n        }\n    }\n        \n    *((syntaxtreeindx *) out)=list;\n    \n    return true;\n}\n\n/** Parse an import declaration. Each import has the following structure:\n *          IMPORT\n *         /              \\\n *     module           import qualifier list\n * These are chained together as sequence nodes if there's more than one\n */\nbool parse_importdeclaration(parser *p, void *out) {\n    syntaxtreeindx prev=SYNTAXTREE_UNCONNECTED, // Use to construct list\n                   current=SYNTAXTREE_UNCONNECTED;\n    \n    do {\n        syntaxtreeindx modulename=SYNTAXTREE_UNCONNECTED,\n                       qualifier=SYNTAXTREE_UNCONNECTED;\n        token start = p->previous;\n        \n        if (parse_checktokenadvance(p, TOKEN_STRING)) {\n            PARSE_CHECK(parse_string(p, &modulename));\n        } else if (parse_checktokenadvance(p, TOKEN_SYMBOL)){\n            PARSE_CHECK(parse_symbol(p, &modulename));\n        } else {\n            parse_error(p, true, PARSE_IMPORTMISSINGNAME);\n            return false;\n        }\n        \n        PARSE_CHECK(parse_importqualifierlist(p, &qualifier));\n        \n        PARSE_CHECK(parse_addnode(p, NODE_IMPORT, MORPHO_NIL, &start, modulename, qualifier, &current));\n        \n        PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, prev, current, &current));\n        prev = current;\n    } while (parse_checktokenadvance(p, TOKEN_COMMA));\n    \n    PARSE_CHECK(parse_statementterminator(p));\n    \n    *((syntaxtreeindx *) out)=current;\n    \n    return true;\n}\n\n/* -------------------------------\n * Statements\n * ------------------------------- */\n\n/** Parse a print statement */\nbool parse_printstatement(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx left;\n    PARSE_CHECK(parse_pseudoexpression(p, &left));\n    PARSE_CHECK(parse_statementterminator(p));\n    return parse_addnode(p, NODE_PRINT, MORPHO_NIL, &start, left, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parse an expression statement */\nbool parse_expressionstatement(parser *p, void *out) {\n    syntaxtreeindx new;\n    PARSE_CHECK(parse_expression(p, &new));\n    PARSE_CHECK(parse_statementterminator(p));\n    *((syntaxtreeindx *) out) = new;\n    return true;\n}\n\n/** @brief Parse a block statement.\n *  @details This wraps up a sequence of statements in a SCOPE node:\n *                     SCOPE\n *                    /     \\\n *                   -       body\n **/\nbool parse_blockstatement(parser *p, void *out) {\n    syntaxtreeindx body = SYNTAXTREE_UNCONNECTED;\n    token start = p->previous;\n    tokentype terminator[] = { TOKEN_RIGHTCURLYBRACKET };\n    \n    PARSE_CHECK(parse_declarationmulti(p, 1, terminator, &body));\n    if (parse_checktoken(p, TOKEN_EOF)) {\n        parse_error(p, false, PARSE_INCOMPLETEEXPRESSION);\n        return false;\n    } else {\n        PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTCURLYBRACKET, PARSE_MISSINGSEMICOLONEXP));\n    }\n    \n    return parse_addnode(p, NODE_SCOPE, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, body, out);\n}\n\n/** Parse an if statement */\nbool parse_ifstatement(parser *p, void *out) {\n    syntaxtreeindx  cond=SYNTAXTREE_UNCONNECTED,\n                    then=SYNTAXTREE_UNCONNECTED,\n                    els=SYNTAXTREE_UNCONNECTED;\n    token start = p->previous;\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTPAREN, PARSE_IFLFTPARENMISSING));\n    PARSE_CHECK(parse_expression(p, &cond));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_IFRGHTPARENMISSING));\n    \n    token thentok = p->current;\n    PARSE_CHECK(parse_statement(p, &then));\n    \n    if (parse_checktoken(p, TOKEN_ELSE)) {\n        PARSE_CHECK(parse_advance(p));\n        PARSE_CHECK(parse_statement(p, &els));\n        \n        /* Create an additional node that contains both statements */\n        PARSE_CHECK(parse_addnode(p, NODE_THEN, MORPHO_NIL, &thentok, then, els, &then));\n    }\n    \n    return parse_addnode(p, NODE_IF, MORPHO_NIL, &start, cond, then, out);\n}\n\n/** Parse a while statement */\nbool parse_whilestatement(parser *p, void *out) {\n    syntaxtreeindx  cond=SYNTAXTREE_UNCONNECTED,\n                    body=SYNTAXTREE_UNCONNECTED;\n    token start = p->previous;\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTPAREN, PARSE_WHILELFTPARENMISSING));\n    PARSE_CHECK(parse_expression(p, &cond));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_IFRGHTPARENMISSING));\n    PARSE_CHECK(parse_statement(p, &body));\n    \n    return parse_addnode(p, NODE_WHILE, MORPHO_NIL, &start, cond, body, out);\n}\n\n/** Parse a for statement. */\nbool parse_forstatement(parser *p, void *out) {\n    syntaxtreeindx init=SYNTAXTREE_UNCONNECTED, // Initializer\n                   cond=SYNTAXTREE_UNCONNECTED, // Condition\n                   body=SYNTAXTREE_UNCONNECTED, // Loop body\n                   final=SYNTAXTREE_UNCONNECTED; // Final statement\n    token start = p->current;\n    bool forin=false, ret=false;\n \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTPAREN, PARSE_FORLFTPARENMISSING));\n    if (parse_checktokenadvance(p, TOKEN_SEMICOLON)) {\n        \n    } else if (parse_checktokenadvance(p, TOKEN_VAR)) {\n        PARSE_CHECK(parse_vardeclaration(p, &init));\n    } else {\n        PARSE_CHECK(parse_expression(p, &init));\n        while (parse_checktokenadvance(p, TOKEN_COMMA)) {\n            syntaxtreeindx new;\n            PARSE_CHECK(parse_expressionstatement(p, &new));\n            parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &p->current, init, new, &init);\n        }\n        parse_checktokenadvance(p, TOKEN_SEMICOLON);\n    }\n    \n    if (parse_checktokenadvance(p, TOKEN_IN)) {\n        /* If its an for..in loop, parse the collection */\n        PARSE_CHECK(parse_expression(p, &cond));\n        forin=true;\n    } else {\n        /* Otherwise, parse the condition and final clause in a traditional for loop. */\n        if (!parse_checktokenadvance(p, TOKEN_SEMICOLON)) {\n            PARSE_CHECK(parse_expressionstatement(p, &cond));\n        }\n        \n        if (!parse_checktoken(p, TOKEN_RIGHTPAREN)) {\n            PARSE_CHECK(parse_expression(p, &final));\n        }\n    }\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_FORRGHTPARENMISSING));\n    \n    if (!parse_checkstatementterminator(p)) {\n        PARSE_CHECK(parse_statement(p, &body));\n    }\n    \n    if (forin) {\n        /* A for..in loop is parsed into the syntax tree as follows:\n         *\n         *                 forin\n         *                /     \\\n         *               in      body\n         *              /  \\\n         *          init    collection\n         */\n        syntaxtreeindx innode;\n        PARSE_CHECK(parse_addnode(p, NODE_IN, MORPHO_NIL, &start, init, cond, &innode));\n        ret=parse_addnode(p, NODE_FOR, MORPHO_NIL, &start, innode, body, (syntaxtreeindx *) out);\n    } else {\n        /* A traditional for loop is parsed into an equivalent while loop:\n         * -> for (init; cond; inc) body;\n         *\n         * becomes\n         *              scope\n         *                   \\\n         *                    ;\n         *                   / \\\n         *               init   while\n         *                     /     \\\n         *                 cond       ; // The presence of the seq. indicates a for loop\n         *                           / \\\n         *                       body   inc\n         * */\n        syntaxtreeindx loop,whil,seq;\n        \n        PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, body, final, &loop));\n        PARSE_CHECK(parse_addnode(p, NODE_WHILE, MORPHO_NIL, &start, cond, loop, &whil));\n        PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, init, whil, &seq));\n        ret=parse_addnode(p, NODE_SCOPE, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, seq, (syntaxtreeindx *) out);\n    }\n    \n    return ret;\n}\n\n/** Parses a do...while loop */\nbool parse_dostatement(parser *p, void *out) {\n    syntaxtreeindx body=SYNTAXTREE_UNCONNECTED, // Loop body\n                   cond=SYNTAXTREE_UNCONNECTED; // Condition\n    token start = p->current;\n    \n    if (!parse_statement(p, &body)) return false;\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_WHILE, PARSE_EXPCTWHL));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTPAREN, PARSE_WHILELFTPARENMISSING));\n    PARSE_CHECK(parse_expression(p, &cond));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_IFRGHTPARENMISSING));\n    \n    /* Optional statement terminator */\n    if (parse_checkstatementterminator(p)) {\n        PARSE_CHECK(parse_statementterminator(p));\n    }\n    \n    return parse_addnode(p, NODE_DO, MORPHO_NIL, &start, body, cond, (syntaxtreeindx *) out);\n}\n\n/** Parses a break or continue statement */\nbool parse_breakstatement(parser *p, void *out) {\n    token start = p->previous;\n    \n    PARSE_CHECK(parse_statementterminator(p));\n    \n    return parse_addnode(p, (start.type==TOKEN_BREAK ? NODE_BREAK: NODE_CONTINUE), MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parse a return statement */\nbool parse_returnstatement(parser *p, void *out) {\n    token start = p->previous;\n    syntaxtreeindx left = SYNTAXTREE_UNCONNECTED;\n    \n    if (!parse_checkstatementterminator(p)) {\n        PARSE_CHECK(parse_pseudoexpression(p, &left));\n    }\n    \n    PARSE_CHECK(parse_statementterminator(p));\n    \n    return parse_addnode(p, NODE_RETURN, MORPHO_NIL, &start, left, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/** Parse a try/catch statement\n        try\n      /          \\\n    body        catch block */\nbool parse_trystatement(parser *p, void *out) {\n    syntaxtreeindx try=SYNTAXTREE_UNCONNECTED, // Try block\n                   catch=SYNTAXTREE_UNCONNECTED; // Catch dictionary\n    token start = p->current;\n    \n    PARSE_CHECK(parse_statement(p, &try));\n    \n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_CATCH, PARSE_EXPCTCTCH));\n    PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_LEFTCURLYBRACKET, PARSE_CATCHLEFTCURLYMISSING));\n    \n    PARSE_CHECK(parse_switch(p, &catch));\n    \n    /* Optional statement terminator */\n    if (parse_checkstatementterminator(p)) {\n        PARSE_CHECK(parse_statementterminator(p));\n    }\n    \n    return parse_addnode(p, NODE_TRY, MORPHO_NIL, &start, try, catch, (syntaxtreeindx *) out);\n}\n\n/** Parse a breakpoint statement */\nbool parse_breakpointstatement(parser *p, void *out) {\n    token start = p->previous;\n    \n    if (parse_checkstatementterminator(p)) {\n        PARSE_CHECK(parse_statementterminator(p));\n    }\n    \n    return parse_addnode(p, NODE_BREAKPOINT, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, SYNTAXTREE_UNCONNECTED, (syntaxtreeindx *) out);\n}\n\n/* -------------------------------------------------------\n * Parsers for different statement types\n * ------------------------------------------------------- */\n\n/** Parses an expression */\nbool parse_expression(parser *p, void *out) {\n    return parse_precedence(p, PREC_ASSIGN, out);\n}\n\n/** Parses an expression that may include an anonymous function */\nbool parse_pseudoexpression(parser *p, void *out) {\n    if (parse_checktokenadvance(p, TOKEN_FUNCTION)) {\n        return parse_anonymousfunction(p, out);\n    } else {\n        return parse_expression(p, out);\n    }\n}\n\n/** @brief Parse statements\n *  @details Statements are things that are allowed inside control flow statements */\nbool parse_statement(parser *p, void *out) {\n    if (parse_checktokenadvance(p, TOKEN_PRINT)) {\n        return parse_printstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_IF)) {\n        return parse_ifstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_WHILE)) {\n        return parse_whilestatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_FOR)) {\n        return parse_forstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_DO)) {\n        return parse_dostatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_BREAK)) {\n        return parse_breakstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_CONTINUE)) {\n        return parse_breakstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_RETURN)) {\n        return parse_returnstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_TRY)) {\n        return parse_trystatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_LEFTCURLYBRACKET)) {\n        return parse_blockstatement(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_AT)) {\n        return parse_breakpointstatement(p, out);\n    } else {\n        return parse_expressionstatement(p, out);\n    }\n    return false;\n}\n\n/** @brief Parse declarations\n *  @details Declarations define something (e.g. a variable or a function) OR\n *           a regular statement. They are *not* allowed in control flow statements. */\nbool parse_declaration(parser *p, void *out) {\n    bool success=false;\n    if (parse_checktokenadvance(p, TOKEN_FUNCTION)) {\n        success=parse_functiondeclaration(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_VAR)) {\n        success=parse_vardeclaration(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_CLASS)) {\n        success=parse_classdeclaration(p, out);\n    } else if (parse_checktokenadvance(p, TOKEN_IMPORT)) {\n        success=parse_importdeclaration(p, out);\n    } else if (parse_checktoken(p, TOKEN_SYMBOL)) { // Typed var declaration ?\n        success=parse_typedvardeclaration(p, out);\n    } else {\n        success=parse_statement(p, out);\n    }\n    \n    //if (!success) parse_synchronize(p);\n    return success;\n}\n\n/** Parses multiple declarations, separated by ; separators\n *  @param p    the parser\n *  @param end  token type to terminate on [use TOKEN_EOF if no special terminator]\n *  @returns    the syntaxtreeindx of the parsed expression */\nbool parse_declarationmulti(parser *p, int n, tokentype *end, void *out) {\n    syntaxtreeindx last=SYNTAXTREE_UNCONNECTED, current=SYNTAXTREE_UNCONNECTED;\n    token start = p->current;\n    \n    while (!parse_checktokenmulti(p, n, end) && !parse_checktoken(p, TOKEN_EOF)) {\n        PARSE_CHECK(parse_declaration(p, &current));\n        \n        /* If we now have more than one node, insert a sequence node */\n        if (last!=SYNTAXTREE_UNCONNECTED) {\n            PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, last, current, &current));\n        }\n        \n        last = current;\n    }\n    \n    *((syntaxtreeindx *) out) = current;\n    \n    return true;\n}\n\n/** Parse a program as a sequence of declarations and statements */\nbool parse_program(parser *p, void *out) {\n    tokentype terminator[] = { TOKEN_EOF };\n    \n    parse_declarationmulti(p, 1, terminator, &((syntaxtree *) p->out)->entry);\n    \n    return ERROR_SUCCEEDED(*p->err);\n}\n\n/* **********************************************************************\n * Parser definition table for morpho grammar\n * ********************************************************************** */\n\nparserule rules[] = {\n    PARSERULE_UNUSED(TOKEN_NEWLINE),\n    PARSERULE_INFIX(TOKEN_QUESTION, parse_ternary, PREC_TERNARY),\n    \n    PARSERULE_PREFIX(TOKEN_STRING, parse_string),\n    PARSERULE_PREFIX(TOKEN_INTERPOLATION, parse_interpolation),\n    PARSERULE_PREFIX(TOKEN_INTEGER, parse_integer),\n    PARSERULE_PREFIX(TOKEN_NUMBER, parse_number),\n    PARSERULE_PREFIX(TOKEN_SYMBOL, parse_symbol),\n\n    PARSERULE_MIXFIX(TOKEN_LEFTPAREN, parse_grouping, parse_call, PREC_CALL),\n    PARSERULE_UNUSED(TOKEN_RIGHTPAREN),\n    PARSERULE_MIXFIX(TOKEN_LEFTSQBRACKET, parse_list, parse_index, PREC_CALL),\n    PARSERULE_UNUSED(TOKEN_RIGHTSQBRACKET),\n    PARSERULE_PREFIX(TOKEN_LEFTCURLYBRACKET, parse_dictionary),\n    PARSERULE_UNUSED(TOKEN_RIGHTCURLYBRACKET),\n    PARSERULE_UNUSED(TOKEN_COLON),\n    PARSERULE_UNUSED(TOKEN_SEMICOLON),\n    PARSERULE_UNUSED(TOKEN_COMMA),\n    \n    PARSERULE_INFIX(TOKEN_PLUS, parse_binary, PREC_TERM),\n    PARSERULE_MIXFIX(TOKEN_MINUS, parse_unary, parse_binary, PREC_TERM),\n    PARSERULE_INFIX(TOKEN_STAR, parse_binary, PREC_FACTOR),\n    PARSERULE_INFIX(TOKEN_SLASH, parse_binary, PREC_FACTOR),\n    PARSERULE_INFIX(TOKEN_CIRCUMFLEX, parse_binary, PREC_POW),\n    \n    PARSERULE_UNUSED(TOKEN_PLUSPLUS),\n    PARSERULE_UNUSED(TOKEN_MINUSMINUS),\n    PARSERULE_INFIX(TOKEN_PLUSEQ, parse_assignby, PREC_ASSIGN),\n    PARSERULE_INFIX(TOKEN_MINUSEQ, parse_assignby, PREC_ASSIGN),\n    PARSERULE_INFIX(TOKEN_STAREQ, parse_assignby, PREC_ASSIGN),\n    PARSERULE_INFIX(TOKEN_SLASHEQ, parse_assignby, PREC_ASSIGN),\n    PARSERULE_UNUSED(TOKEN_HASH),\n    PARSERULE_PREFIX(TOKEN_AT, parse_unary),\n    \n    PARSERULE_INFIX(TOKEN_DOT, parse_binary, PREC_CALL),\n    PARSERULE_INFIX(TOKEN_DOTDOT, parse_range, PREC_RANGE),\n    PARSERULE_INFIX(TOKEN_DOTDOTDOT, parse_range, PREC_RANGE),\n    PARSERULE_PREFIX(TOKEN_EXCLAMATION, parse_unary),\n    PARSERULE_UNUSED(TOKEN_AMP),\n    PARSERULE_UNUSED(TOKEN_VBAR),\n    PARSERULE_INFIX(TOKEN_DBLAMP, parse_binary, PREC_AND),\n    PARSERULE_INFIX(TOKEN_DBLVBAR, parse_binary, PREC_OR),\n    PARSERULE_INFIX(TOKEN_EQUAL, parse_binary, PREC_ASSIGN),\n    PARSERULE_INFIX(TOKEN_EQ, parse_binary, PREC_EQUALITY),\n    PARSERULE_INFIX(TOKEN_NEQ, parse_binary, PREC_EQUALITY),\n    PARSERULE_INFIX(TOKEN_LT, parse_binary, PREC_COMPARISON),\n    PARSERULE_INFIX(TOKEN_GT, parse_binary, PREC_COMPARISON),\n    PARSERULE_INFIX(TOKEN_LTEQ, parse_binary, PREC_COMPARISON),\n    PARSERULE_INFIX(TOKEN_GTEQ, parse_binary, PREC_COMPARISON),\n    \n    PARSERULE_PREFIX(TOKEN_TRUE, parse_bool),\n    PARSERULE_PREFIX(TOKEN_FALSE, parse_bool),\n    PARSERULE_PREFIX(TOKEN_NIL, parse_nil),\n    PARSERULE_PREFIX(TOKEN_SELF, parse_self),\n    PARSERULE_PREFIX(TOKEN_SUPER, parse_super),\n    PARSERULE_PREFIX(TOKEN_IMAG, parse_complex),\n    PARSERULE_UNUSED(TOKEN_PRINT),\n    PARSERULE_UNUSED(TOKEN_VAR),\n    PARSERULE_UNUSED(TOKEN_IF),\n    PARSERULE_UNUSED(TOKEN_ELSE),\n    PARSERULE_UNUSED(TOKEN_IN),\n    PARSERULE_UNUSED(TOKEN_WHILE),\n    PARSERULE_UNUSED(TOKEN_FOR),\n    PARSERULE_UNUSED(TOKEN_DO),\n    PARSERULE_UNUSED(TOKEN_BREAK),\n    PARSERULE_UNUSED(TOKEN_CONTINUE),\n    PARSERULE_UNUSED(TOKEN_FUNCTION),\n    PARSERULE_UNUSED(TOKEN_RETURN),\n    PARSERULE_UNUSED(TOKEN_CLASS),\n    PARSERULE_UNUSED(TOKEN_IMPORT),\n    PARSERULE_UNUSED(TOKEN_AS),\n    PARSERULE_UNUSED(TOKEN_IS),\n    PARSERULE_UNUSED(TOKEN_WITH),\n    PARSERULE_UNUSED(TOKEN_TRY),\n    PARSERULE_UNUSED(TOKEN_CATCH),\n    \n    PARSERULE_UNUSED(TOKEN_INCOMPLETE),\n    PARSERULE_UNUSED(TOKEN_EOF),\n    PARSERULE_UNUSED(TOKEN_NONE)\n};\n\n/* **********************************************************************\n * Obtain parse rules\n * ********************************************************************** */\n\n/** Compares two parse rules */\nint _parse_parserulecmp(const void *l, const void *r) {\n    parserule *a = (parserule *) l;\n    parserule *b = (parserule *) r;\n    return ((int) a->type) - ((int) b->type);\n}\n\n/** Get the rule to parse an element of type tokentype. */\nparserule *parse_getrule(parser *p, tokentype type) {\n    if (p->parsetable.count==0) return &rules[type];\n    \n    parserule key = { .type = type };\n    \n    return bsearch(&key, p->parsetable.data, p->parsetable.count, sizeof(parserule), _parse_parserulecmp);\n}\n\n/* **********************************************************************\n * Customize parsers\n * ********************************************************************** */\n\n/** Defines the parse table. */\nbool parse_setparsetable(parser *p, parserule *rules) {\n    varray_parseruleclear(&p->parsetable);\n    \n    for (int i=0; rules[i].type!=TOKEN_NONE; i++) {\n        if (rules[i].prefix!=NULL || rules[i].infix!=NULL) {\n            if (!varray_parseruleadd(&p->parsetable, &rules[i], 1)) return false;\n        }\n    }\n    \n    qsort(p->parsetable.data, p->parsetable.count, sizeof(parserule), _parse_parserulecmp);\n    return true;\n}\n\n/** Sets the parse function to be called to start parsing */\nvoid parse_setbaseparsefn(parser *p, parsefunction fn) {\n    p->baseparsefn = fn;\n}\n\n/** Sets whether to skip new line tokens, and define the token type if so. */\nvoid parse_setskipnewline(parser *p, bool skip, tokentype toknewline) {\n    p->skipnewline = skip;\n    p->toknewline = toknewline;\n}\n\n/** Set maximum recursion depth\n @warning: It is not guaranteed that values above PARSE_MAXRECURSIONDEPTH are achievable */\nvoid parse_setmaxrecursiondepth(parser *p, int maxdepth) {\n    p->maxrecursiondepth = maxdepth;\n}\n\n\n/* **********************************************************************\n * Interface\n * ********************************************************************** */\n\n/** @brief Initialize a parser\n *  @param p       the parser to initialize\n *  @param lex   lexer to use\n *  @param err   an error structure to fill out if necessary\n *  @param tree Pointer to the output */\nvoid parse_init(parser *p, lexer *lex, error *err, void *out) {\n    p->current = TOKEN_BLANK;\n    p->previous = TOKEN_BLANK;\n    p->left = SYNTAXTREE_UNCONNECTED;\n    p->lex=lex;\n    p->err=err;\n    p->out=out;\n    p->nl=false;\n    p->maxrecursiondepth=PARSE_MAXRECURSIONDEPTH;\n    p->recursiondepth=0;\n    varray_parseruleinit(&p->parsetable);\n    varray_valueinit(&p->objects);\n    \n    // Configure parser to parse morpho by default\n    parse_setbaseparsefn(p, parse_program);\n    parse_setskipnewline(p, true, TOKEN_NEWLINE);\n    parse_setparsetable(p, rules);\n}\n\n/** @brief Clear a parser */\nvoid parse_clear(parser *p) {\n    p->current = TOKEN_BLANK;\n    p->previous = TOKEN_BLANK;\n    p->left = SYNTAXTREE_UNCONNECTED;\n    varray_parseruleclear(&p->parsetable);\n    \n    parse_clearobjects(p);\n}\n\n/** Generic entry point into the parser; call this from your own parser wrapper */\nbool parse(parser *p) {\n    PARSE_CHECK(parse_advance(p));\n    bool success=(p->baseparsefn) (p, p->out);\n    if (!success) parse_freeobjects(p);\n    parse_clearobjects(p);\n    return success;\n}\n\n/** Parse morpho source */\nbool morpho_parse(parser *p) {\n    lex_skipshebang(p->lex);\n    bool success=parse(p);\n    \n    if (!success) syntaxtree_wipe((syntaxtree *) p->out);\n    \n    return success;\n}\n\n/* **********************************************************************\n * Other useful parsers\n * ********************************************************************** */\n\n/** Convenience function to parse a string into an array of values\n * @param[in] string - string to parse\n * @param[in] nmax      - maximum number of values to read\n * @param[in] v            - value array, filled out on return\n * @param[out] n          - number of values read\n * @param[out] err      - error structure filled out if an error occurs\n * @returns true if successful, false otherwise. */\nbool parse_stringtovaluearray(char *string, unsigned int nmax, value *v, unsigned int *n, error *err) {\n    lexer l;\n    token tok;\n    unsigned int k=0;\n    bool minus=false;\n    lex_init(&l, string, 0);\n    \n    do {\n        if (!lex(&l, &tok, err)) return false;\n        switch(tok.type) {\n            case TOKEN_INTEGER: {\n                long f = strtol(tok.start, NULL, 10);\n                v[k]=MORPHO_INTEGER((minus ? -f : f)); k++; minus=false;\n            }\n                break;\n            case TOKEN_NUMBER: {\n                double f = strtod(tok.start, NULL);\n                v[k]=MORPHO_FLOAT((minus ? -f : f)); k++; minus=false;\n            }\n                break;\n            case TOKEN_MINUS:\n                minus=true;\n                break;\n            case TOKEN_COMMA:\n            case TOKEN_EOF:\n                break;\n            default:\n                morpho_writeerrorwithid(err, PARSE_UNRECGNZEDTOK, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE);\n                return false; \n                break;\n        }\n    } while (tok.type!=TOKEN_EOF && k<nmax);\n    \n    if (n) *n=k;\n    \n    lex_clear(&l);\n    \n    return true;\n}\n\n/* Parses a literal string, returning a value */\nbool parse_value(const char *in, value *out) {\n    lexer l;\n    parser p;\n    syntaxtree tree;\n    error err;\n    bool success=false;\n    error_init(&err);\n    syntaxtree_init(&tree);\n    lex_init(&l, in, 1);\n    parse_init(&p, &l, &err, &tree);\n    if (parse(&p) && tree.tree.count>0) {\n        syntaxtreenode node = tree.tree.data[tree.entry];\n        \n        if (SYNTAXTREE_ISLEAF(node.type)) {\n            if (MORPHO_ISSTRING(node.content)) {\n                *out = object_clonestring(node.content);\n            } else *out = node.content;\n            \n            success=true;\n        }\n    }\n    \n    syntaxtree_clear(&tree);\n    return success;\n}\n\n\nvoid parse_initialize(void) {\n    /* Parse errors */\n    morpho_defineerror(PARSE_INCOMPLETEEXPRESSION, ERROR_PARSE, PARSE_INCOMPLETEEXPRESSION_MSG);\n    morpho_defineerror(PARSE_MISSINGPARENTHESIS, ERROR_PARSE, PARSE_MISSINGPARENTHESIS_MSG);\n    morpho_defineerror(PARSE_EXPECTEXPRESSION, ERROR_PARSE, PARSE_EXPECTEXPRESSION_MSG);\n    morpho_defineerror(PARSE_MISSINGSEMICOLON, ERROR_PARSE, PARSE_MISSINGSEMICOLON_MSG);\n    morpho_defineerror(PARSE_MISSINGSEMICOLONEXP, ERROR_PARSE, PARSE_MISSINGSEMICOLONEXP_MSG);\n    morpho_defineerror(PARSE_MISSINGSEMICOLONVAR, ERROR_PARSE, PARSE_MISSINGSEMICOLONVAR_MSG);\n    morpho_defineerror(PARSE_VAREXPECTED, ERROR_PARSE, PARSE_VAREXPECTED_MSG);\n    morpho_defineerror(PARSE_SYMBLEXPECTED, ERROR_PARSE, PARSE_SYMBLEXPECTED_MSG);\n    morpho_defineerror(PARSE_BLOCKTERMINATOREXP, ERROR_PARSE, PARSE_BLOCKTERMINATOREXP_MSG);\n    morpho_defineerror(PARSE_MSSNGSQBRC, ERROR_PARSE, PARSE_MSSNGSQBRC_MSG);\n    morpho_defineerror(PARSE_MSSNGCOMMA, ERROR_PARSE, PARSE_MSSNGCOMMA_MSG);\n    morpho_defineerror(PARSE_TRNRYMSSNGCOLON, ERROR_PARSE, PARSE_TRNRYMSSNGCOLON_MSG);\n    morpho_defineerror(PARSE_IFLFTPARENMISSING, ERROR_PARSE, PARSE_IFLFTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_IFRGHTPARENMISSING, ERROR_PARSE, PARSE_IFRGHTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_WHILELFTPARENMISSING, ERROR_PARSE, PARSE_WHILELFTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_FORLFTPARENMISSING, ERROR_PARSE, PARSE_FORLFTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_FORSEMICOLONMISSING, ERROR_PARSE, PARSE_FORSEMICOLONMISSING_MSG);\n    morpho_defineerror(PARSE_FORRGHTPARENMISSING, ERROR_PARSE, PARSE_FORRGHTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_FNNAMEMISSING, ERROR_PARSE, PARSE_FNNAMEMISSING_MSG);\n    morpho_defineerror(PARSE_FNLEFTPARENMISSING, ERROR_PARSE, PARSE_FNLEFTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_FNRGHTPARENMISSING, ERROR_PARSE, PARSE_FNRGHTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_FNLEFTCURLYMISSING, ERROR_PARSE, PARSE_FNLEFTCURLYMISSING_MSG);\n    morpho_defineerror(PARSE_CALLRGHTPARENMISSING, ERROR_PARSE, PARSE_CALLRGHTPARENMISSING_MSG);\n    morpho_defineerror(PARSE_EXPECTCLASSNAME, ERROR_PARSE, PARSE_EXPECTCLASSNAME_MSG);\n    morpho_defineerror(PARSE_CLASSLEFTCURLYMISSING, ERROR_PARSE, PARSE_CLASSLEFTCURLYMISSING_MSG);\n    morpho_defineerror(PARSE_CLASSRGHTCURLYMISSING, ERROR_PARSE, PARSE_CLASSRGHTCURLYMISSING_MSG);\n    morpho_defineerror(PARSE_EXPECTDOTAFTERSUPER, ERROR_PARSE, PARSE_EXPECTDOTAFTERSUPER_MSG);\n    morpho_defineerror(PARSE_INCOMPLETESTRINGINT, ERROR_PARSE, PARSE_INCOMPLETESTRINGINT_MSG);\n    morpho_defineerror(PARSE_VARBLANKINDEX, ERROR_COMPILE, PARSE_VARBLANKINDEX_MSG);\n    morpho_defineerror(PARSE_IMPORTMISSINGNAME, ERROR_PARSE, PARSE_IMPORTMISSINGNAME_MSG);\n    morpho_defineerror(PARSE_IMPORTMLTPLAS, ERROR_PARSE, PARSE_IMPORTMLTPLAS_MSG);\n    morpho_defineerror(PARSE_IMPORTUNEXPCTDTOK, ERROR_PARSE, PARSE_IMPORTUNEXPCTDTOK_MSG);\n    morpho_defineerror(PARSE_IMPORTASSYMBL, ERROR_PARSE, PARSE_IMPORTASSYMBL_MSG);\n    morpho_defineerror(PARSE_IMPORTFORSYMBL, ERROR_PARSE, PARSE_IMPORTFORSYMBL_MSG);\n    morpho_defineerror(PARSE_EXPECTSUPER, ERROR_COMPILE, PARSE_EXPECTSUPER_MSG);\n    morpho_defineerror(PARSE_EXPECTMIXIN, ERROR_COMPILE, PARSE_EXPECTMIXIN_MSG);\n    \n    morpho_defineerror(PARSE_UNRECGNZEDTOK, ERROR_PARSE, PARSE_UNRECGNZEDTOK_MSG);\n    morpho_defineerror(PARSE_DCTSPRTR, ERROR_PARSE, PARSE_DCTSPRTR_MSG);\n    morpho_defineerror(PARSE_DCTTRMNTR, ERROR_PARSE, PARSE_DCTTRMNTR_MSG);\n    morpho_defineerror(PARSE_SWTCHSPRTR, ERROR_PARSE, PARSE_SWTCHSPRTR_MSG);\n    morpho_defineerror(PARSE_DCTENTRYSPRTR, ERROR_PARSE, PARSE_DCTENTRYSPRTR_MSG);\n    morpho_defineerror(PARSE_EXPCTWHL, ERROR_PARSE, PARSE_EXPCTWHL_MSG);\n    morpho_defineerror(PARSE_EXPCTCTCH, ERROR_PARSE, PARSE_EXPCTCTCH_MSG);\n    morpho_defineerror(PARSE_ONEVARPR, ERROR_PARSE, PARSE_ONEVARPR_MSG);\n    morpho_defineerror(PARSE_CATCHLEFTCURLYMISSING, ERROR_PARSE, PARSE_CATCHLEFTCURLYMISSING_MSG);\n    morpho_defineerror(PARSE_VALRANGE, ERROR_PARSE, PARSE_VALRANGE_MSG);\n    morpho_defineerror(PARSE_STRESC, ERROR_PARSE, PARSE_STRESC_MSG);\n    morpho_defineerror(PARSE_RCRSNLMT, ERROR_PARSE, PARSE_RCRSNLMT_MSG);\n    morpho_defineerror(PARSE_UNESCPDCTRL, ERROR_PARSE, PARSE_UNESCPDCTRL_MSG);\n    morpho_defineerror(PARSE_INVLDUNCD, ERROR_PARSE, PARSE_INVLDUNCD_MSG);\n}\n"
  },
  {
    "path": "src/support/parse.h",
    "content": "/** @file parse.h\n *  @author T J Atherton and others (see below)\n *\n *  @brief Parser\n*/\n\n#ifndef parse_h\n#define parse_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"lex.h\"\n#include \"syntaxtree.h\"\n\n/* -------------------------------------------------------\n * Parser data types\n * ------------------------------------------------------- */\n\n/** Parser type defined below */\ntypedef struct sparser parser;\n\n/* -------------------------------------------------------\n * The parser is defined by parserules that respond to\n * various token types\n * ------------------------------------------------------- */\n\n/** @brief an enumerated type that defines precedence order. */\nenum {\n    PREC_NONE,\n    PREC_LOWEST,\n    PREC_ASSIGN,\n    PREC_TERNARY,\n    PREC_COMMA,\n    PREC_OR,\n    PREC_AND,\n    PREC_EQUALITY,\n    PREC_COMPARISON,\n    PREC_RANGE,\n    PREC_TERM,\n    PREC_FACTOR,\n    PREC_UNARY,\n    PREC_POW,\n    PREC_CALL,\n    PREC_HIGHEST\n};\n\n/** Precedence order */\ntypedef int precedence;\n\n/** @brief Definition of a parse function. */\ntypedef bool (*parsefunction) (parser *c, void *out);\n\n/** @brief A parse rule will be defined for each token,\n * providing functions to parse the token if it is encountered in the\n * prefix or infix positions. The parse rule also defines the precedence. */\ntypedef struct {\n    tokentype type;\n    parsefunction prefix;\n    parsefunction infix;\n    precedence precedence;\n} parserule;\n\n/** @brief Macros used to build a parser definition table\n *  Each line in the table defines the parserule(s) for a specific token type.  */\n#define PARSERULE_UNUSED(tok)                         { tok, NULL,    NULL,    PREC_NONE }\n#define PARSERULE_PREFIX(tok, fn)                     { tok, fn,      NULL,    PREC_NONE }\n#define PARSERULE_INFIX(tok, fn, prec)                { tok, NULL,    fn,      prec      }\n#define PARSERULE_MIXFIX(tok, unaryfn, infixfn, prec) { tok, unaryfn, infixfn, prec      }\n\n/** Varrays of parse rules */\nDECLARE_VARRAY(parserule, parserule)\n\n/* -------------------------------------------------------\n * Define a Parser\n * ------------------------------------------------------- */\n\n/** Maximum depth of recursion for a parser */\n#define PARSE_MAXRECURSIONDEPTH 1000\n\n/** @brief A structure that defines the state of a parser */\nstruct sparser {\n    token current; /** The current token */\n    token previous; /** The previous token */\n    syntaxtreeindx left;\n    \n    int maxrecursiondepth; /** Maximum recursion depth */\n    int recursiondepth; /** Recursion depth */\n    \n    lexer *lex; /** Lexer to use */\n    void *out; /** Output */\n    error *err; /** Error structure to output errors to */\n    bool nl; /** Was a newline encountered before the current token? */\n    \n    // Customize parser\n    bool skipnewline; /** Whether to advance past newline tokens or return them */\n    tokentype toknewline; /** Newline token type */\n    parsefunction baseparsefn; /** Base parse function */\n    varray_parserule parsetable; /** Table of parse rules */\n    \n    varray_value objects; /** Hold objects generated while parsing */\n};\n\n/* -------------------------------------------------------\n * Parser error messages\n * ------------------------------------------------------- */\n\n#define PARSE_INCOMPLETEEXPRESSION        \"IncExp\"\n#define PARSE_INCOMPLETEEXPRESSION_MSG    \"Incomplete expression.\"\n\n#define PARSE_MISSINGPARENTHESIS          \"MssngParen\"\n#define PARSE_MISSINGPARENTHESIS_MSG      \"Expect ')' after expression.\"\n\n#define PARSE_EXPECTEXPRESSION            \"ExpExpr\"\n#define PARSE_EXPECTEXPRESSION_MSG        \"Expected expression.\"\n\n#define PARSE_MISSINGSEMICOLON            \"MssngSemiVal\"\n#define PARSE_MISSINGSEMICOLON_MSG        \"Expect ; after value.\"\n\n#define PARSE_MISSINGSEMICOLONEXP         \"MssngExpTerm\"\n#define PARSE_MISSINGSEMICOLONEXP_MSG     \"Expect expression terminator (; or newline) after expression.\"\n\n#define PARSE_MISSINGSEMICOLONVAR         \"MssngSemiVar\"\n#define PARSE_MISSINGSEMICOLONVAR_MSG     \"Expect ; after variable declaration.\"\n\n#define PARSE_VAREXPECTED                 \"VarExpct\"\n#define PARSE_VAREXPECTED_MSG             \"Variable name expected after var.\"\n\n#define PARSE_SYMBLEXPECTED               \"SymblExpct\"\n#define PARSE_SYMBLEXPECTED_MSG           \"Symbol expected.\"\n\n#define PARSE_BLOCKTERMINATOREXP          \"MssngBrc\"\n#define PARSE_BLOCKTERMINATOREXP_MSG      \"Expected '}' to finish block.\"\n\n#define PARSE_MSSNGSQBRC                  \"MssngSqBrc\"\n#define PARSE_MSSNGSQBRC_MSG              \"Expected ']' to finish list.\"\n\n#define PARSE_MSSNGCOMMA                  \"MssngComma\"\n#define PARSE_MSSNGCOMMA_MSG              \"Expected ','.\"\n\n#define PARSE_TRNRYMSSNGCOLON             \"TrnryMssngColon\"\n#define PARSE_TRNRYMSSNGCOLON_MSG         \"Expected ':' after expression in ternary operator.\"\n\n#define PARSE_IFLFTPARENMISSING           \"IfMssngLftPrn\"\n#define PARSE_IFLFTPARENMISSING_MSG       \"Expected '(' after if.\"\n\n#define PARSE_IFRGHTPARENMISSING          \"IfMssngRgtPrn\"\n#define PARSE_IFRGHTPARENMISSING_MSG      \"Expected ')' after condition.\"\n\n#define PARSE_WHILELFTPARENMISSING        \"WhlMssngLftPrn\"\n#define PARSE_WHILELFTPARENMISSING_MSG    \"Expected '(' after while.\"\n\n#define PARSE_FORLFTPARENMISSING          \"ForMssngLftPrn\"\n#define PARSE_FORLFTPARENMISSING_MSG      \"Expected '(' after for.\"\n\n#define PARSE_FORSEMICOLONMISSING         \"ForMssngSemi\"\n#define PARSE_FORSEMICOLONMISSING_MSG     \"Expected ';'.\"\n\n#define PARSE_FORRGHTPARENMISSING         \"ForMssngRgtPrn\"\n#define PARSE_FORRGHTPARENMISSING_MSG     \"Expected ')' after for clauses.\"\n\n#define PARSE_FNNAMEMISSING               \"FnNoName\"\n#define PARSE_FNNAMEMISSING_MSG           \"Expected function or method name.\"\n\n#define PARSE_FNLEFTPARENMISSING          \"FnMssngLftPrn\"\n#define PARSE_FNLEFTPARENMISSING_MSG      \"Expect '(' after name.\"\n\n#define PARSE_FNRGHTPARENMISSING          \"FnMssngRgtPrn\"\n#define PARSE_FNRGHTPARENMISSING_MSG      \"Expect ')' after parameters.\"\n\n#define PARSE_FNLEFTCURLYMISSING          \"FnMssngLftBrc\"\n#define PARSE_FNLEFTCURLYMISSING_MSG      \"Expect '{' before body.\"\n\n#define PARSE_CALLRGHTPARENMISSING        \"CllMssngRgtPrn\"\n#define PARSE_CALLRGHTPARENMISSING_MSG    \"Expect ')' after arguments.\"\n\n#define PARSE_EXPECTCLASSNAME             \"ClsNmMssng\"\n#define PARSE_EXPECTCLASSNAME_MSG         \"Expect class name.\"\n\n#define PARSE_CLASSLEFTCURLYMISSING       \"ClsMssngLftBrc\"\n#define PARSE_CLASSLEFTCURLYMISSING_MSG   \"Expect '{' before class body.\"\n\n#define PARSE_CLASSRGHTCURLYMISSING       \"ClsMssngRgtBrc\"\n#define PARSE_CLASSRGHTCURLYMISSING_MSG   \"Expect '}' after class body.\"\n\n#define PARSE_EXPECTDOTAFTERSUPER         \"ExpctDtSpr\"\n#define PARSE_EXPECTDOTAFTERSUPER_MSG     \"Expect '.' after 'super'\"\n\n#define PARSE_INCOMPLETESTRINGINT         \"IntrpIncmp\"\n#define PARSE_INCOMPLETESTRINGINT_MSG     \"Incomplete string after interpolation.\"\n\n#define PARSE_VARBLANKINDEX               \"EmptyIndx\"\n#define PARSE_VARBLANKINDEX_MSG           \"Empty capacity in variable declaration.\"\n\n#define PARSE_IMPORTMISSINGNAME           \"ImprtMssngNm\"\n#define PARSE_IMPORTMISSINGNAME_MSG       \"Import expects a module or file name.\"\n\n#define PARSE_IMPORTMLTPLAS               \"ImprtMltplAs\"\n#define PARSE_IMPORTMLTPLAS_MSG           \"Import statement can only include one 'as' clause.\"\n\n#define PARSE_IMPORTUNEXPCTDTOK           \"ImprtExpctFrAs\"\n#define PARSE_IMPORTUNEXPCTDTOK_MSG       \"Import expects a module or file name followed by for or as.\"\n\n#define PARSE_IMPORTASSYMBL               \"ExpctSymblAftrAs\"\n#define PARSE_IMPORTASSYMBL_MSG           \"Expect symbol after as in import.\"\n\n#define PARSE_IMPORTFORSYMBL              \"ExpctSymblAftrFr\"\n#define PARSE_IMPORTFORSYMBL_MSG          \"Expect symbol(s) after for in import.\"\n\n#define PARSE_EXPECTSUPER                 \"SprNmMssng\"\n#define PARSE_EXPECTSUPER_MSG             \"Expect superclass name.\"\n\n#define PARSE_EXPECTMIXIN                 \"MxnNmMssng\"\n#define PARSE_EXPECTMIXIN_MSG             \"Expect mixin class name.\"\n\n#define PARSE_UNRECGNZEDTOK               \"UnrcgnzdTok\"\n#define PARSE_UNRECGNZEDTOK_MSG           \"Encountered an unrecognized token.\"\n\n#define PARSE_DCTSPRTR                    \"DctSprtr\"\n#define PARSE_DCTSPRTR_MSG                \"Expected a colon separating a key/value pair in dictionary.\"\n\n#define PARSE_SWTCHSPRTR                  \"SwtchSprtr\"\n#define PARSE_SWTCHSPRTR_MSG              \"Expected a colon after label.\"\n\n#define PARSE_DCTENTRYSPRTR               \"DctEntrySprtr\"\n#define PARSE_DCTENTRYSPRTR_MSG           \"Expected a comma separating each dictionary entry.\"\n\n#define PARSE_DCTTRMNTR                   \"DctTrmntr\"\n#define PARSE_DCTTRMNTR_MSG               \"Expected a '}' to end the dictionary.\"\n\n#define PARSE_EXPCTWHL                    \"ExpctWhl\"\n#define PARSE_EXPCTWHL_MSG                \"Expected while after loop body.\"\n\n#define PARSE_EXPCTCTCH                   \"ExpctCtch\"\n#define PARSE_EXPCTCTCH_MSG               \"Expected catch after try statement.\"\n\n#define PARSE_CATCHLEFTCURLYMISSING       \"ExpctHndlr\"\n#define PARSE_CATCHLEFTCURLYMISSING_MSG   \"Expected block of error handlers after catch.\"\n\n#define PARSE_ONEVARPR                    \"OneVarPr\"\n#define PARSE_ONEVARPR_MSG                \"Functions can have only one variadic parameter.\"\n\n#define PARSE_VALRANGE                    \"ValRng\"\n#define PARSE_VALRANGE_MSG                \"Value out of range.\"\n\n#define PARSE_STRESC                      \"StrEsc\"\n#define PARSE_STRESC_MSG                  \"Unrecognized escape sequence in string.\"\n\n#define PARSE_RCRSNLMT                    \"RcrsnLmt\"\n#define PARSE_RCRSNLMT_MSG                \"Recursion depth exceeded.\"\n\n#define PARSE_UNESCPDCTRL                 \"UnescpdCtrl\"\n#define PARSE_UNESCPDCTRL_MSG             \"Unescaped control character in string literal.\"\n\n#define PARSE_INVLDUNCD                   \"InvldUncd\"\n#define PARSE_INVLDUNCD_MSG               \"Invalid unicode escape sequence.\"\n\n/* -------------------------------------------------------\n * Interface for writing a custom parser\n * ------------------------------------------------------- */\n\n// Library functions\nvoid parse_error(parser *p, bool use_prev, errorid id, ... );\nbool parse_advance(parser *p);\nbool parse_precedence(parser *p, precedence prec, void *out);\nbool parse_checktoken(parser *p, tokentype type);\nbool parse_checktokenmulti(parser *p, int n, tokentype *type);\nbool parse_checktokenadvance(parser *p, tokentype type);\nbool parse_checkrequiredtoken(parser *p, tokentype type, errorid id);\nbool parse_checkdisallowedtoken(parser *p, tokentype type, errorid id);\nbool parse_codepointfromhex(parser *p, const char *codestr, int nhex, bool raw, varray_char *out);\nbool parse_stringfromtoken(parser *p, unsigned int start, unsigned int length, value *out);\nvalue parse_tokenasstring(parser *p);\nbool parse_tokenassymbol(parser *p);\n\n// Functions for use when out is a syntaxtree\nbool parse_addnode(parser *p, syntaxtreenodetype type, value content, token *tok, syntaxtreeindx left, syntaxtreeindx right, syntaxtreeindx *out);\nsyntaxtreenode *parse_lookupnode(parser *p, syntaxtreeindx i);\n\n// Find a parserule for a given tokentype\nparserule *parse_getrule(parser *p, tokentype type);\n\n// Validate output\nbool parse_validatestrtol(parser *p, long f);\nbool parse_validatestrtod(parser *p, double f);\n\n// Convert tokens to C types\nbool parse_tokentointeger(parser *p, long *i);\nbool parse_tokentodouble(parser *p, double *x);\n\n// Recursion depth checking\nbool parse_incrementrecursiondepth(parser *p);\nbool parse_decrementrecursiondepth(parser *p);\n\n/* -------------------------------------------------------\n * Morpho parse rules\n * ------------------------------------------------------- */\n\n// Fundamental parse rules\nbool parse_expression(parser *p, void *out);\nbool parse_pseudoexpression(parser *p, void *out);\nbool parse_statement(parser *p, void *out);\nbool parse_declaration(parser *p, void *out);\nbool parse_declarationmulti(parser *p, int n, tokentype *end, void *out);\nbool parse_program(parser *p, void *out);\n\n// Declarations\nbool parse_vardeclaration(parser *p, void *out);\nbool parse_functiondeclaration(parser *p, void *out);\nbool parse_classdeclaration(parser *p, void *out);\nbool parse_importdeclaration(parser *p, void *out);\n\n// Statements\nbool parse_printstatement(parser *p, void *out);\nbool parse_expressionstatement(parser *p, void *out);\nbool parse_blockstatement(parser *p, void *out);\nbool parse_ifstatement(parser *p, void *out);\nbool parse_whilestatement(parser *p, void *out);\nbool parse_forstatement(parser *p, void *out);\nbool parse_dostatement(parser *p, void *out);\nbool parse_breakstatement(parser *p, void *out);\nbool parse_returnstatement(parser *p, void *out);\nbool parse_trystatement(parser *p, void *out);\nbool parse_breakpointstatement(parser *p, void *out);\n\n// Simple literals\nbool parse_nil(parser *p, void *out);\nbool parse_integer(parser *p, void *out);\nbool parse_number(parser *p, void *out);\nbool parse_complex(parser *p, void *out);\nbool parse_bool(parser *p, void *out);\nbool parse_self(parser *p, void *out);\nbool parse_super(parser *p, void *out);\nbool parse_symbol(parser *p, void *out);\n\n// Compound objects\nbool parse_string(parser *p, void *out);\nbool parse_dictionary(parser *p, void *out);\nbool parse_interpolation(parser *p, void *out);\nbool parse_grouping(parser *p, void *out);\nbool parse_unary(parser *p, void *out);\nbool parse_binary(parser *p, void *out);\nbool parse_assignby(parser *p, void *out);\nbool parse_call(parser *p, void *out);\nbool parse_index(parser *p, void *out);\nbool parse_list(parser *p, void *out);\nbool parse_anonymousfunction(parser *p, void *out);\nbool parse_switch(parser *p, void *out);\n\n/* -------------------------------------------------------\n * Parser interface\n * ------------------------------------------------------- */\n\nvoid parse_init(parser *p, lexer *lex, error *err, void *out);\nvoid parse_clear(parser *p);\n\nbool parse_setparsetable(parser *p, parserule *rules);\nvoid parse_setbaseparsefn(parser *p, parsefunction fn);\nvoid parse_setskipnewline(parser *p, bool skip, tokentype toknewline);\nvoid parse_setmaxrecursiondepth(parser *p, int maxdepth);\n\nbool parse(parser *p);\n\nbool morpho_parse(parser *p);\n\nbool parse_stringtovaluearray(char *string, unsigned int nmax, value *v, unsigned int *n, error *err);\n\nbool parse_value(const char *in, value *out);\n\n// Initialization/finalization\nvoid parse_initialize(void);\nvoid parse_finalize(void);\n\n#endif /* parse_h */\n"
  },
  {
    "path": "src/support/platform.c",
    "content": "/** @file platform.c\n *  @author T J Atherton \n *\n *  @brief Isolates platform dependent code in morpho */\n\n/** Platform-dependent code in morpho arises in several ways: \n *  - Complex numbers for platforms that do not fully implement C99\n *  - Navigating the file system\n *  - APIs for opening dynamic libraries\n *  - APIs for using threads \n *  - Functions that involve time */\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <float.h>\n#include \"build.h\"\n#include \"platform.h\"\n#include \"error.h\"\n\n#ifdef _WIN32\n#include <windows.h>\n#include <wincrypt.h>\n#else \n#define _POSIX_C_SOURCE 199309L\n#include <unistd.h>\n#include <dirent.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <pwd.h>\n#include <time.h>\n#include <dlfcn.h>\n#endif\n\n/* **********************************************************************\n * Platform name\n * ********************************************************************** */\n\nconst char *platform_name(void) {\n#if __APPLE__\n    return MORPHO_PLATFORM_MACOS;\n#elif __linux__\n    return MORPHO_PLATFORM_LINUX;\n#elif __UNIX__\n    return MORPHO_PLATFORM_UNIX;\n#elif defined(_WIN32)\n    return MORPHO_PLATFORM_WINDOWS;\n#endif\n    return NULL; // Unrecognized platform\n}\n\n/* **********************************************************************\n * Random numbers\n * ********************************************************************** */\n\n/** Obtain a number of random bytes from the host platform */\nbool platform_randombytes(char *buffer, size_t nbytes) {\n    bool success=false;\n#ifdef _WIN32\n    HCRYPTPROV hProvider = 0;\n    if (CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) &&\n        CryptGenRandom(hProvider, nbytes, buffer)) {\n        CryptReleaseContext(hProvider, 0);\n        success=true; \n    }\n#else \n    FILE *urandom;\n    /* Initialize from OS random bits */\n    urandom=fopen(\"/dev/urandom\", \"r\");\n    if (urandom) {\n        for(int i=0; i<nbytes; i++) buffer[i]=(char) fgetc(urandom);\n        fclose(urandom);\n        success=true; \n    }\n#endif \n    return success; \n}\n\n/* **********************************************************************\n * Complex arithmetic\n * ********************************************************************** */\n\n#ifdef _WIN32\nMorphoComplex MCAdd(MorphoComplex a, MorphoComplex b) {\n    return MCBuild(creal(a)+creal(b), cimag(a)+cimag(b)); \n}\n\nMorphoComplex MCSub(MorphoComplex a, MorphoComplex b) {\n    return MCBuild(creal(a)-creal(b), cimag(a)-cimag(b)); \n}\n\nMorphoComplex MCDiv(MorphoComplex a, MorphoComplex b) {\n    return _Cmulcr(MCMul(a, conj(b)), 1.0/norm(b));\n}\n\nbool MCSame(MorphoComplex a, MorphoComplex b) {\n    return (creal(a)==creal(b) && cimag(a)==cimag(b));\n}\n#endif\n\n/** Compare two complex numbers using both absolute and relative tolerance */\nbool MCEq(MorphoComplex a, MorphoComplex b) {\n    double diff = cabs(MCSub(a,b));\n    double absa = cabs(a), absb = cabs(b);\n    double absmax = (absa>absb ? absa : absb);\n    return (diff == 0.0) || (absmax > DBL_MIN && diff/absmax <= MORPHO_RELATIVE_EPS);\n}\n\n/* **********************************************************************\n * File system functions\n * ********************************************************************** */\n\n/* Tells if an object at path corresponds to a directory */\nbool platform_isdirectory(const char *path) {\n#ifdef _WIN32\n    DWORD attributes = GetFileAttributes(path);\n    if (attributes==INVALID_FILE_ATTRIBUTES) return false; \n    return (attributes & FILE_ATTRIBUTE_DIRECTORY);\n#else\n   struct stat statbuf;\n   if (stat(path, &statbuf) != 0)\n       return 0;\n   return (bool) S_ISDIR(statbuf.st_mode);\n#endif\n}\n\n/** Returns the maximum size of a file path */\nsize_t platform_maxpathsize(void) {\n#ifdef _WIN32 \n    return (size_t) MAX_PATH;\n#else\n    return pathconf(\"/\", _PC_PATH_MAX);\n#endif \n}\n\n/** Sets the current working directory to path */\nbool platform_setcurrentdirectory(const char *path) {\n#ifdef _WIN32\n    return SetCurrentDirectory(path);\n#else\n    return chdir(path)==0;\n#endif\n}\n\n/** Gets the path for the current working directory */\nbool platform_getcurrentdirectory(char *buffer, size_t size) {\n#ifdef _WIN32 \n    return GetCurrentDirectory(size, buffer);\n#else \n    return getcwd(buffer, size);\n#endif\n}\n\n/** Gets a path containing the user's home directory */\nbool platform_gethomedirectory(char *buffer, size_t size) {\n#ifdef _WIN32\n    DWORD length = GetEnvironmentVariable(\"USERPROFILE\", buffer, size);\n    return (length!=0 && length<size);\n#else\n    const char *homedir=getenv(\"HOME\"); \n    if (!homedir) {\n        struct passwd *pwd = getpwuid(getuid());\n        if (pwd) homedir = pwd->pw_dir;\n    }\n    if (homedir) strncpy(buffer, homedir, size);\n\n    return homedir;\n#endif\n}\n\n/** Initializes a MorphoDirContents structure with a given path */\nbool platform_directorycontentsinit(MorphoDirContents *contents, const char *path) {\n#ifdef _WIN32\n    size_t len = strlen(path)+3;\n    char srch[len];\n    strcpy(srch,path);\n    strcat(srch, \"/*\"); // Add required wildcard\n\n    bool success=false;\n    contents->handle = FindFirstFile(srch, &contents->finddata);\n    if (contents->handle != INVALID_HANDLE_VALUE) {\n        contents->isvalid=true; // Used by platform_directorcontentsnext to check that finddata contains valid information\n        success=true; \n    }\n\n    return success; \n#else\n    contents->dir=opendir(path);\n    return contents->dir;\n#endif\n}\n\n/** Clears the contents of a MorphoDirContents structure */\nvoid platform_directorycontentsclear(MorphoDirContents *contents) {\n#ifdef _WIN32\n    FindClose(contents->handle);\n#else\n    closedir(contents->dir);\n#endif\n}\n\n/** Call this function repeatedly to extract the next file in the directory. Returns true if a file is found; the filename is in the buffer. */\nbool platform_directorycontents(MorphoDirContents *contents, char *buffer, size_t size) {\n#ifdef _WIN32\n    if (!contents->isvalid) return false;\n\n    while (strcmp(contents->finddata.cFileName, \".\")==0 ||\n           strcmp(contents->finddata.cFileName, \"..\")==0) {\n\n        if (FindNextFile(contents->handle, &contents->finddata)==0) return false; \n    }\n\n    strncpy(buffer, contents->finddata.cFileName, size);\n\n    // Fetch next file and record whether it succeeded\n    contents->isvalid=(FindNextFile(contents->handle, &contents->finddata)!=0);\n    return true; \n#else\n    struct dirent *entry;\n    do {\n        entry = readdir(contents->dir);\n        if (!entry) return false;\n    } while (strcmp(entry->d_name, \".\")==0 ||\n             strcmp(entry->d_name, \"..\")==0); // Skip links to this and parent folder\n    \n    strncpy(buffer, entry->d_name, size);\n    return true;\n#endif\n}\n\n/* **********************************************************************\n * Dynamic libraries\n * ********************************************************************** */\n\n/** Opens a dynamic library, returning a handle for future use */\nMorphoDLHandle platform_dlopen(const char *path) {\n#ifdef _WIN32\n    return LoadLibrary((LPCSTR) path);\n#else\n    return dlopen(path, RTLD_LAZY);\n#endif\n}\n\n/** Closes a dynamic libary */\nvoid platform_dlclose(MorphoDLHandle handle) {\n#ifdef _WIN32\n    FreeLibrary(handle);\n#else\n    dlclose(handle);\n#endif\n}\n\n/** Looks up a symbol in a dynamic library */\nvoid *platform_dlsym(MorphoDLHandle handle, const char *symbol) {\n#ifdef _WIN32\n    return (void *) GetProcAddress(handle, symbol);\n#else \n    return dlsym(handle, symbol);\n#endif\n}\n\n/* **********************************************************************\n * Threads\n * ********************************************************************** */\n\nDEFINE_VARRAY(MorphoThread, MorphoThread);\n\n/** Creates a thread */\nbool MorphoThread_create(MorphoThread *thread, MorphoThreadFn threadfn, void *ref) {\n#ifdef _WIN32\n    DWORD threadId; \n    *thread = CreateThread(NULL, // Default security attributes\n                           0, // Default stack size\n                           threadfn, \n                           ref,\n                           0, // Default creation flags\n                           &threadId); \n    return (*thread!=NULL);\n#else\n    return (pthread_create(thread, NULL, threadfn, ref)==0);\n#endif\n}\n\n/** Waits for a thread to finish */\nvoid MorphoThread_join(MorphoThread thread) {\n#ifdef _WIN32\n    WaitForSingleObject(thread, INFINITE);\n#else \n    pthread_join(thread, NULL);\n#endif\n}\n\n/** Clears a thread, releasing any resources used */\nvoid MorphoThread_clear(MorphoThread thread) {\n#ifdef _WIN32\n    CloseHandle(thread);\n#endif\n}\n\n/** Exits a thread */\nvoid MorphoThread_exit(void) {\n#ifdef _WIN32\n    ExitThread(0);\n#else \n    pthread_exit(NULL);\n#endif\n}\n\n/** Initializes a mutex */\nbool MorphoMutex_init(MorphoMutex *mutex) {\n#ifdef _WIN32\n    InitializeCriticalSection(mutex);\n    return true; \n#else \n    return pthread_mutex_init(mutex, NULL)==0;\n#endif\n}\n\n/** Clears a mutex */\nvoid MorphoMutex_clear(MorphoMutex *mutex) {\n#ifdef _WIN32\n    DeleteCriticalSection(mutex);\n#else \n    pthread_mutex_destroy(mutex);\n#endif\n}\n\n/** Locks a mutex */\nvoid MorphoMutex_lock(MorphoMutex *mutex) {\n#ifdef _WIN32\n    EnterCriticalSection(mutex);\n#else \n    pthread_mutex_lock(mutex);\n#endif\n}\n\n/** Unlocks a mutex */\nvoid MorphoMutex_unlock(MorphoMutex *mutex) {\n#ifdef _WIN32\n    LeaveCriticalSection(mutex);\n#else \n    pthread_mutex_unlock(mutex);\n#endif\n}\n\n/** Initializes a condition variable */\nbool MorphoCond_init(MorphoCond *cond) {\n#ifdef _WIN32\n    InitializeConditionVariable(cond);\n    return true;\n#else \n    return (pthread_cond_init(cond, NULL)==0);\n#endif\n}\n\n/** Clears a condition variable */\nvoid MorphoCond_clear(MorphoCond *cond) {\n#ifdef _WIN32\n    /* Condition variables don't require cleanup on windows */\n#else \n    pthread_cond_destroy(cond);\n#endif\n}\n\n/** Signals condition variable, waking up a waiting thread */\nvoid MorphoCond_signal(MorphoCond *cond) {\n#ifdef _WIN32\n    WakeConditionVariable(cond);\n#else \n    pthread_cond_signal(cond);\n#endif\n}\n\n/** Wake all threads waiting on a condition variable */\nvoid MorphoCond_broadcast(MorphoCond *cond) {\n#ifdef _WIN32\n    WakeAllConditionVariable(cond);\n#else \n    pthread_cond_broadcast(cond);\n#endif\n}\n\n/** Waits for the condition variable to be signalled */\nvoid MorphoCond_wait(MorphoCond *cond, MorphoMutex *mutex) {\n#ifdef _WIN32\n    SleepConditionVariableCS(cond, mutex, INFINITE);\n#else \n    pthread_cond_wait(cond, mutex);\n#endif\n}\n\n/* **********************************************************************\n * Time\n * ********************************************************************** */\n\n/** Returns the system clock time in seconds. */\ndouble platform_clock(void) {\n#ifdef _WIN32\n    SYSTEMTIME st;\n    GetSystemTime(&st);\n    double seconds = st.wMilliseconds*1e-3 +\n                     st.wSecond +\n                     st.wMinute*60 +\n                     st.wHour*3600; \n    return seconds;\n#else\n    struct timeval tv;\n    gettimeofday(&tv, NULL);\n    return ((double) tv.tv_sec) + tv.tv_usec * 1e-6;\n#endif\n}\n\n/** Sleep for a specified number of milliseconds */\nvoid platform_sleep(int msecs) {\n#ifdef _WIN32\n    Sleep(msecs);\n#else\n    struct timespec t;\n    t.tv_sec  =  msecs / 1000;\n    t.tv_nsec = (msecs % 1000) * 1000000;\n    nanosleep(&t, NULL);\n#endif\n}\n"
  },
  {
    "path": "src/support/platform.h",
    "content": "/** @file platform.h\n *  @author T J Atherton \n *\n *  @brief Isolates platform dependent code in morpho\n*/\n\n#ifndef platform_h\n#define platform_h\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <complex.h>\n#include \"varray.h\"\n\n#ifdef _WIN32\n#include <windows.h>\n#else\n#include <dirent.h>\n#include <pthread.h>\n#endif\n\n/* -------------------------------------------------------\n * Detecting platform name\n * ------------------------------------------------------- */\n\n#define MORPHO_PLATFORM_MACOS                  \"macos\"\n#define MORPHO_PLATFORM_LINUX                  \"linux\"\n#define MORPHO_PLATFORM_UNIX                   \"unix\"\n#define MORPHO_PLATFORM_WINDOWS                \"windows\"\n\nconst char *platform_name(void);\n\n/* -------------------------------------------------------\n * Random numbers\n * ------------------------------------------------------- */\n\nbool platform_randombytes(char *buffer, size_t nbytes);\n\n/* -------------------------------------------------------\n * Complex numbers\n * ------------------------------------------------------- */\n\n/** The windows C library has only partial support for C99 complex numbers, \n *  which are implemented as a struct rather than a native type. While\n *  C99 complex functions are provided, basic arithmetic operations don't\n *  work. Hence we provide a type, as well as several macros and functions \n *  to fill in missing functionality: */\n\n#ifdef _WIN32\ntypedef _Dcomplex MorphoComplex;\n#define MCBuild(re,im) _Cbuild(re, im)\n\nMorphoComplex MCAdd(MorphoComplex a, MorphoComplex b);\nMorphoComplex MCSub(MorphoComplex a, MorphoComplex b);\n#define MCMul(a,b) (_Cmulcc(a,b))\n#define MCScale(a,b) (_Cmulcr(a,b))\nMorphoComplex MCDiv(MorphoComplex a, MorphoComplex b);\nbool MCSame(MorphoComplex a, MorphoComplex b);\n#else\ntypedef double complex MorphoComplex;\n#define MCBuild(re,im) (re + I * im)\n\n#define MCAdd(a, b) (a + b)\n#define MCSub(a, b) (a - b)\n#define MCMul(a, b) (a * b)\n#define MCScale(a, b) (a * b)\n#define MCDiv(a, b) (a / b)\n#define MCSame(a, b) (a == b)\n#endif\n\nbool MCEq(MorphoComplex a, MorphoComplex b);\n\n/* -------------------------------------------------------\n * Navigating the file system\n * ------------------------------------------------------- */\n\nsize_t platform_maxpathsize(void);\nbool platform_setcurrentdirectory(const char *path);\nbool platform_getcurrentdirectory(char *buffer, size_t size);\nbool platform_gethomedirectory(char *buffer, size_t size);\nbool platform_isdirectory(const char *path);\n\ntypedef struct {\n#ifdef _WIN32\n    bool isvalid; \n    WIN32_FIND_DATA finddata;\n    HANDLE handle;\n#else\n    DIR *dir;\n#endif\n} MorphoDirContents;\n\nbool platform_directorycontentsinit(MorphoDirContents *contents, const char *path);\nvoid platform_directorycontentsclear(MorphoDirContents *contents);\nbool platform_directorycontents(MorphoDirContents *contents, char *buffer, size_t size);\n\n/* -------------------------------------------------------\n * Dynamic libraries\n * ------------------------------------------------------- */\n\n#ifdef _WIN32\ntypedef HMODULE MorphoDLHandle;\n#else \ntypedef void* MorphoDLHandle;\n#endif\n\nMorphoDLHandle platform_dlopen(const char *path);\nvoid platform_dlclose(MorphoDLHandle handle);\nvoid *platform_dlsym(MorphoDLHandle handle, const char *symbol);\n\nbool morpho_isdirectory(const char *path);\n\n/* -------------------------------------------------------\n * Threads\n * ------------------------------------------------------- */\n\n#ifdef _WIN32\ntypedef HANDLE             MorphoThread;\ntypedef CRITICAL_SECTION   MorphoMutex;\ntypedef CONDITION_VARIABLE MorphoCond;\ntypedef DWORD              MorphoThreadFnReturnType; \ntypedef DWORD (*MorphoThreadFn)(void *);\n#else \ntypedef pthread_t          MorphoThread;\ntypedef pthread_mutex_t    MorphoMutex;\ntypedef pthread_cond_t     MorphoCond;\ntypedef void*              MorphoThreadFnReturnType;\ntypedef void* (*MorphoThreadFn)(void *);\n#endif\n\nDECLARE_VARRAY(MorphoThread, MorphoThread);\n\nbool MorphoThread_create(MorphoThread *thread, MorphoThreadFn threadfn, void *ref);\nvoid MorphoThread_join(MorphoThread thread);\nvoid MorphoThread_clear(MorphoThread thread);\nvoid MorphoThread_exit(void);\n\nbool MorphoMutex_init(MorphoMutex *mutex);\nvoid MorphoMutex_clear(MorphoMutex *mutex);\nvoid MorphoMutex_lock(MorphoMutex *mutex);\nvoid MorphoMutex_unlock(MorphoMutex *mutex);\n\nbool MorphoCond_init(MorphoCond *cond);\nvoid MorphoCond_clear(MorphoCond *cond);\nvoid MorphoCond_signal(MorphoCond *cond);\nvoid MorphoCond_broadcast(MorphoCond *cond);\nvoid MorphoCond_wait(MorphoCond *cond, MorphoMutex *mutex);\n\n/* -------------------------------------------------------\n * Time\n * ------------------------------------------------------- */\n\ndouble platform_clock(void);\nvoid platform_sleep(int msecs);\n\n#endif\n"
  },
  {
    "path": "src/support/random.c",
    "content": "/** @file random.c\n *  @author T J Atherton and others (see below)\n *\n *  @brief Random number generation\n*/\n\n#include \"random.h\"\n#include \"platform.h\"\n\n/* **********************************************************************\n * Splitmix64 (used for initialization purposes only)\n * ********************************************************************** */\n\n/*  Written in 2015 by Sebastiano Vigna (vigna@acm.org)\n    To the extent possible under law, the author has dedicated all copyright\n    and related and neighboring rights to this software to the public domain\n    worldwide. This software is distributed without any warranty.\n    See <http://creativecommons.org/publicdomain/zero/1.0/>.\n\n    This is a fixed-increment version of Java 8's SplittableRandom generator\n    See http://dx.doi.org/10.1145/2714064.2660195 and\n    http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html\n    It is a very fast generator passing BigCrush, and it can be useful if\n    for some reason you absolutely want 64 bits of state; otherwise, we\n    rather suggest to use a xoroshiro128+ (for moderately parallel\n    computations) or xorshift1024* (for massively parallel computations)\n    generator. */\n\nuint64_t splitmix64_state; /* The state can be seeded with any value. */\n\n/** Get the next random number generated by splitmix64 */\nstatic inline uint64_t splitmix64_next(void) {\n    uint64_t z = (splitmix64_state += UINT64_C(0x9E3779B97F4A7C15));\n    z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);\n    z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB);\n    return z ^ (z >> 31);\n}\n\n/** Set the seed for splitmix64 */\nvoid splitmix64_seed(uint64_t seed) {\n    splitmix64_state=seed;\n}\n\n/* **********************************************************************\n * xoshiro256++\n * ********************************************************************** */\n\n/*  Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org)\n\n    To the extent possible under law, the author has dedicated all copyright\n    and related and neighboring rights to this software to the public domain\n    worldwide. This software is distributed without any warranty.\n\n    See <http://creativecommons.org/publicdomain/zero/1.0/>.\n\n    This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.\n    It has excellent (sub-ns) speed, a state (256 bits) that is large\n    enough for any parallel application, and it passes all tests we are\n    aware of.\n\n    For generating just floating-point numbers, xoshiro256+ is even faster.\n\n    The state must be seeded so that it is not everywhere zero. If you have\n    a 64-bit seed, we suggest to seed a splitmix64 generator and use its\n    output to fill s. */\n\nstatic inline uint64_t xoshiro256pp_rotl(const uint64_t x, int k) {\n    return (x << k) | (x >> (64 - k));\n}\n\nstatic uint64_t xoshiro256pp_state[4];\n\nstatic inline uint64_t next(void) {\n    const uint64_t result = xoshiro256pp_rotl(xoshiro256pp_state[0] + xoshiro256pp_state[3], 23) + xoshiro256pp_state[0];\n\n    const uint64_t t = xoshiro256pp_state[1] << 17;\n\n    xoshiro256pp_state[2] ^= xoshiro256pp_state[0];\n    xoshiro256pp_state[3] ^= xoshiro256pp_state[1];\n    xoshiro256pp_state[1] ^= xoshiro256pp_state[2];\n    xoshiro256pp_state[0] ^= xoshiro256pp_state[3];\n\n    xoshiro256pp_state[2] ^= t;\n\n    xoshiro256pp_state[3] = xoshiro256pp_rotl(xoshiro256pp_state[3], 45);\n\n    return result;\n}\n\n/* This is the jump function for the generator. It is equivalent\n   to 2^128 calls to next(); it can be used to generate 2^128\n   non-overlapping subsequences for parallel computations. */\n\nvoid xoshiro256pp_jump(void) {\n    static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };\n\n    uint64_t s0 = 0;\n    uint64_t s1 = 0;\n    uint64_t s2 = 0;\n    uint64_t s3 = 0;\n    for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++)\n        for(int b = 0; b < 64; b++) {\n            if (JUMP[i] & UINT64_C(1) << b) {\n                s0 ^= xoshiro256pp_state[0];\n                s1 ^= xoshiro256pp_state[1];\n                s2 ^= xoshiro256pp_state[2];\n                s3 ^= xoshiro256pp_state[3];\n            }\n            next();\n        }\n        \n    xoshiro256pp_state[0] = s0;\n    xoshiro256pp_state[1] = s1;\n    xoshiro256pp_state[2] = s2;\n    xoshiro256pp_state[3] = s3;\n}\n\n/* This is the long-jump function for the generator. It is equivalent to\n   2^192 calls to next(); it can be used to generate 2^64 starting points,\n   from each of which jump() will generate 2^64 non-overlapping\n   subsequences for parallel distributed computations. */\n\nvoid xoshiro256pp_longjump(void) {\n    static const uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 };\n\n    uint64_t s0 = 0;\n    uint64_t s1 = 0;\n    uint64_t s2 = 0;\n    uint64_t s3 = 0;\n    for(int i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++)\n        for(int b = 0; b < 64; b++) {\n            if (LONG_JUMP[i] & UINT64_C(1) << b) {\n                s0 ^= xoshiro256pp_state[0];\n                s1 ^= xoshiro256pp_state[1];\n                s2 ^= xoshiro256pp_state[2];\n                s3 ^= xoshiro256pp_state[3];\n            }\n            next();\n        }\n        \n    xoshiro256pp_state[0] = s0;\n    xoshiro256pp_state[1] = s1;\n    xoshiro256pp_state[2] = s2;\n    xoshiro256pp_state[3] = s3;\n}\n\n/* **********************************************************************\n * xoshiro256+\n * ********************************************************************** */\n\n/*  Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)\n\n    To the extent possible under law, the author has dedicated all copyright\n    and related and neighboring rights to this software to the public domain\n    worldwide. This software is distributed without any warranty.\n\n    See <http://creativecommons.org/publicdomain/zero/1.0/>\n\n    This is xoshiro256+ 1.0, our best and fastest generator for floating-point\n    numbers. We suggest to use its upper bits for floating-point\n    generation, as it is slightly faster than xoshiro256++/xoshiro256**. It\n    passes all tests we are aware of except for the lowest three bits,\n    which might fail linearity tests (and just those), so if low linear\n    complexity is not considered an issue (as it is usually the case) it\n    can be used to generate 64-bit outputs, too.\n\n    We suggest to use a sign test to extract a random Boolean value, and\n    right shifts to extract subsets of bits.\n\n    The state must be seeded so that it is not everywhere zero. If you have\n    a 64-bit seed, we suggest to seed a splitmix64 generator and use its\n    output to fill s. */\n\n\nstatic inline uint64_t xoshiro256p_rotl(const uint64_t x, int k) {\n    return (x << k) | (x >> (64 - k));\n}\n\n\nstatic uint64_t xoshiro256p_state[4];\n\nstatic inline uint64_t xoshiro256p_next(void) {\n    const uint64_t result = xoshiro256p_state[0] + xoshiro256p_state[3];\n\n    const uint64_t t = xoshiro256p_state[1] << 17;\n\n    xoshiro256p_state[2] ^= xoshiro256p_state[0];\n    xoshiro256p_state[3] ^= xoshiro256p_state[1];\n    xoshiro256p_state[1] ^= xoshiro256p_state[2];\n    xoshiro256p_state[0] ^= xoshiro256p_state[3];\n\n    xoshiro256p_state[2] ^= t;\n\n    xoshiro256p_state[3] = xoshiro256p_rotl(xoshiro256p_state[3], 45);\n\n    return result;\n}\n\n\n/* This is the jump function for the generator. It is equivalent\n   to 2^128 calls to next(); it can be used to generate 2^128\n   non-overlapping subsequences for parallel computations. */\n\nvoid xoshiro256p_jump(void) {\n    static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };\n\n    uint64_t s0 = 0;\n    uint64_t s1 = 0;\n    uint64_t s2 = 0;\n    uint64_t s3 = 0;\n    for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++)\n        for(int b = 0; b < 64; b++) {\n            if (JUMP[i] & UINT64_C(1) << b) {\n                s0 ^= xoshiro256p_state[0];\n                s1 ^= xoshiro256p_state[1];\n                s2 ^= xoshiro256p_state[2];\n                s3 ^= xoshiro256p_state[3];\n            }\n            next();\n        }\n        \n    xoshiro256p_state[0] = s0;\n    xoshiro256p_state[1] = s1;\n    xoshiro256p_state[2] = s2;\n    xoshiro256p_state[3] = s3;\n}\n\n\n/* This is the long-jump function for the generator. It is equivalent to\n   2^192 calls to next(); it can be used to generate 2^64 starting points,\n   from each of which jump() will generate 2^64 non-overlapping\n   subsequences for parallel distributed computations. */\n\nvoid xoshiro256p_longjump(void) {\n    static const uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 };\n\n    uint64_t s0 = 0;\n    uint64_t s1 = 0;\n    uint64_t s2 = 0;\n    uint64_t s3 = 0;\n    for(int i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++)\n        for(int b = 0; b < 64; b++) {\n            if (LONG_JUMP[i] & UINT64_C(1) << b) {\n                s0 ^= xoshiro256p_state[0];\n                s1 ^= xoshiro256p_state[1];\n                s2 ^= xoshiro256p_state[2];\n                s3 ^= xoshiro256p_state[3];\n            }\n            next();\n        }\n        \n    xoshiro256p_state[0] = s0;\n    xoshiro256p_state[1] = s1;\n    xoshiro256p_state[2] = s2;\n    xoshiro256p_state[3] = s3;\n}\n\n/* **********************************************************************\n * Public interface\n * ********************************************************************** */\n\n/** Generate a random double on the interval [0.0,1.0] */\ndouble random_double(void) {\n    uint64_t x = xoshiro256p_next();\n    \n    return (double) (x >> 11) * 0x1.0p-53;\n}\n\n/** Generate a random 32 bit unsigned int */\nunsigned int random_int(void) {\n    uint64_t x = xoshiro256p_next();\n    \n    return (unsigned int) (x>>32);\n}\n\n/** Initialize the random number generator */\nvoid random_initialize(void) {\n    uint64_t seed = (uint64_t) time(NULL);\n    char bytes[sizeof(uint64_t)];\n\n    if (platform_randombytes(bytes, sizeof(uint64_t))) {\n        seed = *((uint64_t *) bytes);\n    } else {\n        fprintf(stderr, \"Warning: initializing random number generator using time-not recommended for production runs.\\n\");\n    }\n    \n    /* Use this to initialize splitmix64 */\n    splitmix64_seed(seed);\n    \n    /* Then initialize xoshiro256pp */\n    xoshiro256pp_state[0]=splitmix64_next();\n    xoshiro256pp_state[1]=splitmix64_next();\n    xoshiro256pp_state[2]=splitmix64_next();\n    xoshiro256pp_state[3]=splitmix64_next();\n    \n    /* ... and xoshiro256p */\n    xoshiro256p_state[0]=splitmix64_next();\n    xoshiro256p_state[1]=splitmix64_next();\n    xoshiro256p_state[2]=splitmix64_next();\n    xoshiro256p_state[3]=splitmix64_next();\n}\n"
  },
  {
    "path": "src/support/random.h",
    "content": "/** @file random.c\n *  @author T J Atherton and others (see below)\n *\n *  @brief Random number generation\n*/\n\n#ifndef random_h\n#define random_h\n\n#include <stdio.h>\n#include <stdint.h>\n#include <time.h>\n\ndouble random_double(void);\nunsigned int random_int(void);\nvoid random_initialize(void);\n\n#endif /* random_h */\n"
  },
  {
    "path": "src/support/resources.c",
    "content": "/** @file resources.c\n *  @author T J Atherton\n *\n *  @brief Locates resources from installation folders and packages\n */\n\n#include <stdio.h>\n\n#include \"common.h\"\n#include \"resources.h\"\n#include \"platform.h\"\n#include \"file.h\"\n\n/* **********************************************************************\n * Resource enumerator structure\n * ********************************************************************** */\n\n/** A resource enumerator contains state information to enable the resources system to recursively search various resource locations (e.g. /usr/local/share/morpho/ ) for a specified query. You initialize the  resourceenumerator with a query,\n    then call morpho_enumerateresources until no further resources are found, which is indicated by it returning false. */\n\ntypedef struct {\n    char *folder; // folder specification to scan\n    char *fname;  // filename to match\n    char **ext;   // list of possible extensions, terminated by an empty string\n    bool recurse; // whether to search recursively\n    varray_value resources;\n} resourceenumerator;\n\n/* **********************************************************************\n * Resource types\n * ********************************************************************** */\n\n/** Map morphoresourcetypes to package subfolders.\n   @warning: These must match the order of the morphoresourcetypeenum */\nstatic char *_dir[] = {\n    MORPHO_HELPDIR,\n    MORPHO_MODULEDIR,\n    MORPHO_EXTENSIONDIR\n};\n\n/* Map morphoresourcetypes to extensions */\nstatic char *_helpext[] =      { MORPHO_HELPEXTENSION, \"\" };\nstatic char *_moduleext[] =    { MORPHO_EXTENSION, \"\" };\nstatic char *_extensionext[] = { MORPHO_DYLIBEXTENSION, \"dylib\", \"so\", \"\" };\n\nstatic char **_ext[] = { _helpext, _moduleext, _extensionext };\n\n/* Map morphoresourcetypes to base folders */\nstatic char *_basedir[] = {\n    MORPHO_HELP_BASEDIR,\n    MORPHO_MODULE_BASEDIR,\n    NULL\n};\n\nchar *_folderfortype(morphoresourcetype type) { return _dir[type]; }\nchar **_extfortype(morphoresourcetype type) { return _ext[type]; }\nchar *_basedirfortype(morphoresourcetype type) { return _basedir[type]; }\n\n/* **********************************************************************\n * Resources\n * ********************************************************************** */\n\nvarray_value resourcelocations;\n\n/** Identifies a base folder emanating from path and consistent with resourceenumerator */\nvoid resources_matchbasefolder(resourceenumerator *en, char *path) {\n    varray_char fname;\n    varray_charinit(&fname);\n    varray_charadd(&fname, path, (int) strlen(path));\n    varray_charwrite(&fname, MORPHO_DIRSEPARATOR);\n\n    if (en->folder) varray_charadd(&fname, en->folder, (int) strlen(en->folder));\n    varray_charwrite(&fname, '\\0');\n\n    if (platform_isdirectory(fname.data)) {\n        value v = object_stringfromcstring(fname.data, fname.count);\n        if (MORPHO_ISSTRING(v)) varray_valuewrite(&en->resources, v);\n    }\n\n    varray_charclear(&fname);\n}\n\n/** Locates all possible base folders consistent with the current folder specification\n @param[in] en - initialized enumerator */\nvoid resources_basefolders(resourceenumerator *en) {\n    for (int i=0; i<resourcelocations.count; i++) { // Loop over possible resource folders\n        if (MORPHO_ISSTRING(resourcelocations.data[i])) {\n            resources_matchbasefolder(en, MORPHO_GETCSTRING(resourcelocations.data[i]));\n        }\n    }\n}\n\n/** Finds the character at which the extension separator occurs in a filename. Returns NULL if no extension is present */\nchar *resources_findextension(char *f) {\n    for (char *c = f+strlen(f); c>=f; c--) {\n        if (*c=='.') return c;\n    }\n    \n    return NULL;\n}\n\n/** Checks if a filename matches all criteria in a resourceenumerator\n @param[in] en - initialized enumerator */\nbool resources_matchfile(resourceenumerator *en, char *file) {\n    // Skip extension\n    char *ext = resources_findextension(file);\n    if (!ext) ext = file+strlen(file); // If no extension found, just go to the end of the filename\n\n    if (en->fname) { // Match filename if requested\n        char *f = ext;\n        while (f>=file && *f!=MORPHO_DIRSEPARATOR) f--; // Find last separator\n        if (*f==MORPHO_DIRSEPARATOR) f++; // If we stopped at a separator, skip it\n        \n        size_t len = strlen(en->fname);\n        if (strncmp(en->fname, f, len)!=0) return false; // Compare string\n        if (!(f[len]=='.' || f[len]=='\\0')) return false; // Ensure filename is terminated by null byte or file extension separator.\n    }\n\n    if (!en->ext) return true; // Match extension only if requested\n\n    if (*ext!='.') return false;\n    for (int k=0; *en->ext[k]!='\\0'; k++) { // Check extension against possible extensions\n        if (strcmp(ext+1, en->ext[k])==0) return true; // We have a match\n    }\n\n    return false;\n}\n\n/** Searches a given folder, adding all resources to the enumerator\n @param[in] en - initialized enumerator */\nvoid resources_searchfolder(resourceenumerator *en, char *path) {\n    MorphoDirContents contents;\n    \n    size_t size = platform_maxpathsize();\n    char buffer[size];\n    char sep[2] = { MORPHO_DIRSEPARATOR, '\\0' };\n    \n    if (platform_directorycontentsinit(&contents, path)) {\n        while (platform_directorycontents(&contents, buffer, size)) {\n            /* Construct the file name */\n            size_t len = strlen(path)+strlen(buffer)+2;\n            \n            char file[len];\n            strcpy(file, path);\n            strcat(file, sep);\n            strcat(file, buffer);\n\n            if (platform_isdirectory(file)) {\n                if (!en->recurse) continue;\n            } else {\n                if (!resources_matchfile(en, file)) continue;\n            }\n\n            /* Add the file or folder to the work list */\n            value v = object_stringfromcstring(file, len);\n            if (MORPHO_ISSTRING(v)) varray_valuewrite(&en->resources, v);\n        }\n        platform_directorycontentsclear(&contents);\n    }\n}\n\n/** Initialize a resource enumerator\n @param[in] en - enumerator to initialize\n @param[in] folder - folder specification to scan\n @param[in] fname - filename to match\n @param[in] ext - list of possible extensions, terminated by an empty string\n @param[in] recurse - search recursively */\nvoid resourceenumerator_init(resourceenumerator *en, char *folder, char *fname, char *ext[], bool recurse) {\n    en->folder = folder;\n    en->fname = fname;\n    en->ext = ext;\n    en->recurse = recurse;\n    varray_valueinit(&en->resources);\n    resources_basefolders(en);\n}\n\n/** Clears a resource enumerator\n @param[in] en - enumerator to clear */\nvoid resourceenumerator_clear(resourceenumerator *en) {\n    for (int i=0; i<en->resources.count; i++) morpho_freeobject(en->resources.data[i]);\n    varray_valueclear(&en->resources);\n}\n\n/** Enumerates resources\n @param[in] en - enumerator to use\n @param[out] out - next resource */\nbool resourceenumerator_enumerate(resourceenumerator *en, value *out) {\n    if (en->resources.count==0) return false;\n    value next = en->resources.data[--en->resources.count];\n\n    while (platform_isdirectory(MORPHO_GETCSTRING(next))) {\n        resources_searchfolder(en, MORPHO_GETCSTRING(next));\n        morpho_freeobject(next);\n        if (en->resources.count==0) return false;\n        next = en->resources.data[--en->resources.count];\n    }\n\n    *out = next;\n    return true;\n}\n\n/** Adds the default folder for a given resource type */\nvoid resourceenumerator_defaultfolder(resourceenumerator *en, morphoresourcetype type) {\n    char *basedir = _basedirfortype(type);\n    if (basedir) {\n        value v = object_stringfromcstring(basedir, strlen(basedir));\n        if (MORPHO_ISSTRING(v)) varray_valuewrite(&en->resources, v);\n    }\n}\n\n/** Locates a resource\n @param[in] type - type of resource to locate\n @param[in] fname - filename to match\n @param[out] out - an objectstring that contains the resource file location */\nbool morpho_findresource(morphoresourcetype type, char *fname, value *out) {\n    char *folder = _folderfortype(type);\n    char **ext = _extfortype(type);\n    \n    bool success=false;\n    resourceenumerator en;\n    resourceenumerator_init(&en, folder, fname, ext, true);\n    resourceenumerator_defaultfolder(&en, type);\n    success=resourceenumerator_enumerate(&en, out);\n    resourceenumerator_clear(&en);\n    return success;\n}\n\n/** Locates all resources of a given type\n @param[in] type - type of resource to locate\n @param[out] out - a varray_value that contains the resource file locations */\nbool morpho_listresources(morphoresourcetype type, varray_value *out) {\n    char *folder = _folderfortype(type);\n    char **ext = _extfortype(type);\n    \n    resourceenumerator en;\n    resourceenumerator_init(&en, folder, NULL, ext, true);\n    resourceenumerator_defaultfolder(&en, type);\n    value file;\n    while (resourceenumerator_enumerate(&en, &file)) {\n        varray_valuewrite(out, file);\n    }\n    resourceenumerator_clear(&en);\n    return (out->count>0);\n}\n\n/** Loads a list of packages in ~/.morphopackages */\nvoid resources_loadpackagelist(void) {\n    varray_char line;\n    varray_charinit(&line);\n\n    size_t len = platform_maxpathsize();\n    char home[len];\n    if (platform_gethomedirectory(home, len)) {\n        varray_charadd(&line, home, (int) strlen(home));\n    }\n\n    varray_charwrite(&line, MORPHO_DIRSEPARATOR);\n    varray_charadd(&line, MORPHO_PACKAGELIST, (int) strlen(MORPHO_PACKAGELIST));\n    varray_charwrite(&line, '\\0');\n\n    FILE *f = fopen(line.data, \"r\");\n    if (f) {\n        while (!feof(f)) {\n            line.count=0;\n            if (file_readlineintovarray(f, &line) &&\n                line.count>0) {\n                value str = object_stringfromvarraychar(&line);\n                varray_valuewrite(&resourcelocations, str);\n            }\n        }\n        fclose(f);\n    }\n    varray_charclear(&line);\n}\n\nvoid resources_initialize(void) {\n    varray_valueinit(&resourcelocations);\n\n    resources_loadpackagelist();\n    \n    morpho_addfinalizefn(resources_finalize);\n}\n\nvoid resources_finalize(void) {\n    for (int i=0; i<resourcelocations.count; i++) morpho_freeobject(resourcelocations.data[i]);\n    varray_valueclear(&resourcelocations);\n}\n"
  },
  {
    "path": "src/support/resources.h",
    "content": "/** @file resources.h\n *  @author T J Atherton\n *\n *  @brief Locates resources from installation folders and packages\n */\n\n/** Morpho depends on various supporting files, including help files, modules, extensions, etc. The resources system provides a simple interface to locate these. */\n\n#ifndef resources_h\n#define resources_h\n\n#include \"value.h\"\n\n/* -----------------------------------------\n * Resource types\n * ----------------------------------------- */\n\ntypedef enum {\n    MORPHO_RESOURCE_HELP,\n    MORPHO_RESOURCE_MODULE,\n    MORPHO_RESOURCE_EXTENSION\n} morphoresourcetype;\n\n/* -----------------------------------------\n * Resources interface\n * ----------------------------------------- */\n\n/** Interface to locate a specified resource\n @param[in] type - type of resource to find\n @param[in] fname - filename to match\n @param[out] out - an objectstring that contains the resource file location\n @warning: You must free the objectstring after use.*/\nbool morpho_findresource(morphoresourcetype type, char *fname, value *out);\n\n/** Interface to locate all resources of a given type\n @param[in] type - type of resource to find\n @param[out] out - a varray_value that contains resource files\n @warning: You must free the contents of the varray_value after use.*/\nbool morpho_listresources(morphoresourcetype type, varray_value *out);\n\nvoid resources_initialize(void);\nvoid resources_finalize(void);\n\n#endif /* resources_h */\n"
  },
  {
    "path": "src/support/threadpool.c",
    "content": "/** @file threadpool.c\n *  @author T J Atherton\n *\n *  @brief Thread pool \n */\n\n#include <ctype.h>\n#include \"build.h\"\n#include \"threadpool.h\"\n\n/* **********************************************************************\n* Thread pools\n* ********************************************************************** */\n\nint threadpool_nthreads = MORPHO_DEFAULTTHREADNUMBER;\n\n/** Sets the number of worker threads to use */\nvoid morpho_setthreadnumber(int nthreads) {\n    threadpool_nthreads = nthreads;\n}\n\n/** Returns the number of worker threads to use */\nint morpho_threadnumber(void) {\n    return threadpool_nthreads;\n}\n\nDEFINE_VARRAY(task, task);\n\n/* Worker thread */\nMorphoThreadFnReturnType threadpool_worker(void *ref) {\n    threadpool *pool = (threadpool *) ref;\n    task t = { .func = NULL, .arg = NULL };\n\n    while (true) {\n        /* Await a task */\n        MorphoMutex_lock(&pool->lock_mutex);\n        while (pool->queue.count == 0 && !pool->stop)\n            MorphoCond_wait(&pool->work_available_cond, &pool->lock_mutex);\n\n        if (pool->stop) break; /* Terminate if asked to do so */\n\n        varray_taskpop(&pool->queue, &t); /* Get the task */\n        pool->nprocessing++;\n        MorphoMutex_unlock(&pool->lock_mutex);\n\n        if (t.func) { (t.func) (t.arg); }; /* Perform the assigned task */\n\n        MorphoMutex_lock(&pool->lock_mutex);\n        pool->nprocessing--;\n        if (!pool->stop && pool->nprocessing == 0 && pool->queue.count == 0)\n            MorphoCond_signal(&pool->work_halted_cond);\n        MorphoMutex_unlock(&pool->lock_mutex);\n    }\n\n    /* No need to lock here as lock was already obtained */\n    pool->nthreads--;\n    MorphoCond_signal(&pool->work_halted_cond);\n    MorphoMutex_unlock(&pool->lock_mutex);\n\n    return (MorphoThreadFnReturnType) NULL;\n}\n\n/* Interface */\n\n/** Initialize a threadpool with n worker threads. */\nbool threadpool_init(threadpool *pool, int nworkers) {\n    if (nworkers<1) return false;\n\n    varray_taskinit(&pool->queue);\n    varray_MorphoThreadinit(&pool->threads);\n\n    MorphoMutex_init(&pool->lock_mutex);\n    MorphoCond_init(&pool->work_available_cond);\n    MorphoCond_init(&pool->work_halted_cond);\n\n    pool->nthreads=nworkers;\n    pool->stop=false;\n    pool->nprocessing=0;\n\n    for (int i=0; i<pool->nthreads; i++) {\n        MorphoThread thread;\n        MorphoThread_create(&thread, threadpool_worker, pool);\n        varray_MorphoThreadadd(&pool->threads, &thread, 1);\n    }\n\n    return true;\n}\n\n/** Clears a threadpool. */\nvoid threadpool_clear(threadpool *pool) {\n    MorphoMutex_lock(&pool->lock_mutex);\n    varray_taskclear(&pool->queue); /* Erase any remaining tasks */\n    pool->stop = true; /* Tell workers to stop */\n    MorphoCond_broadcast(&pool->work_available_cond); /* Signal to workers to wake up */\n    MorphoMutex_unlock(&pool->lock_mutex);\n\n    for (int i=0; i<pool->threads.count; i++) MorphoThread_join(pool->threads.data[i]);\n\n    MorphoMutex_clear(&pool->lock_mutex);\n    MorphoCond_clear(&pool->work_available_cond);\n    MorphoCond_clear(&pool->work_halted_cond);\n\n    for (int i=0; i<pool->threads.count; i++) MorphoThread_clear(pool->threads.data[i]);\n    \n    varray_MorphoThreadclear(&pool->threads);\n}\n\n/** Adds a task to the threadpool */\nbool threadpool_add_task(threadpool *pool, workfn func, void *arg) {\n    bool success=true;\n    MorphoMutex_lock(&pool->lock_mutex);\n\n    task t = { .func = func, .arg=arg };\n    if (!varray_taskadd(&pool->queue, &t, 1)) success=false; /* Add the task to the queue */\n\n    MorphoCond_broadcast(&pool->work_available_cond); /* Signal there is work to be done */\n    MorphoMutex_unlock(&pool->lock_mutex);\n    return success;\n}\n\n/** Blocks until all tasks in the thread pool are complete */\nvoid threadpool_fence(threadpool *pool) {\n    MorphoMutex_lock(&pool->lock_mutex);\n\n    while (true) {\n        if ((!pool->stop && (pool->queue.count > 0 || pool->nprocessing>0)) || // If we are simply waiting for tasks to finish\n            (pool->stop && pool->nthreads > 0)) { // Or if we have been told to stop\n            MorphoCond_wait(&pool->work_halted_cond, &pool->lock_mutex); // Block until working_cond is set\n        } else break;\n    }\n\n    MorphoMutex_unlock(&pool->lock_mutex);\n}\n"
  },
  {
    "path": "src/support/threadpool.h",
    "content": "/** @file threadpool.h\n *  @author T J Atherton\n *\n *  @brief Thread pool \n */\n\n#ifndef threadpool_h\n#define threadpool_h\n\n#include <stdbool.h>\n#include \"varray.h\"\n#include \"platform.h\"\n\n/* -----------------------------------------\n * Tasks\n * ----------------------------------------- */\n\n/** A task is the basic unit of work that is allocated to the thread pool; it comprises a work function to perform the task, and a single argument. */\n \n/** A workfn will be called by the threadpool once a thread is available.\n    You must supply all relevant information for both input and output in a single structure passed as an opaque reference. */\ntypedef bool (* workfn) (void *arg);\n\ntypedef struct {\n    workfn func;    // Function to call that performs the task\n    void *arg;      // Opaque pointer passed as an argument to the work function\n} task;\n\nDECLARE_VARRAY(task, task);\n\n/* -----------------------------------------\n * Thread pools\n * ----------------------------------------- */\n\ntypedef struct {\n    MorphoMutex lock_mutex; /* Lock for access to threadpool structure. */\n    MorphoCond work_available_cond; /* Signals that work is available. */\n    MorphoCond work_halted_cond; /* Signals when no threads are processing. */\n    int nprocessing; /* Number of threads actively processing work */\n    int nthreads; /* Number of active threads. */\n    bool stop; /* Indicates threads should terminate */\n\n    varray_task queue; /* Queue of tasks lined up */\n    varray_MorphoThread threads; /* Threads created by this pool */\n} threadpool;\n\nbool threadpool_init(threadpool *pool, int nworkers);\nvoid threadpool_clear(threadpool *pool);\nbool threadpool_add_task(threadpool *pool, workfn func, void *arg);\nvoid threadpool_fence(threadpool *pool);\nvoid threadpool_wait(threadpool *pool);\n\n#endif /* threadpool_h */\n"
  },
  {
    "path": "test/apply/apply.morpho",
    "content": "// Apply a function to arguments\n\nfn f(x) {\n  return 1-x^2\n}\n\nprint apply(f, 0.5)\n// expect: 0.75\n\nprint apply(f, 0.1)\n// expect: 0.99\n"
  },
  {
    "path": "test/apply/apply_args.morpho",
    "content": "// Apply a function to arguments\n\nfn f(x,y,z) {\n  return x*x+y*y+z*z;\n}\n\nprint apply(f, 0.5, 0.2, 0.1)\n// expect: 0.3\n\nprint apply(f, 0.5)\n// expect: Error 'InvldArgs'\n"
  },
  {
    "path": "test/apply/apply_builtin.morpho",
    "content": "// Apply a function to arguments\n\nvar a = sin(0.1)\n\nvar b = apply(sin, 0.1)\n\nprint a-b\n// expect: 0\n"
  },
  {
    "path": "test/apply/apply_closure.morpho",
    "content": "// Apply a closure to arguments\n\nfn f(x) {\n  fn g(y) {\n    return x+y\n  }\n  return g\n}\n\nprint apply(apply(f, 2), 3)\n// expect: 5\n"
  },
  {
    "path": "test/apply/apply_list.morpho",
    "content": "// Apply a function to a list of arguments\n\nfn f(x,y) {\n  return x^2+y^2\n}\n\nprint apply(f, [0.5,0.5])\n// expect: 0.5\n\nvar a = [0.1, 0.1]\n\nprint apply(f, a)\n// expect: 0.02\n\nfn g(x) {\n  print x\n}\n\napply(g, [[1,2,3]])\n// expect: [ 1, 2, 3 ]\n"
  },
  {
    "path": "test/apply/apply_method.morpho",
    "content": "// Apply a method\n\nclass Blob {\n  init(x) {\n    self.a = x\n  }\n\n  blip(x) {\n    return self.a+x^2\n  }\n}\n\nvar a = Blob(0.5)\n\nprint a.blip(0.5)\n// expect: 0.75\n\nprint apply(a.blip, 0.1)\n// expect: 0.51\n\nvar p = a.blip\nprint apply(p, 0.1)\n// expect: 0.51\n"
  },
  {
    "path": "test/apply/apply_recursion.morpho",
    "content": "// Apply a function to arguments\n\nfn f(x) {\n  if (x==0) return 0\n  return x+apply(f, x-1)\n}\n\nprint f(60)\n// expect: 1830\n"
  },
  {
    "path": "test/arithmetic/power.morpho",
    "content": "// Test power operator\nprint 2^2\n// expect: 4\n\nprint 2^3^2\n// expect: 512\n\n// Check both integer and float operands work\nprint 2.0^3.0\n// expect: 8\n\nprint 2.0^3\n// expect: 8\n\nprint 2^3.0\n// expect: 8\n\n// Check precedence relative to unary operator\nprint -1^2\n// expect: -1\n\nvar x = -1\nprint x^2\n// expect: 1\n\nprint (-1)^2\n// expect: 1\n"
  },
  {
    "path": "test/arithmetic/redirection.morpho",
    "content": "class Redirect {\n  add (x) {\n    print \"Adding ${x}\"\n  }\n\n  addr (x) {\n    print \"Right adding ${x}\"\n  }\n\n  sub (x) {\n    print \"Subtracting ${x}\"\n  }\n\n  subr (x) {\n    print \"Right subtracting ${x}\"\n  }\n\n  mul (x) {\n    print \"Multiplying ${x}\"\n  }\n\n  mulr (x) {\n    print \"Right multiplying ${x}\"\n  }\n\n  div (x) {\n    print \"Dividing ${x}\"\n  }\n\n  divr (x) {\n    print \"Right dividing ${x}\"\n  }\n}\n\nvar r = Redirect()\n\nr + 1\n// expect: Adding 1\n1 + r\n// expect: Right adding 1\n\nr - 2\n// expect: Subtracting 2\n2 - r\n// expect: Right subtracting 2\n\nr * 3\n// expect: Multiplying 3\n3 * r\n// expect: Right multiplying 3\n\nr / 3\n// expect: Dividing 3\n3 / r\n// expect: Right dividing 3\n\n(-r)\n// expect: Right subtracting 0\n"
  },
  {
    "path": "test/arithmetic/unary_div.morpho",
    "content": "// Ensuring unary minus and operations are compatible\n\nvar a = 1\nvar b = 4\n\nprint -a/b\n// expect: -0.25\n\nprint -a/b\n// expect: -0.25\n"
  },
  {
    "path": "test/array/array.morpho",
    "content": "/*\n * Arrays\n */\n\n// Initialize variable with a list\nvar a = [5, 3]\n\nprint a[0] // expect: 5\nprint a[1] // expect: 3\n\n// Uninitialized array\nvar b[1]\nprint b[0] // expect: 0\n\n// Two dimensional array\nvar c[2,2]\nprint c[1,1] // expect: 0\n\n//= [[1,0],[0,1]]\n\n// Set array element\na[0] = 20\nprint a[0] // expect: 20\n\n// Set array element 2D\nc[0,0]=3\nc[1,1]=4\nprint c[0,0] // expect: 3\nprint c[1,0] // expect: 0\nprint c[1,1] // expect: 4\nprint c[0,1] // expect: 0\n\n// Assign beyond bounds\na[4] = 2 // expect Error 'IndxBnds'\n"
  },
  {
    "path": "test/array/array_assign_beyond_bounds.morpho",
    "content": "\nvar a = [1,2,3]\na[4] = 2 // expect: Error 'IndxBnds' Array index out of bounds.\n"
  },
  {
    "path": "test/array/array_dim_with_initializer.morpho",
    "content": "// Initialize array\n\nvar a[2,2] = [ [ 1, 0 ], [ 2, 0 ] ]\nprint a\n// expect: [ [ 1, 0 ], [ 2, 0 ] ]\n\nfn f() {\n  for (i in 1..2) {\n    var a[2,2] = [ [ 1, 2 ], [ 3, 4 ] ]\n    print a\n  }\n}\n\nf()\n// expect: [ [ 1, 2 ], [ 3, 4 ] ]\n// expect: [ [ 1, 2 ], [ 3, 4 ] ]\n"
  },
  {
    "path": "test/array/array_enumerate.morpho",
    "content": "/* Enumerate the contents of an array */\n\n// Two dimensional array\nvar a[2,2]\na[0,0]=1\na[1,1]=2\n\nfor (x in a) print x\n// expect: 1\n// expect: 0\n// expect: 0\n// expect: 2\n"
  },
  {
    "path": "test/array/array_garbage_collect.morpho",
    "content": "// Creates nested arrays to stress garbage collector\n\n// Recursively make arrays\nfn makearray(size, depth) {\n  if (depth<1) return nil;\n\n  var a[size,size]\n  for (var i=0; i<size; i=i+1) {\n    for (var j=0; j<size; j=j+1) {\n      a[i,j]=makearray(size, depth-1)\n    }\n  }\n\n  return a\n}\n\n// Make a few of them\nfn test() {\n  for (var k=0; k<10; k=k+1) {\n    makearray(4,5)\n    print k\n  }\n}\n\ntest()\n// expect: 0\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n// expect: 5\n// expect: 6\n// expect: 7\n// expect: 8\n// expect: 9\n"
  },
  {
    "path": "test/array/array_incompatible_initializer.morpho",
    "content": "// Initialize array with incompatible dimensions\n\nvar a[2,2]\nprint a\n// expect: [ [ 0, 0 ], [ 0, 0 ] ]\n\nvar b = Array(3,3,a)\n// expect error 'ArrayCmpt'\n"
  },
  {
    "path": "test/array/array_initialize_array.morpho",
    "content": "// Initialize array\n\nvar a[2,2]\na[0,0]=1\na[1,0]=2\nprint a\n// expect: [ [ 1, 0 ], [ 2, 0 ] ]\n\nvar b = Array(2,2,a)\nprint b\n// expect: [ [ 1, 0 ], [ 2, 0 ] ]\n"
  },
  {
    "path": "test/array/array_initialize_incompatible_list.morpho",
    "content": "// Initialize array with incompatible list\n\nvar b = Array(3, 3, [[1,2], [3, 4]])\n// expect error: 'ArrayCmpt'\n"
  },
  {
    "path": "test/array/array_initialize_list.morpho",
    "content": "// Initialize array with list\n\nvar a = Array(4, [1,2,3,4])\nprint a\n// expect: [ 1, 2, 3, 4 ]\n\nvar b = Array(2, 2, [[1,2], [3, 4]])\nprint b\n// expect: [ [ 1, 2 ], [ 3, 4 ] ]\n"
  },
  {
    "path": "test/array/array_nonnum_indices.morpho",
    "content": "\nvar a[3,3] \n\na[\"Squirrel\",3] = 2 // expect: Error 'NonNmIndx' Non-numerical array index.\n"
  },
  {
    "path": "test/array/array_print.morpho",
    "content": "/*\n * Printing arrays\n */\n\n// Uninitialized array\nvar b[5]\nprint b\n// expect: [ 0, 0, 0, 0, 0 ]\n\n// Two dimensional array\nvar c[2,2]\nc[0,0]=1\nc[1,0]=2\nc[0,1]=3\nc[1,1]=4\nprint c\n// expect: [ [ 1, 3 ], [ 2, 4 ] ]\n\n\nc[0,0]=true\nc[1,0]=Object()\nc[0,1]=false\nc[1,1]=nil\nprint c\n// expect: [ [ true, false ], [ <Object>, nil ] ]\n"
  },
  {
    "path": "test/array/array_read_beyond_bounds.morpho",
    "content": "\nvar a = [1,2,3]\nprint a[4] // expect: Error 'IndxBnds' Array index out of bounds.\n"
  },
  {
    "path": "test/array/array_three_dim.morpho",
    "content": "// Three dimensional arrays\n\nvar N=2\nvar a[N,N,N]\n\nvar k=0\nfor (var i=0; i<N; i=i+1) {\n  for (var j=0; j<N; j=j+1) {\n    for (var l=0; l<N; l=l+1) {\n      a[i,j,l]=k\n      k=k+1\n    }\n  }\n}\n\nfor (var i=0; i<N; i=i+1) {\n  for (var j=0; j<N; j=j+1) {\n    for (var l=0; l<N; l=l+1) {\n      print a[i,j,l]\n    }\n  }\n}\n// expect: 0\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n// expect: 5\n// expect: 6\n// expect: 7\n"
  },
  {
    "path": "test/array/array_two_dim.morpho",
    "content": "// Two dimensional arrays\n\nvar a[3,3]\n\nvar k=0\nfor (var i=0; i<3; i=i+1) {\n  for (var j=0; j<3; j=j+1) {\n    a[i,j]=k\n    k=k+1\n  }\n}\n\nfor (var i=0; i<3; i=i+1) {\n  for (var j=0; j<3; j=j+1) {\n    print a[i,j]\n  }\n}\n\n// expect: 0\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n// expect: 5\n// expect: 6\n// expect: 7\n// expect: 8\n"
  },
  {
    "path": "test/array/array_veneer.morpho",
    "content": "// Veneer class methods\n\nvar a[3,3]\n\na[1,1]=\"Hi!\"\n\na.setindex(2,2,\"Bye!\")\n\nprint a.count()\n// expect: 9\n\nprint a.index(1,1)\n// expect: Hi!\n\nprint a.index(2,2)\n// expect: Bye!\n\nprint a.index(4,4)\n// expect Error 'IndxBnds'\n"
  },
  {
    "path": "test/array/array_wrong_dim.morpho",
    "content": "// Using the wrong dimension for an array\n\nvar a[3,3]\na[4] = 2 // expect: Error 'ArrayDim' Incorrect number of dimensions for array.\n"
  },
  {
    "path": "test/array/constructor.morpho",
    "content": "/*\n * Arrays\n */\n\n// Initialize variable with a list\nvar a = Array(3,3)\n\nprint a\n// expect: [ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]\n\nprint a.dimensions()\n// expect: [ 3, 3 ]\n"
  },
  {
    "path": "test/array/inherited.morpho",
    "content": "// Test aing methods inherited from Object \n\nvar a[2] \n\nprint a.respondsto(\"clss\") // expect: true\n\nprint islist(a.respondsto()) // expect: true\n\nprint a.clss() // expect: @Array\n\nprint a.superclass() // expect: @Object\n\nprint islist(a.invoke(\"respondsto\")) // expect: true\n\nprint a.has(\"a\") // expect: false\n\nprint a.count() // expect: 2"
  },
  {
    "path": "test/assignment/associativity.morpho",
    "content": "// Check that assignment is right associative\nvar a = \"Not me!\"\nvar b = \"Or me!\"\nvar c = \"But me!\"\n\na = b = c\nprint a  // expect: But me!\nprint b  // expect: But me!\nprint c  // expect: But me!\n"
  },
  {
    "path": "test/assignment/global.morpho",
    "content": "// Assignment to a global in an expression\n\nvar a = \"before\"\nprint a // expect: before\n\na = \"after\"\nprint a // expect: after\n\n// Make sure assignments evaluate to their rhs\nprint a = \"arg\" // expect: arg\nprint a // expect: arg\n"
  },
  {
    "path": "test/assignment/grouping.morpho",
    "content": "// Check that cannot assign to a quantity in grouping. \n\nvar a = \"a\"\n(a) = \"value\" // Error 'InvldAssgn'\n"
  },
  {
    "path": "test/assignment/infix_operator.morpho",
    "content": "// Check that you can't assign to an expression\n\nvar a = \"a\"\nvar b = \"b\"\na + b = \"value\" // Error 'InvldAssgn'\n"
  },
  {
    "path": "test/assignment/local.morpho",
    "content": "// Check assignment to a local variable\n\n{\n  var a = \"before\"\n  print a // expect: before\n\n  a = \"after\"\n  print a // expect: after\n\n  print a = \"arg\" // expect: arg\n  print a // expect: arg\n}\n"
  },
  {
    "path": "test/assignment/prefix_operator.morpho",
    "content": "// Check that you can't assign to a prefix operator\nvar a = \"a\";\n!a = \"value\"; // Error 'InvldAssgn'\n"
  },
  {
    "path": "test/assignment/shorthand.morpho",
    "content": "// Tests the shorthand operators +=, -=, *=, /=\n\nvar a = 1\nprint a*=2\n// expect: 2\nprint a\n// expect: 2\n\na+=1\nprint a\n// expect: 3\n\na-=2\nprint a\n// expect: 1\n\na/=2\nprint a\n// expect: 0.5\n\nvar b = \"He\"\nb+=\"llo\"\nprint b\n// expect: Hello\n\n// Improves traditional loop readability\nfor (var i=1; i<3; i+=1) {\n  print i\n}\n// expect: 1\n// expect: 2\n\nvar c=8\nc+=c\nprint c\n// expect: 16\n"
  },
  {
    "path": "test/assignment/syntax.morpho",
    "content": "// Check multiple assignments \nvar a = \"before\"\nvar c = a = \"var\"\nprint a // expect: var\nprint c // expect: var\n"
  },
  {
    "path": "test/assignment/to_self.morpho",
    "content": "class Foo {\n  Foo() {\n    self = \"value\" // Error 'InvldAssgn'\n  }\n}\n\nFoo()\n"
  },
  {
    "path": "test/assignment/undefined.morpho",
    "content": "unknown = \"who?\" // expect error 'SymblUndf'\n"
  },
  {
    "path": "test/blank/empty_file.morpho",
    "content": ""
  },
  {
    "path": "test/blank/shebang.morpho",
    "content": "#! /usr/bin/env morpho6\nprint \"Hello world!\"\n// expect: Hello world!"
  },
  {
    "path": "test/block/empty.morpho",
    "content": "{} // By itself.\n\n// In a statement.\nif (true) {}\nif (false) {} else {}\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/block/scope.morpho",
    "content": "var a = \"outer\"\n\n{\n  var a = \"inner\"\n  print a // expect: inner\n}\n\nprint a // expect: outer\n"
  },
  {
    "path": "test/block/scope_error.morpho",
    "content": "{\n  var a = \"inner\"\n}\n\nprint a\n// expect error: 'SymblUndf'\n"
  },
  {
    "path": "test/bool/builtin.morpho",
    "content": "// Test not operator on Boolean values\n\nprint Bool(1) // expect: true\nprint Bool(0) // expect: true\nprint Bool(nil) // expect: false\nprint Bool(false) // expect: false\n"
  },
  {
    "path": "test/bool/equality.morpho",
    "content": "// Equality tests on boolean values\n\nprint true == true     // expect: true\nprint true == false    // expect: false\nprint false == true    // expect: false\nprint false == false   // expect: true\n\n// Not equal to other types.\nprint true == 1        // expect: false\nprint false == 0       // expect: false\nprint true == \"true\"   // expect: false\nprint false == \"false\" // expect: false\nprint false == \"\"      // expect: false\n\nprint true != true     // expect: false\nprint true != false    // expect: true\nprint false != true    // expect: true\nprint false != false   // expect: false\n\n// Not equal to other types.\nprint true != 1         // expect: true\nprint false != 0        // expect: true\nprint true != \"true\"    // expect: true\nprint false != \"false\"  // expect: true\nprint false != \"\"       // expect: true\n"
  },
  {
    "path": "test/bool/not.morpho",
    "content": "// Test not operator on boolean values\n\nprint !true;    // expect: false\nprint !false;   // expect: true\nprint !!true;   // expect: true\n"
  },
  {
    "path": "test/break/break_in_if_outside_loop.morpho",
    "content": "// Break outside of loop but in if statement\n\nif (true) {\n  break\n}\n// expect error 'BrkOtsdLp'\n"
  },
  {
    "path": "test/break/break_outside_loop.morpho",
    "content": "// Break outside of loop \n\nbreak\n// expect error 'BrkOtsdLp'\n"
  },
  {
    "path": "test/break/continue_in_if_outside_loop.morpho",
    "content": "// Continue outside of loop but in if statement\n\nif (true) {\n  continue\n}\n// expect error 'CntOtsdLp'\n"
  },
  {
    "path": "test/break/continue_outside_loop.morpho",
    "content": "// Continue outside of loop\n\ncontinue\n// expect error 'CntOtsdLp'\n"
  },
  {
    "path": "test/break/in_for.morpho",
    "content": "// For loops with break and continue\n\n// Immediately stop a loop\nfor (var i=0;i<6;i+=1) break\n\n// Do nothing with a continue\nfor (var i=0;i<6;i+=1) continue\n\n// Conditionally terminate a loop early\nfor (var i=1;i<6;i+=1) {\n  if (i>3) break\n  print i\n}\n// expect: 1\n// expect: 2\n// expect: 3\n\n// Conditionally skip early elements\nfor (var i=0;i<6;i+=1) {\n  if (i<3) continue\n  print i\n}\n// expect: 3\n// expect: 4\n// expect: 5\n"
  },
  {
    "path": "test/break/in_forin.morpho",
    "content": "// For in loops with break and continue\n\n// Immediately stop a loop\nfor (i in 1..5) break\n\n// Conditionally terminate a loop early\nfor (i in 1..5) {\n  if (i>3) break\n  print i\n}\n// expect: 1\n// expect: 2\n// expect: 3\n\n// Conditionally skip early elements\nfor (i in 1..5) {\n  if (i<3) continue\n  print i\n}\n// expect: 3\n// expect: 4\n// expect: 5\n"
  },
  {
    "path": "test/break/in_while.morpho",
    "content": "// While loops with break and continue\n\n// Immediately stop a loop\nwhile (true) break\n\n// Use break and continue\nvar k=0\nwhile (k<10) {\n  if (k==2) { k+=1; continue }\n  if (k>4) break\n  print k\n  k+=1\n  continue\n  print \"You'll never see this!\"\n}\n// expect: 0\n// expect: 1\n// expect: 3\n// expect: 4\n\n// A loop that shouldn't execute \nwhile (false) continue\n"
  },
  {
    "path": "test/builtin/apply.morpho",
    "content": "// Check whether apply works correctly\n\nfn f(x,y,z) { \n  print x+y+z\n} \n\napply(f, (1,2,3))\n// expect: 6\n\napply(f, [1,2,3])\n// expect: 6\n\napply(f, 1,2,3)\n// expect: 6\n\napply([1,2,3], f)\n// expect error 'ApplyNtCllble'"
  },
  {
    "path": "test/builtin/bounds.morpho",
    "content": "// Calculate bounds of enumerable objects\n\nvar a = [ 4, 5, 3, 1, 2 ]\n\nprint bounds(a)\n// expect: [ 1, 5 ]\n\nprint min(a)\n// expect: 1\n\nprint max(a)\n// expect: 5\n\nvar b = Matrix([[-1,0],[0, 1]])\n\nprint min(b)\n// expect: -1\n\nprint bounds()\n// expect Error 'MnMxArgs'\n"
  },
  {
    "path": "test/builtin/boundsvargs.morpho",
    "content": "// Calculate bounds of enumerable objects\n\nvar a = [ 4, 5, 3, 1, 2 ]\nvar b = [ 2, -1, 0.3 ]\n\nprint bounds(a, 7, b)\n// expect: [ -1, 7 ]\n\nprint bounds()\n// expect error 'MnMxArgs'\n"
  },
  {
    "path": "test/builtin/iscallable.morpho",
    "content": "// Check whether iscallable works correctly\n\nclass Foo {\n  bar() { }\n}\n\nvar a[2,2]\n\nvar f = Foo()\n\nfn g(x) { return x }\n\nfn h(t) {\n  fn q() { return t }\n  return q\n}\n\nvar s = h(1)\n\nvar vals = [  f, // expect: false\n              g, // expect: true\n              f.bar, // expect: true\n              9, // expect: false\n              9.0, // expect: false\n              \"Hi\", // expect: false\n              a, // expect: false\n              sin, // expect: true\n              s, // expect: true\n              h(2) // expect: true\n              ]\n\nfor (x in vals) {\n  print iscallable(x)\n}\n"
  },
  {
    "path": "test/builtin/maxargs.morpho",
    "content": "print max([])\n// expect: nil\nprint max(-120)\n// expect: -120\n"
  },
  {
    "path": "test/builtin/minargs.morpho",
    "content": "print min([])\n// expect: nil\nprint min(-120)\n// expect: -120\n"
  },
  {
    "path": "test/builtin/minnumbers.morpho",
    "content": "var list = [1,0.1,0.2,0.5,0.3]\n\nprint min(list) // expect: 0.1\nprint max(list) // expect: 1\n\nlist.sort()\nprint list\n// expect: [ 0.1, 0.2, 0.3, 0.5, 1 ]\n\nlist.sort(fn (a, b) b-a)\nprint list\n// expect: [ 1, 0.5, 0.3, 0.2, 0.1 ]\n"
  },
  {
    "path": "test/builtin/minvargs.morpho",
    "content": "// Use min/max on varargs\n\nprint min(0.1, 0.5, 1, 0.001, 2)\n// expect: 0.001\n\nprint min(0.1, [0.5, 1], [2, 0.003])\n// expect: 0.003\n\nprint max(0.1, 0.5, 1, 0.001, 2)\n// expect: 2\n\nprint max(0.1, [0.5, 3], [2, 0.003])\n// expect: 3\n"
  },
  {
    "path": "test/builtin/mod.morpho",
    "content": "// Mod function\n\nprint mod(5, 3)\n// expect: 2\n\nprint mod(5.3, 3)\n// expect: 2.3\n\nprint mod(4, 2.5)\n// expect: 1.5\n\nprint mod(3.4, 1.1)\n// expect: 0.1\n\nprint mod(1,2,3)\n// expect Error 'InvldArgs'\n"
  },
  {
    "path": "test/builtin/typecheck.morpho",
    "content": "// Check that type checking functions work correctly\n\nclass Foo {\n  init(x) { self.bar = x }\n}\n\nvar a[2,2]\n\nvar vals = [  nil, 1, 0.2, true, false, Object(), \"Hi\",\n              Foo, 1..2, { \"a\": 1, \"b\": 2}, [], a,\n              Matrix(2,2), Sparse([[1,1,1]]) ]\nvar check = [ isnil, isint, isfloat, isbool, isbool, isobject, isstring,\n              isclass, isrange, isdictionary, islist, isarray,\n              ismatrix, issparse ]\n\nvar tst=Matrix(check.count(),vals.count())\n\nfor (i in 0..vals.count()-1) {\n  for (j in 0..vals.count()-1) {\n    if (apply(check[i], [].append(vals[j]))) tst[i,j]=1\n  }\n}\n\nprint tst\n// expect: [ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 1 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 1 1 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 1 1 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 1 1 1 1 1 1 1 1 1 ]\n// expect: [ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ]\n"
  },
  {
    "path": "test/call/bool.morpho",
    "content": "// Check that calling a bool generates an err.\n\ntrue() // expect error: 'Uncallable'\n"
  },
  {
    "path": "test/call/call.morpho",
    "content": "// Test calling functions\n\n// Built in function\nprint exp(0)\n// expect: 1\n\n// A function\nfn sqr(x) {\n  return x*x\n}\nprint sqr(2)\n// expect: 4\n"
  },
  {
    "path": "test/call/nil.morpho",
    "content": "// Check that calling nil generates an err\n\nnil() // expect error: 'Uncallable'\n"
  },
  {
    "path": "test/call/num.morpho",
    "content": "// Check that calling a number generates an err\n\n123() // expect error: 'Uncallable'\n"
  },
  {
    "path": "test/call/object.morpho",
    "content": "// Check that calling an object generates an err\n\nclass Foo {}\n\nvar foo = Foo()\nfoo() // expect error: 'Uncallable'\n"
  },
  {
    "path": "test/call/string.morpho",
    "content": "// Check that calling a string generates an err.\n\n\"str\"() // expect error: 'Uncallable'\n"
  },
  {
    "path": "test/class/apply_class.morpho",
    "content": "// Ensure a property can store a class and be callable\n\nclass A {\n  init(a) {\n    self.a = a\n  }\n}\n\nvar a = apply(A, \"foo\")\n\nprint a\n// expect: <A>\n\nprint a.a\n// expect: foo\n"
  },
  {
    "path": "test/class/call_class_in_property.morpho",
    "content": "// Ensure a property can store a class and be callable\n\nclass A {\n  init(a) {\n    self.a = a\n  }\n\n  make() {\n    return self.a(\"foo\") \n  }\n}\n\nvar a = A(A) \n\nprint a.make() \n// expect: <A>\n"
  },
  {
    "path": "test/class/call_on_class.morpho",
    "content": "// Call on a class \n\nclass Foo {\n  prnt() {\n    print \"Hello\"\n  }\n}\n\nFoo.prnt() // expect: Hello\n\nSystem.prnt(\"Foo\\n\") // expect: Foo"
  },
  {
    "path": "test/class/empty.morpho",
    "content": "class Foo {}\n\nprint Foo // expect: @Foo\n"
  },
  {
    "path": "test/class/forward_ref_in_method.morpho",
    "content": "// Forward reference in method \n\nclass A {\n  foo() {\n    print boo() \n  }\n}\n\nfn boo() {\n  return \"woo!\"\n}\n\nvar a = A() \na.foo() \n// expect error 'UnrslvdFrwdRf'\n"
  },
  {
    "path": "test/class/inherit_self.morpho",
    "content": "// Ensure that a class cannot inherit itself\n\nclass Foo < Foo {}\n// expect error 'ClssCrcRf'\n"
  },
  {
    "path": "test/class/inherited_method.morpho",
    "content": "// Check inheritance\n\nclass Foo {\n  inFoo() {\n    print \"in foo\"\n  }\n}\n\nclass Bar < Foo {\n  inBar() {\n    print \"in bar\"\n  }\n}\n\nclass Baz < Bar {\n  inBaz() {\n    print \"in baz\"\n  }\n}\n\nvar baz = Baz()\nbaz.inFoo() // expect: in foo\nbaz.inBar() // expect: in bar\nbaz.inBaz() // expect: in baz\n"
  },
  {
    "path": "test/class/is.morpho",
    "content": "// Test 'is'\nclass A {\n  a() {\n    return \"Foo\"\n  }\n}\n\nclass B is A {\n\n}\n\nvar b = B()\n\nprint b.a() // expect: Foo\n"
  },
  {
    "path": "test/class/keyword_method.morpho",
    "content": "// Ensure a class can use keywords for method labels\n\nclass A {\n  init(a) {\n    self.a = a\n  }\n\n  print() {\n    print self.a\n  }\n}\n\nvar a = A(\"foo\")\n\na.print() \n// expect: foo\n"
  },
  {
    "path": "test/class/keyword_property.morpho",
    "content": "// Ensure a class can use keywords for property labels\n\nclass A {\n  init(a) {\n    self.while = a\n  }\n}\n\nvar a = A(\"foo\")\n\nprint a\n// expect: <A>\n\nprint a.while \n// expect: foo\n"
  },
  {
    "path": "test/class/linearization.morpho",
    "content": "// Class linearization\n\nclass O { }\n\nclass F is O { }\n\nclass E is O { }\n\nclass D is O { }\n\nclass C is D with F { }\n\nclass B is D with E { }\n\nclass A is B with C { }\n\nprint O.linearization() // expect: [ @O ]\nprint F.linearization() // expect: [ @F, @O ]\nprint E.linearization() // expect: [ @E, @O ]\nprint D.linearization() // expect: [ @D, @O ]\nprint C.linearization() // expect: [ @C, @D, @F, @O ]\nprint B.linearization() // expect: [ @B, @D, @E, @O ]\nprint A.linearization() // expect: [ @A, @B, @C, @D, @E, @F, @O ]\n"
  },
  {
    "path": "test/class/local_fn_supersedes_method_call.morpho",
    "content": "// A local function supercedes a method call\n\nclass A {\n  foo() {\n    fn goo(x) {\n      print \"Boo\"\n    }\n\n    goo(\"Hoo\")\n  }\n\n  goo(x) {\n    print x\n  }\n}\n\nvar a = A() \n\na.foo() \n// expect: Boo\n\na.goo(\"Woo\") \n// expect: Woo\n"
  },
  {
    "path": "test/class/local_inherit_other.morpho",
    "content": "// Check that we can return a local class\n\nclass A {}\n\nfn f() {\n  class B < A {}\n  return B\n}\n\nprint f() // expect: @B\n"
  },
  {
    "path": "test/class/local_inherit_self.morpho",
    "content": "// Ensure classes cannot inherit from themselves\n\n{\n  class Foo < Foo {} // expect error 'ClssCrcRf'\n}\n"
  },
  {
    "path": "test/class/local_reference_self.morpho",
    "content": "// Check a method can return the class. \n\n{\n  class Foo {\n    returnSelf() {\n      return Foo\n    }\n  }\n\n  print Foo().returnSelf() // expect: @Foo\n}\n"
  },
  {
    "path": "test/class/method_call_supersedes_global_fn.morpho",
    "content": "// A method reference supersedes a global function definition\n\nfn goo(x) {\n  print \"Boo\"\n}\n\nclass A {\n  foo() {\n    goo(\"Hoo\")\n  }\n\n  goo(x) {\n    print x\n  }\n}\n\nvar a = A() \n\na.foo() \n// expect: Hoo\n\na.goo(\"Woo\") \n// expect: Woo\n"
  },
  {
    "path": "test/class/mixins.morpho",
    "content": "// Test mixins \n\nclass A {\n  a() { return \"Foo\" }\n  b() { return \"Ooo Noo!\" }\n}\n\nclass B {\n  b() { return \"Boo\" }\n  c() { return \"Coo\" }\n}\n\nclass Q {\n  b() { return \"Woo\" }\n  q() { return \"Qoo\" }\n  z() { return self.a() }\n}\n\nclass C is A with B, Q {\n  \n}\n\nvar x = C()\n\nprint x.a() // expect: Foo\nprint x.b() // expect: Woo\nprint x.c() // expect: Coo\nprint x.q() // expect: Qoo\nprint x.z() // expect: Foo\n\n\n\n\n"
  },
  {
    "path": "test/class/nested_class.morpho",
    "content": "class Foo {\n    foo() {\n        class Boo {\n\n        }\n\n        return Boo() \n    }\n}\n\nprint Foo() \n// expect error 'NstdClss'\n"
  },
  {
    "path": "test/class/no_self_for_method_call.morpho",
    "content": "// Avoid needing self in a method call\n\nclass A {\n  foo() {\n    self.goo(\"Foo\")\n    goo(\"Hoo\")\n  }\n\n  goo(x) {\n    print x\n  }\n}\n\nvar a = A() \n\na.foo() \n// expect: Foo\n// expect: Hoo\n\na.goo(\"Woo\") \n// expect: Woo\n"
  },
  {
    "path": "test/class/prefer_local.morpho",
    "content": "// Ensure that variables shadow globals in method definitions\n\nvar m = \"Still here!\"\n\nclass Foo {\n  bar(f) {\n    var m = Matrix(2,2)\n    for (i in 0...2) {\n      for (j in 0...2) {\n        m[i,j]=i*j\n      }\n    }\n    print m\n  }\n}\n\nvar a = Foo()\n\na.bar(1)\n// expect: [ 0 0 ]\n// expect: [ 0 1 ]\n\nprint m\n// expect: Still here!\n"
  },
  {
    "path": "test/class/reference_self.morpho",
    "content": "class Foo {\n  returnSelf() {\n    return Foo\n  }\n}\n\nprint Foo().returnSelf() // expect: @Foo\n"
  },
  {
    "path": "test/class/syntax_error_in_constructor.morpho",
    "content": "// Syntax err. in call to constructor\n\nclass A {\n    init(m, a=1, b=1, c=1) {\n    }\n}\n\nclass B {} \n\nvar b = B() \n\nvar a = A(b., a=1.0, b=1.0, c=1.0)\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/class/unlinearizable.morpho",
    "content": "// Unlinearizable classes\n\nclass A { }\n\nclass B { }\n\nclass C is A with B { }\n\nclass D is B with A { }\n\nclass E is C with D { }\n\n// expect error 'ClssLnrz'\n"
  },
  {
    "path": "test/closure/assign_to_closure.morpho",
    "content": "// Check that assignment works within closures\n\nvar f\nvar g\n\n{\n  var local = \"local\" // A local variable\n  fn f_() { // Set the value of the local variable\n    print local\n    local = \"after f\"\n    print local\n  }\n  f = f_\n\n  fn g_() {\n    print local\n    local = \"after g\"\n    print local\n  }\n  g = g_\n}\n\nf()\n// expect: local\n// expect: after f\n\ng()\n// expect: after f\n// expect: after g\n"
  },
  {
    "path": "test/closure/assign_to_shadowed_later.morpho",
    "content": "var a = \"global\"\n\n{\n  fn assign() {\n    a = \"assigned\"\n  }\n\n  var a = \"inner\"\n  assign()\n  print a // expect: inner\n}\n\nprint a // expect: assigned\n"
  },
  {
    "path": "test/closure/close_over_function_parameter.morpho",
    "content": "var f\n\nfn foo(param) {\n  fn f_() {\n    print param\n  }\n  f = f_\n}\nfoo(\"param\")\n\nf() // expect: param\n"
  },
  {
    "path": "test/closure/close_over_later_variable.morpho",
    "content": "// This is a regression test. There was a bug where if an upvalue for an\n// earlier local (here \"a\") was captured *after* a later one (\"b\"), then it\n// would crash because it walked to the end of the upvalue list (correct), but\n// then didn't handle not finding the variable.\n\nfn f() {\n  var a = \"a\"\n  var b = \"b\"\n  fn g() {\n    print b // expect: b\n    print a // expect: a\n  }\n  g()\n}\nf()\n"
  },
  {
    "path": "test/closure/closed_closure_in_function.morpho",
    "content": "var f\n\n{\n  var local = \"local\"\n  fn f_() {\n    print local\n  }\n  f = f_\n}\n\nf() // expect: local\n"
  },
  {
    "path": "test/closure/closures_in_loop.morpho",
    "content": "\nvar mesh = Mesh()\n\nwhile (true) {\n\tvar L = 1\n\n\tvar a=Selection(mesh, fn (x,y,z) x+L<0.01)\n\n\tvar b=LineIntegral(fn (x) x)\n\tbreak\n}\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/closure/nested_closure.morpho",
    "content": "var f\n\nfn f1() {\n  var a = \"a\"\n  fn f2() {\n    var b = \"b\"\n    fn f3() {\n      var c = \"c\"\n      fn f4() {\n        print a\n        print b\n        print c\n      }\n      f = f4\n    }\n    f3()\n  }\n  f2()\n}\nf1()\n\nf()\n// expect: a\n// expect: b\n// expect: c\n"
  },
  {
    "path": "test/closure/open_closure_in_function.morpho",
    "content": "{\n  var local = \"local\"\n  fn f() {\n    print local // expect: local\n  }\n  f()\n}\n"
  },
  {
    "path": "test/closure/prioritize_upvalues_over_globals.morpho",
    "content": "// Checks that upvalues are searched before globals\n\n// Create a global\nvar a = \"Hello\"\n\n// This is a reference to an upvalue\nfn h(a) {\n  fn q() { return a }\n  return q\n}\n\n// Hence the returned value is a closure\nprint h(1)\n// expect: <<fn q>>\n\nprint h(1)()\n// expect: 1\n\n// This is a reference to a global\nfn g() {\n  fn q() { return a }\n  return q\n}\n\nprint g()\n// expect: <fn q>\n\nprint g()()\n// expect: Hello\n"
  },
  {
    "path": "test/closure/reference_closure_multiple_times.morpho",
    "content": "var f\n\n{\n  var a = \"a\"\n  fn f_() {\n    print a\n    print a\n  }\n  f = f_\n}\n\nf()\n// expect: a\n// expect: a\n"
  },
  {
    "path": "test/closure/reuse_closure_slot.morpho",
    "content": "{\n  var f\n\n  {\n    var a = \"a\"\n    fn f_() { print a }\n    f = f_\n  }\n\n  {\n    // Since a is out of scope, the local slot will be reused by b. Make sure\n    // that f still closes over a.\n    var b = \"b\"\n    f() // expect: a\n  }\n}\n"
  },
  {
    "path": "test/closure/shadow_closure_with_local.morpho",
    "content": "{\n  var foo = \"closure\"\n  fn f() {\n    {\n      print foo // expect: closure\n      var foo = \"shadow\"\n      print foo // expect: shadow\n    }\n    print foo // expect: closure\n  }\n  f()\n}\n"
  },
  {
    "path": "test/closure/unused_closure.morpho",
    "content": "// Ensure closures that are never created don't cause problems\n\n{\n  var a = \"a\"\n  if (false) {\n    fn foo() { a }\n  }\n}\n\n// If we get here, we didn't segfault when a went out of scope.\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/closure/unused_later_closure.morpho",
    "content": "// This is a regression test. When closing upvalues for discarded locals, it\n// wouldn't make sure it discarded the upvalue for the correct stack slot.\n//\n// Here we create two locals that can be closed over, but only the first one\n// actually is. When \"b\" goes out of scope, we need to make sure we don't\n// prematurely close \"a\".\nvar closure\n\n{\n  var a = \"a\"\n\n  {\n    var b = \"b\"\n    fn returnA() {\n      return a\n    }\n\n    closure = returnA\n\n    if (false) {\n      fn returnB() {\n        return b\n      }\n    }\n  }\n\n  print closure() // expect: a\n}\n"
  },
  {
    "path": "test/closure/veneer.morpho",
    "content": "// Test veneer class \n\nfn func(x) {\n  fn g() { return x }\n  return g\n}\n\nvar a = func(1)\n\nprint a() // expect: 1\n\nprint a // expect: <<fn g>>\n\nprint a.clss() // expect: @Closure\n\nprint a.clone() // expect error 'ObjCantClone'\n"
  },
  {
    "path": "test/comments/line_at_eof.morpho",
    "content": "// Comment at end of file\n\nprint \"ok\" // expect: ok\n// comment\n"
  },
  {
    "path": "test/comments/multline.morpho",
    "content": "/* This\n * is\n * a\n * multiline\n * comment\n * print \"Hello\"\n */\n"
  },
  {
    "path": "test/comments/nested.morpho",
    "content": "/* /* Nesting /* Comments /* Like */\n*/ this\n*/\n */\n"
  },
  {
    "path": "test/comments/only_line_comment.morpho",
    "content": "// comment"
  },
  {
    "path": "test/comments/only_line_comment_and_line.morpho",
    "content": "// comment\n"
  },
  {
    "path": "test/comments/unicode.morpho",
    "content": "// Unicode characters are allowed in comments.\n//\n// Latin 1 Supplement: £§¶ÜÞ\n// Latin Extended-A: ĐĦŋœ\n// Latin Extended-B: ƂƢƩǁ\n// Other stuff: ឃᢆ᯽₪ℜ↩⊗┺░\n// Emoji: ☃☺♣\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/comments/unterminated.morpho",
    "content": "// expect error 'UntrmComm'\n\n/* Unterminated comment\n\nprint \"Hello\"\n"
  },
  {
    "path": "test/complex/ComplexBuiltin.morpho",
    "content": "//Complex builtin test\nimport constants\n\nvar A = Complex(1,2)\nvar B = Complex(2.2,-3.1)\nvar C = Complex(3,4)\n\nvar compList = [A,B,C]\nvar tol = 1e-15\n\nprint A == 1+2im\n// expect: true\n\nprint 1+2im == 1+2*im\n// expect: true\n\nprint 1-2im == 1+2/im\n// expect: true\n\nprint B == 2.2-3.1im\n// expect: true\n\nfor (Z in compList){\n    print real(Z)\n}\n// expect: 1\n// expect: 2.2\n// expect: 3\n\n// imag\nfor (Z in compList){\n    print imag(Z)\n}\n// expect: 2\n// expect: -3.1\n// expect: 4\n\n\n\n// abs\nprint abs(C)\n// expect: 5\n\n// exp\nprint (imag(exp(Complex(2,Pi/2)))==exp(2))\n// expect: true\nprint real(exp(Complex(4,Pi)))==-exp(4)\n// expect: true\n\n// log\nprint log((E*im))==(1+Pi/2*im)\n// expect: true\n\nprint abs(log(abs(A))+angle(A)*im - log(A))<1e-15\n// expect: true\n\n\n// log10\nprint abs(log10(A)-(log(abs(A))/log(10)+angle(A)*im/log(10)))<tol\n// expect: true\n\n\n// sqrt\nprint abs(sqrt(im)-(1+im)/sqrt(2))<tol\n// expect: true\n\n// floor\nprint floor(1.2+3.2im)==(1+3im)\n// expect: true\nprint floor(-1.2-3.2im)==(-2-4im)\n// expect: true\n\n\n// ceil\nprint ceil(1.2+3.2im)==(2+4im)\n// expect: true\nprint ceil(-1.2-3.2im)==(-1-3im)\n// expect: true\n\n\n// isfinite\nprint isfinite(1+im)\n// expect: true\nprint isfinite(1/0+im)\n// expect: false\nprint isfinite(1+1/0*im)\n// expect: false\nprint isfinite(1/0+1/0*im)\n// expect: false\n\n\n// isinf\nprint isinf(1+im)\n// expect: false\nprint isinf(1/0+im)\n// expect: true\nprint isinf(1+1/0*im)\n// expect: true\nprint isinf(1/0+1/0*im)\n// expect: true\n\n// isnan\nprint isnan(1+im)\n// expect: false\nprint isnan(0/0+im)\n// expect: true\nprint isnan(1+0/0*im)\n// expect: true\nprint isnan(0/0+0/0*im)\n// expect: true\n\n// angle\nfor (Z in compList){\n    print (angle(Z)-arctan(real(Z),imag(Z)))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// conj\nfor (Z in compList){\n    print conj(Z)==real(Z)-im*imag(Z)\n// expect: true\n// expect: true\n// expect: true\n}\n\n// comparison\nprint (1+0im == 1)\n// expect: true\n\n//sqrt of negative number\nprint sqrt(-1)\n// expect: 0 + 1im\n"
  },
  {
    "path": "test/complex/ComplexTrig.morpho",
    "content": "import constants\n\nvar A = Complex(1,2)\nvar B = Complex(2.2,-3.1)\nvar C = Complex(3,4)\n\nvar compList = [A,B,C]\nvar tol = 1e-14\n\n// sin\nfor (Z in compList){\n    print abs(sin(Z)-(sin(real(Z))*cosh(imag(Z))+im*cos(real(Z))*sinh(imag(Z))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// cos\nfor (Z in compList){\n    print abs(cos(Z)-cos(real(Z))*cosh(imag(Z))+im*sin(real(Z))*sinh(imag(Z)))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// tan\nfor (Z in compList){\n    print abs(tan(Z)-(tan(real(Z))+im*tanh(imag(Z)))/(1-im*tan(real(Z))*tanh(imag(Z))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// asin\nfor (Z in compList){\n    print abs(asin(Z)-im * log(sqrt(1-Z^2)-im*Z))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n\n// acos\nfor (Z in compList){\n    print abs(acos(Z)-(-im * log(im*sqrt(1-Z^2)+Z)))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n\n// atan\nfor (Z in compList){\n    print abs(arctan(Z)-(-im/2*log((1+im*Z)/(1-im*Z))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\nfor (Z in compList){\n    print abs(arctan(2,Z)-(-im/2*log((1+im*Z/2)/(1-im*Z/2))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\nfor (Z in compList){\n    print abs(arctan(Z,2)-(-im/2*log((1+im*2/Z)/(1-im*2/Z))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\nvar D = 3.14+2.7im\nfor (Z in compList){\n    print abs(arctan(D,Z)-(-im/2*log((1+im*Z/D)/(1-im*Z/D))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// sinh\nfor (Z in compList){\n    print abs(sinh(Z)-(sinh(real(Z))*cos(imag(Z))+im*cosh(real(Z))*sin(imag(Z))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// cosh\nfor (Z in compList){\n    print abs(cosh(Z)-(cosh(real(Z))*cos(imag(Z))+im*sinh(real(Z))*sin(imag(Z))))<tol\n// expect: true\n// expect: true\n// expect: true\n}\n\n// tanh\nfor (Z in compList){\n    var expect = (tanh(real(Z))+im*tan(imag(Z)))/(1+im*tanh(real(Z))*tan(imag(Z)))\n    print abs(tanh(Z)-expect)<tol\n// expect: true\n// expect: true\n// expect: true\n}"
  },
  {
    "path": "test/complex/arithmetic.morpho",
    "content": "\nvar Z = 1 + 2im \nvar C = 3.2 - 1.1im\n\n// conj\nprint Z.conj()\n// expect: 1 - 2im\nprint C.conj()\n// expect: 3.2 + 1.1im\n\n// addition\nprint Z+C\n// expect: 4.2 + 0.9im\nprint 1 + Z\n// expect: 2 + 2im\nprint C + 1.2 \n// expect: 4.4 - 1.1im\nvar A = 1;\nA += Complex(2,3)\nprint A\n// expect: 3 + 3im\n\n\n// subtraction\nprint Z - C\n// expect: -2.2 + 3.1im\nprint 1 - Z\n// expect: 0 - 2im\nprint C - 1.2 \n// expect: 2 - 1.1im\nvar A = 1;\nA -= Complex(2,3)\nprint A\n// expect: -1 - 3im\n\n// multiplication\nprint Z * C\n// expect: 5.4 + 5.3im\n\nprint Z * -3\n// expect: -3 - 6im\n\nprint 0.2 * C\n// expect: 0.64 - 0.22im\nvar A = 2;\nA *= Complex(2,3)\nprint A\n// expect: 4 + 6im\n\n// division\nprint Complex(1,1)/Complex(2,2)\n// expect: 0.5 + 0im\n\nprint Z/C\n// expect: 0.0873362 + 0.655022im\nprint Z/3\n// expect: 0.333333 + 0.666667im\nprint 2/C\n// expect: 0.558952 + 0.19214im\nprint 1/Complex(3,4)\n// expect: 0.12 - 0.16im\nvar A = 2;\nA /= Complex(3,4)\nprint A\n// expect: 0.24 - 0.32im\n\n"
  },
  {
    "path": "test/complex/clone.morpho",
    "content": "\nvar Z = 1 + 2im \nvar C = 3.2 - 1.1im\n\n// clone\nvar X = Z\nprint X\n// expect: 1 + 2im\n"
  },
  {
    "path": "test/complex/complex_overflow_literal.morpho",
    "content": "// Check for overflow in complex literals\n\nvar err = 0.11e55555im\n// expect error 'ValRng'\n"
  },
  {
    "path": "test/complex/constructor.morpho",
    "content": "// Complex number testing\nimport constants\n\n// constructor\nvar Z = Complex(1,2);\nprint Z\n// expect: 1 + 2im\n\nvar C = Complex(3.2,-1.1);\nprint C \n// expect: 3.2 - 1.1im\nprint Z.real()\n// expect: 1\nprint Z.imag()\n// expect: 2\nprint C.real()\n// expect: 3.2\nprint C.imag()\n// expect: -1.1\n\n"
  },
  {
    "path": "test/complex/constructor_error.morpho",
    "content": "// constructor error\ntry {\n    var X = Complex(1)    \n} catch {\n    \"CmplxCns\" : print \"ok\"\n// expect: ok\n}    \n\ntry {\n    var C = Complex(1,\"text\")    \n} catch {\n    \"CmplxCns\" : print \"ok\"\n// expect: ok\n}    "
  },
  {
    "path": "test/complex/inherited.morpho",
    "content": "// Test Complex methods inherited from Object \n\nvar err = 1 + 1im\n\nprint err.respondsto(\"respondsto\") // expect: true\n\nprint islist(err.respondsto()) // expect: true\n\nprint err.clss() // expect: @Complex\n\nprint err.superclass() // expect: @Object\n\nprint islist(err.invoke(\"respondsto\")) // expect: true\n\nprint err.has(\"a\") // expect: false\n\n"
  },
  {
    "path": "test/complex/methods.morpho",
    "content": "// Complex number testing\nimport constants\n\nvar Z = 1 + 2im \nvar C = 3.2 - 1.1im\n\nprint Z.real()\n// expect: 1\n\nprint C.real()\n// expect: 3.2\n\nprint Z.imag()\n// expect: 2\n\nprint C.imag()\n// expect: -1.1\n\nprint abs(Z.angle()-arctan(Z.real(),Z.imag()))<1e-15\n// expect: true\n\nprint abs(C.angle()-arctan(C.real(),C.imag()))<1e-15\n// expect: true\n\nvar a1 = im\nprint abs(a1.angle()-Pi/2)<1e-15\n// expect: true\n\na1 = a1+(1-im)\nprint abs(a1.angle())<1e-15\n// expect: true\n\na1 = -a1\nprint abs(abs(a1.angle())-Pi)<1e-15\n// expect: true\n\na1 = -im\nprint abs(a1.angle()+Pi/2)<1e-15\n// expect: true\n\na1 = 1+im\nprint abs(a1.angle()-Pi/4)<1e-15\n// expect: true\n\na1 = 3+4im\nprint a1.abs()\n// expect: 5"
  },
  {
    "path": "test/complex/powers.morpho",
    "content": "\nimport constants \n\nvar Z = 1 + 2im \nvar C = 3.2 - 1.1im\n\n// powers\nprint Z^2\n// expect: -3 + 4im\n\nprint C^2.3\n// expect: 11.9464 - 11.3891im\n\nprint Complex(3,4)^-1\n// expect: 0.12 - 0.16im\n\nprint C^Z\n// expect: -3.35141 + 5.6408im\n\nprint 2^Complex(1,1)\n// expect: 1.53848 + 1.27792im\n\nprint (E^Complex(0,Pi) + 1).abs() < 1e-15\n// expect: true\n"
  },
  {
    "path": "test/constructor/arguments.morpho",
    "content": "class Foo {\n  init(a, b) {\n    print \"init\" // expect: init\n    self.a = a\n    self.b = b\n  }\n}\n\nvar foo = Foo(1, 2)\nprint foo.a // expect: 1\nprint foo.b // expect: 2\n"
  },
  {
    "path": "test/constructor/call_init_early_return.morpho",
    "content": "class Foo {\n  init() {\n    print \"init\"\n    return\n    print \"nope\"\n  }\n}\n\nvar foo = Foo() // expect: init\nprint foo.init() // expect: init\n// expect: <Foo>\n"
  },
  {
    "path": "test/constructor/call_init_explicitly.morpho",
    "content": "class Foo {\n  init(arg) {\n    print \"Foo.init(\" + arg + \")\"\n    self.field = \"init\"\n  }\n}\n\nvar foo = Foo(\"one\") // expect: Foo.init(one)\nfoo.field = \"field\"\n\nvar foo2 = foo.init(\"two\") // expect: Foo.init(two)\nprint foo2 // expect: <Foo>\n\n// Make sure init() doesn't create a fresh instance.\nprint foo.field // expect: init\n"
  },
  {
    "path": "test/constructor/default.morpho",
    "content": "class Foo {}\n\nvar foo = Foo()\nprint foo // expect: <Foo>\n"
  },
  {
    "path": "test/constructor/default_arguments.morpho",
    "content": "class Foo {}\n\nvar foo = Foo(1, 2, 3) // expect error 'NoInit'\n"
  },
  {
    "path": "test/constructor/early_return.morpho",
    "content": "class Foo {\n  init() {\n    print \"init\"\n    return\n    print \"nope\"\n  }\n}\n\nvar foo = Foo() // expect: init\nprint foo // expect: <Foo>\n"
  },
  {
    "path": "test/constructor/extra_arguments.morpho",
    "content": "class Foo {\n  init(a, b) {\n    self.a = a\n    self.b = b\n  }\n}\n\nvar foo = Foo(1, 2, 3, 4) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/constructor/init_not_method.morpho",
    "content": "class Foo {\n  init(arg) {\n    print \"Foo.init(\" + arg + \")\"\n    self.field = \"init\"\n  }\n}\n\nfn init() {\n  print \"not an initializer\"\n}\n\ninit() // expect: not an initializer\n"
  },
  {
    "path": "test/constructor/missing_arguments.morpho",
    "content": "class Foo {\n  init(a, b) {}\n}\n\nvar foo = Foo(1) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/constructor/return_in_nested_function.morpho",
    "content": "class Foo {\n  init() {\n    fn init() {\n      return \"bar\"\n    }\n    print init() // expect: bar\n  }\n}\n\nprint Foo() // expect: <Foo>\n"
  },
  {
    "path": "test/constructor/return_value.morpho",
    "content": "class Foo {\n  init() {\n    return \"result\" // expect error 'InitRtn'\n  }\n}\n"
  },
  {
    "path": "test/dictionary/clear.morpho",
    "content": "// Test Dictionary clear method\n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nmass.clear()\n\nmass[\"Abbreviation\"] = \"MA\"\nprint mass // expect: { Abbreviation : MA }\n"
  },
  {
    "path": "test/dictionary/contains.morpho",
    "content": "// Test Dictionary contains method\n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nprint mass.contains(\"Capital\") // expect: true\n\nprint mass.contains(\"State\") // expect: false\n"
  },
  {
    "path": "test/dictionary/empty_literal.morpho",
    "content": "// Empty dictionary literal\n\nvar a = { }\nprint isdictionary(a)\n// expect: true"
  },
  {
    "path": "test/dictionary/inherited.morpho",
    "content": "// Test Dictionary methods inherited from Object \n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nprint mass.respondsto(\"contains\") // expect: true\n\nprint islist(mass.respondsto()) // expect: true\n\nprint mass.clss() // expect: @Dictionary\n\nprint mass.superclass() // expect: @Object\n\nprint islist(mass.invoke(\"respondsto\")) // expect: true\n\nprint mass.has(\"a\") // expect: false\n\nprint mass.count() // expect: 2"
  },
  {
    "path": "test/dictionary/key_not_found.morpho",
    "content": "// Test Dictionary standard methods\n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nprint mass[\"Capital\"] // expect: Boston\nprint mass[\"Area\"] // expect error 'DctKyNtFnd'\n"
  },
  {
    "path": "test/dictionary/literal_from_vars.morpho",
    "content": "// Dictionary literal in a loop constucted from variables (with operations!)\n// Checks register allocation and instruction count\n\nvar t = \"Type\"\n\nfor (i in 1..2) {\n  var p =  { t: \"Cruller\", \"Calories\": 400*i+50 }\n\n  print p[\"Type\"]\n  print p[\"Calories\"]\n}\n\n// expect: Cruller\n// expect: 450\n// expect: Cruller\n// expect: 850\n"
  },
  {
    "path": "test/dictionary/literal_in_function.morpho",
    "content": "// Dictionary literal in a function\n\nfn state(cap, pop) {\n  return { \"Capital\" : cap, \"Population\" : 6892503 }\n}\n\nvar mass=state(\"Boston\", 6892503)\n\nprint mass[\"Capital\"] // expect: Boston\nprint mass[\"Population\"] // expect: 6892503\n"
  },
  {
    "path": "test/dictionary/literal_in_loop.morpho",
    "content": "// Dictionary literal in a loop\n// (Checks the compiler returned the right instruction count)\n\nfor (i in 1..5) {\n  var mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n  print mass[\"Capital\"]\n}\n\n// expect: Boston\n// expect: Boston\n// expect: Boston\n// expect: Boston\n// expect: Boston\n"
  },
  {
    "path": "test/dictionary/methods.morpho",
    "content": "// Test Dictionary standard methods\n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nprint mass // expect: { Population : 6892503 , Capital : Boston }\n\nprint mass.count() // expect: 2\n\nfor (key in mass) print key\n// expect: Population\n// expect: Capital\n\nvar mass2 = mass.clone()\nprint mass2[\"Capital\"] // expect: Boston\n"
  },
  {
    "path": "test/dictionary/missing_comma.morpho",
    "content": "// Missing comma between key/value pairs \n\nvar a = { \"a\" : \"b\" \"c\" : \"d\" }\n// expect error 'MssngComma'"
  },
  {
    "path": "test/dictionary/missing_separator.morpho",
    "content": "// Missing separator between key/value pair\n\nvar a = { \"a\" \"b\" }\n// expect error 'DctSprtr'"
  },
  {
    "path": "test/dictionary/remove.morpho",
    "content": "// Test Dictionary remove method\n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nmass.remove(\"Capital\")\n\nprint mass // expect: { Population : 6892503 }\n"
  },
  {
    "path": "test/dictionary/set_operations.morpho",
    "content": "// Dictionary set operations\n\nvar a = { 1: 0, 2: 0, 3: 0 }\nvar b = { 2: 0, 3: 0, 4: 0 }\n\nvar k = a.union(b).keys()\nk.sort()\nprint k\n// expect: [ 1, 2, 3, 4 ]\n\nk=a.intersection(b).keys()\nk.sort()\nprint k\n// expect: [ 2, 3 ]\n\nk=a.difference(b).keys()\nk.sort()\nprint k\n// expect: [ 1 ]\n\n// Addition and subtraction operators map onto difference and union\nprint { \"foo\": 0, \"bar\": 0} - { \"bar\": 0}\n// expect: { foo : 0 }\n\nk=({ \"f\": 0, \"g\": 0} + { \"h\": 0, \"i\": 0}).keys()\nk.sort()\nprint k\n// expect: [ f, g, h, i ]\n"
  },
  {
    "path": "test/dictionary/syntax.morpho",
    "content": "// Dictionary literals\n\nvar mass = { \"Capital\" : \"Boston\", \"Population\" : 6892503 }\n\nprint mass // expect: { Population : 6892503 , Capital : Boston }\n\nprint mass[\"Capital\"] // expect: Boston\nprint mass[\"Population\"] // expect: 6892503\n\nvar shft = { 1: 2, 2: 3, 3: 4}\n\nprint shft[1] // expect: 2\nprint shft[2] // expect: 3\nprint shft[3] // expect: 4\n"
  },
  {
    "path": "test/dictionary/tombstone.morpho",
    "content": "// This test makes sure that dictionary insertion happens even if there\n// are only tombstones and no blank entries left.\n\nvar d = Dictionary()\n\nvar N = 16 // This is chosen because it's the minimum and defualt capacity of the Morpho dictionary.\n\nvar n = floor(0.75*N) // This is the maximum number of entries that can be added to the dictionary  without triggering a resize to 2*N\n\n// Add n items\nvar key \nfor (i in 1..n) {\n    key = \"${i}\"\n    d[key] = i\n}\n\n// Now, remove a single key, followed by addition of another key, for\n// all these keys. This triggers a situation at i=n-1 that all the blank\n// entries have been replaced with tombstones. The old\n// implementation of dictionary_find returns a NULL pointer for entry,\n// and _dictionary_insert in turn doesn't handle that scenario. Hence,\n// the last assignment of d[newkey] fails silently. The current\n// implementation should handle this scenario.\nvar newkey\nfor (i in 1..n) {\n    key = \"${i}\"\n    d.remove(key)\n    newkey = \"new_${i}\"\n    d[newkey] = i\n}\nprint d[newkey] // expect: 12\n"
  },
  {
    "path": "test/dictionary/unterminated.morpho",
    "content": "// Empty dictionary literal\n\nvar a = { \n// expect error 'DctTrmntr'"
  },
  {
    "path": "test/do/do_in_fn.morpho",
    "content": "// Do..while loop in a function\n\nfn f(N) {\n    var i=0\n    do {\n      print i\n      i+=1\n    } while (i<N)\n}\n\nf(5)\n// expect: 0\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n"
  },
  {
    "path": "test/do/do_with_break.morpho",
    "content": "// Do..while loop with break and continue\n\nvar i = 0\ndo {\n  if (i==5) break\n  i+=1\n  if (i==3) continue\n  print i\n} while (true)\n\n// expect: 1\n// expect: 2\n// expect: 4\n// expect: 5\n"
  },
  {
    "path": "test/do/missingcond.morpho",
    "content": "// Missing condition\n\nvar i=0\ndo { } while\n// expect error 'WhlMssngLftPrn'\n"
  },
  {
    "path": "test/do/syntax.morpho",
    "content": "// Do..while loop syntax\n\nvar i=0\ndo {\n  print i\n  i+=1\n} while (i<5)\n// expect: 0\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n"
  },
  {
    "path": "test/error/error_incorrectly_defined.morpho",
    "content": "// User generated errs\n\nvar err = Error(\"FooFoo\", 1)\n\n// expect: Error 'ErrorArgs'\n"
  },
  {
    "path": "test/error/inherited.morpho",
    "content": "// Test List methods inherited from Object \n\nvar err = Error(\"Err\", \"My error\")\n\nprint err.respondsto(\"respondsto\") // expect: true\n\nprint islist(err.respondsto()) // expect: true\n\nprint err.clss() // expect: @Error\n\nprint err.superclass() // expect: @Object\n\nprint islist(err.invoke(\"respondsto\")) // expect: true\n\nprint err.has(\"a\") // expect: false\n\nprint err.count() // expect: 2\n\nprint err[\"message\"] // expect: My error\n\nerr[\"message\"] = \"We're doomed!\"\n\nerr.throw() // expect error 'Err'"
  },
  {
    "path": "test/error/stacktrace.morpho",
    "content": "// Forces a runtime err. generating a stacktrace\n\nfn f() {\n\tvar a\n\n\tprint a-1\n}\n\nf()\n// expect: Error 'InvldOp': Operands must be numbers.\n"
  },
  {
    "path": "test/error/throw.morpho",
    "content": "// User generated errs\n\nvar err = Error(\"FooFoo\", \"Can't foo foos.\")\nerr.throw()\n\n// expect: Error 'FooFoo': Can't foo foos.\n"
  },
  {
    "path": "test/error/throw_on_class.morpho",
    "content": "// Calling throw on the class\n\nError.throw(\"FooFoo\", \"Can't foo foos.\")\n// expect: Error 'FooFoo': Can't foo foos.\n"
  },
  {
    "path": "test/error/throw_on_class_notag.morpho",
    "content": "// Calling throw on the class without a tag\n\nError.throw(\"Can't foo foos.\")\n// expect: Error 'Err': Can't foo foos.\n"
  },
  {
    "path": "test/field/assign.morpho",
    "content": "// Scalar field\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m)\nvar g = Field(m)\n\n// Create a convenient function for output\nfn write(f) { print \"[${f[0]}, ${f[1]}, ${f[2]}, ${f[3]}]\" }\n\n// Set elements\nfor (i in 0...4) f[i]=i\n\nwrite(f)\n// expect: [0, 1, 2, 3]\n\nwrite(g)\n// expect: [0, 0, 0, 0]\n\ng.assign(f)\nwrite(g)\n// expect: [0, 1, 2, 3]\n"
  },
  {
    "path": "test/field/assign_matrix.morpho",
    "content": "// Scalar field assign with matrix\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m)\n\n// Create a convenient function for output\nfn write(f) { print \"[${f[0]}, ${f[1]}, ${f[2]}, ${f[3]}]\" }\n\nvar a = f.linearize().clone() \n\n// Set elements\nfor (i in 0...4) a[i]=i\n\n// Show fresh field \nwrite(f)\n// expect: [0, 0, 0, 0]\n\n// Assign matrix to field \nf.assign(a)\n\n// Show assigned field \nwrite(f)\n// expect: [0, 1, 2, 3]\n\nvar b = Matrix([1,2])\nf.assign(b)\n// expect error 'FldIncmptbl'"
  },
  {
    "path": "test/field/badfnin.morpho",
    "content": "\n\nvar m = Mesh(\"square.mesh\")\n\nvar f = Field(m, fn(x,y,z) [1,2])\n// expect error: 'FldOpFn'\n"
  },
  {
    "path": "test/field/bounds.morpho",
    "content": "// Calculate bounds of enumerable objects\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m)\n\n// Set elements\nfor (i in 0...4) f[i]=i\n\nprint bounds(f)\n// expect: [ 0, 3 ]\n\nprint min(f)\n// expect: 0\n\nprint max(f)\n// expect: 3"
  },
  {
    "path": "test/field/discretizations/boundary_line_integrals.morpho",
    "content": "\n// Gradient of CG1 field\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\nvar m = mb.build()\nm.addgrade(1)\n\nfn integrand(x, q) {\n    return q \n}\n\nvar f = Field(m, fn(x,y) 3 + 4*x - 2*y, finiteelementspace=FiniteElementSpace(\"CG1\", grade=2))\n\nprint abs(LineIntegral(integrand, f, method={ }).total(m) - (7 + 4*sqrt(2))) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg1_area_in_2d_grad.morpho",
    "content": "// Gradient of CG1 field\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([2,0])\nmb.addvertex([0,2])\nmb.addvertex([2,2])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nvar l = FiniteElementSpace(\"CG1\", grade=2)\n\nfn integrand(x, q) {\n    var g = grad(q)\n    return g.norm()^2\n}\n\nvar f = Field(m, fn (x,y) 3 - 4*x + 2*y, finiteelementspace=l)\nprint abs(AreaIntegral(integrand, f, method={ }).total(m) - 80) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg1_area_in_2d_grad_old.morpho",
    "content": "// Gradient of CG1 field\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([2,0])\nmb.addvertex([0,2])\nmb.addvertex([2,2])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nfn integrand(x, q) {\n    var g = grad(q)\n\n    return g[0].norm()^2 + g[1].norm()^2\n}\n\nfn q0(x,y) {\n    return Matrix([3 - 4*x + 2*y, 3 + 4*x - 2*y])\n}\n\nvar f = Field(m, q0)\n\nprint abs(AreaIntegral(integrand, f, method={ }).total(m) - 160) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg1_area_in_2d_old.morpho",
    "content": "// Gradient of CG1 field\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([2,0])\nmb.addvertex([0,2])\nmb.addvertex([2,2])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nfn integrand(x, q) {\n    var g = grad(q)\n    return g.norm()^2\n}\n\nvar f = Field(m, fn (x,y) 3 - 4*x + 2*y)\nprint abs(AreaIntegral(integrand, f, method={ }).total(m) - 80) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg1_line_in_3d_grad.morpho",
    "content": "// Gradient of CG1 field on a line \n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0,0])\nmb.addvertex([2,2,1])\nmb.addedge([0,1])\nvar m = mb.build()\n\nfn integrand(x, q) {\n    var g = grad(q)\n    return g.norm()^2\n}\n\nvar f = Field(m, fn (x,y,z) x + y + 0.5*z, finiteelementspace=FiniteElementSpace(\"CG1\", grade=1))\n\nprint abs(LineIntegral(integrand, f, method={ }).total(m) - 6.75) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_area_in_2d.morpho",
    "content": "// CG2 FunctionSpace in two dimensions\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addvertex([1,1])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nvar l = FiniteElementSpace(\"CG2\", grade=2)\n\nvar f = Field(m, fn (x,y) Matrix([[x,y],[-y, x]]), finiteelementspace=l)\n\nprint f.shape() // expect: [ 1, 1, 0 ]\n\nprint abs(AreaIntegral(fn (x) sqrt(x[0]*x[1]), method={ \"rule\" : \"cubtri7\" }).total(m)-4/9) < 1e-6\n// expect: true\n\nprint abs(AreaIntegral(fn (x, q) q.trace()^2, f, method={ }).total(m)-4/3) < 1e-6\n// expect: true"
  },
  {
    "path": "test/field/discretizations/cg2_area_in_2d_grad.morpho",
    "content": "// Compute the gradient of a quantity\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([2,0])\nmb.addvertex([0,2])\nmb.addvertex([2,2])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nvar l = FiniteElementSpace(\"CG2\", grade=2)\n\nfn integrand(x, q) {\n    var g = grad(q)\n    return g.norm()^2\n}\n\nvar f = Field(m, fn (x,y) 3 - 2*x + 4*y, finiteelementspace=l)\nprint abs(AreaIntegral(integrand, f, method={ }).total(m) - 80) < 1e-8\n// expect: true\n\nvar g = Field(m, fn (x,y) x^2 + y^2, finiteelementspace=l)\nprint abs(AreaIntegral(integrand, g, method={ }).total(m) - 128/3) < 1e-8\n// expect: true\n\nvar h = Field(m, fn (x,y) 3 - 2*x + 4*y)\nprint abs(AreaIntegral(integrand, h, method={ }).total(m) - 80) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_area_in_2d_tensor_grad.morpho",
    "content": "// Compute the gradient of a tensor\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([2,0])\nmb.addvertex([0,2])\nmb.addvertex([2,2])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nfn f0(x,y) {\n    return Matrix([[x+y, x^2+y^2], [-(x^2+y^2),x-y]])\n}\n\nvar f = Field(m, f0, finiteelementspace=FiniteElementSpace(\"CG2\", grade=2))\n\nprint abs(AreaIntegral(fn (x, q) q[0,0], f, method={ }).total(m) - 8) < 1e-8\n// expect: true\n\nfn integrand(x, q) {\n    var g = grad(q)\n    var sum=0\n    for (dg in g) sum+=dg.norm()^2\n    return sum\n}\n\nprint abs(AreaIntegral(integrand, f, method={ }).total(m) - 304/3) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_area_in_3d_grad.morpho",
    "content": "// Compute the gradient of a quantity\n\nimport meshtools \n\nvar mb = MeshBuilder() \nmb.addvertex([0,0,1])\nmb.addvertex([2,0,1])\nmb.addvertex([0,2,1])\nmb.addvertex([2,2,1])\nmb.addface([0,1,2])\nmb.addface([1,3,2])\nvar m = mb.build()\nm.addgrade(1)\n\nvar l = FiniteElementSpace(\"CG2\", grade=2)\n\nfn integrand(x, q) {\n    var g = grad(q)\n    return g.norm()^2\n}\n\nvar f = Field(m, fn (x,y,z) 3 - 2*x + 4*y, finiteelementspace=l)\nprint abs(AreaIntegral(integrand, f, method={ }).total(m) - 80) < 1e-8\n// expect: true\n\nvar g = Field(m, fn (x,y,z) x^2 + y^2, finiteelementspace=l)\nprint abs(AreaIntegral(integrand, g, method={ }).total(m) - 128/3) < 1e-8\n// expect: true\n\nvar h = Field(m, fn (x,y,z) 3 - 2*x + 4*y)\nprint abs(AreaIntegral(integrand, h, method={ }).total(m) - 80) < 1e-8\n// expect: true\n\nvar p = Field(m, fn (x,y,z) 3 - 2*x + 4*y)\nprint abs(AreaIntegral(fn (x, q) grad(q)[0], p, method={}).total(m) - (-8)) < 1e-8\n// expect: true\n\nprint abs(AreaIntegral(fn (x, q) grad(q)[1], p, method={}).total(m) - 16) < 1e-8\n// expect: true"
  },
  {
    "path": "test/field/discretizations/cg2_line_in_1d.morpho",
    "content": "// CG2 FunctionSpace in one dimension\n\nimport meshtools \n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:0.5) \n\nvar l = FiniteElementSpace(\"CG2\", grade=1)\n\nvar f = Field(m, fn (x,y,z) x^2, finiteelementspace=l)\n\nprint l.layout(f)\n// expect: [ 1 0 ]\n// expect: [ 1 1 ]\n// expect: [ 0 1 ]\n// expect: [ 1 0 ]\n// expect: [ 0 1 ]\n\nprint f.shape()\n// expect: [ 1, 1 ]\n\nprint (LineIntegral(fn (x, u) u, f, method={ }).total(m) - 1/3) < 1e-10\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_line_in_2d_grad.morpho",
    "content": "// CG2 FunctionSpace in one dimension\n\nimport meshtools \n\nvar m = LineMesh(fn (t) [t,t], 0..1:0.5) \n\nvar l = FiniteElementSpace(\"CG2\", grade=1)\n\nvar f = Field(m, fn (x,y) x^2+y^2, finiteelementspace=l)\n\nfn integrand(x, u) {\n    var g = grad(u)\n    return g.norm()^2\n}\n\nprint abs(LineIntegral(integrand, f, method={ }).total(m) - 8*sqrt(2)/3) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_line_in_3d_integrate.morpho",
    "content": "\nimport meshtools \n\nvar m = LineMesh(fn (t) [t,0,0], 0..1) \n\nvar cg2 = FiniteElementSpace(\"CG2\", grade=1)\n\nvar f = Field(m, fn (x,y,z) x^2, finiteelementspace=cg2)\n\nvar ans = 20/21\n\nprint (LineIntegral(fn (x) 1-x[0]^20, method={ }).total(m) - ans) < 1e-8\n// expect: true\n\nprint (LineIntegral(fn (x, g) 1-x[0]^20, f, method={ }).total(m) - ans) < 1e-8\n// expect: true\n\nprint (LineIntegral(fn (x, g) 1-g^10, f, method={ }).total(m) - ans) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_vol_in_3d.morpho",
    "content": "// CG2 FunctionSpace in three dimensions\n\nimport meshtools \n\nvar cube = [ [0,0,0], [1,0,0], [0,1,0], [1,1,0],\n             [0,0,1], [1,0,1], [0,1,1], [1,1,1] ]\n\nvar pts = []\nfor (x in cube) pts.append(Matrix(x))\n\nvar m = DelaunayMesh(pts)\nm.addgrade(1)\n\nvar fe = FiniteElementSpace(\"CG2\", grade=3)\n\nvar f = Field(m, fn (x,y,z) x*y*z, finiteelementspace=fe)\nprint f.shape() // expect: [ 1, 1, 0, 0 ]\n\nprint abs(VolumeIntegral(fn (x, q) q, f, method={}).total(m) - 0.125) < 1e-8\n// expect: true\n\nvar g = Field(m, fn (x,y,z) x^2*(y+z), finiteelementspace=fe)\nprint abs(VolumeIntegral(fn (x, q) q, g, method={}).total(m) - 1/3) < 1e-8\n// expect: true\n\nvar h = Field(m, fn (x,y,z) z^3-x^2-y, finiteelementspace=fe)\nprint abs(VolumeIntegral(fn (x, q) q, h, method={}).total(m) - (-7/12)) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/field/discretizations/cg2_vol_in_3d_grad.morpho",
    "content": "// CG2 FunctionSpace in three dimensions\n\nimport meshtools \n\nvar cube = [ [0,0,0], [1,0,0], [0,1,0], [1,1,0],\n             [0,0,1], [1,0,1], [0,1,1], [1,1,1] ]\n\nvar pts = []\nfor (x in cube) pts.append(Matrix(x))\n\nvar m = DelaunayMesh(pts)\nm.addgrade(1)\n\nvar fe = FiniteElementSpace(\"CG2\", grade=3)\n\nfn normsq(x, q) {\n    var g = grad(q)\n    return g.norm()^2\n}\n\nvar f = Field(m, fn (x,y,z) x*y + y*z + z*x, finiteelementspace=fe)\nprint abs(VolumeIntegral(normsq, f, method={}).total(m) - 7/2) < 1e-6\n// expect: true\n\nf = Field(m, fn (x,y,z) x^2 - y^2 + 2*x*y, finiteelementspace=fe)\nprint abs(VolumeIntegral(normsq, f, method={}).total(m) - 16/3) < 1e-6\n// expect: true\n\nf = Field(m, fn (x,y,z) x + y + z, finiteelementspace=fe)\nprint abs(VolumeIntegral(normsq, f, method={}).total(m) - 3) < 1e-6\n// expect: true\n"
  },
  {
    "path": "test/field/enumerate.morpho",
    "content": "// Operations on Field elements\n\nvar m = Mesh(\"square.mesh\")\n\nvar f = Field(m, Matrix([1,0]), grade = 2)\nprint f.count()\n// expect: 2\n\nfor (v in f) print v\n// expect: [ 1 ]\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 0 ]\n\nvar g = f.op(fn (x) x.norm())\nprint g.count()\n// expect: 2\n\nfor (nrm in g) print nrm\n// expect: 1\n// expect: 1\n"
  },
  {
    "path": "test/field/grade.morpho",
    "content": "// Higher grades\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m, grade=2)\n\nf[2, 0]=1\nf[1]=2\n\nprint f[2, 0]\n// expect: 1\nprint f[1]\n// expect: 2\n\nvar g = Field(m, Matrix(2,2), grade=2)\n\ng[1]=Matrix([[1,2],[3,4]])\nprint g[1]\n// expect: [ 1 2 ]\n// expect: [ 3 4 ]\n"
  },
  {
    "path": "test/field/inherited.morpho",
    "content": "// Test Matrix methods inherited from Object \n\nvar m = Mesh() \n\nvar a = Field(m)\n\nprint a.respondsto(\"respondsto\") // expect: true\n\nprint islist(a.respondsto()) // expect: true\n\nprint a.clss() // expect: @Field\n\nprint a.superclass() // expect: @Object\n\nprint islist(a.invoke(\"respondsto\")) // expect: true\n\nprint a.has(\"a\") // expect: false\n\n"
  },
  {
    "path": "test/field/inner.morpho",
    "content": "// Inner product\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m)\n\n// Set elements\nfor (i in 0...4) f[i]=i\n\nprint f.inner(f)\n// expect: 14\n"
  },
  {
    "path": "test/field/linearize.morpho",
    "content": "// Access the underlying linearized store for the Field\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m)\nvar a = f.linearize() \nvar b = f.__linearize() \n\nprint a\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n\nfor (i in 0...4) a[i] = i\nprint f \n// expect: <Field>\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n\nfor (i in 0...4) b[i] = i\nprint f \n// expect: <Field>\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n\nvar f2 = Field(m, Matrix([1,0]))\nprint f2.linearize() \n// expect: [ 1 ]\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 0 ]\n"
  },
  {
    "path": "test/field/matrix.morpho",
    "content": "// Matrix field\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m, Matrix(2,2))\n\nprint f[0]\n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\n\nf[1]=f[0]=Matrix([[1,0],[0,1]])\n\nvar g = f+f\n\nprint g[1]\n// expect: [ 2 0 ]\n// expect: [ 0 2 ]\n\nvar h = Field(m, fn (x,y,z) Matrix([[cos(x),0],[0,sin(x)]]))\n\nprint h[1]\n// expect: [ 0.540302 0 ]\n// expect: [ 0 0.841471 ]\n"
  },
  {
    "path": "test/field/methods.morpho",
    "content": "// Scalar field\n\nvar m = Mesh(\"square.mesh\")\n\nvar f = Field(m, Matrix([1,2]), grade = [1, 0, 1])\n\nprint f.shape()\n// expect: [ 1, 0, 1 ]\n\nprint f.mesh()\n// expect: <Mesh: 4 vertices>\n"
  },
  {
    "path": "test/field/multigrade.morpho",
    "content": "// Multiple grades\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m, Matrix([1,2]), grade = [1,0,1])\n\nfor (i in 0...4) f[0,i]=Matrix([i,-1])\nfor (i in 0...2) f[2,i]=Matrix([1/2,-2])\n\nprint f[0,1]\n// expect: [ 1 ]\n// expect: [ -1 ]\n\nprint f[2,1]\n// expect: [ 0.5 ]\n// expect: [ -2 ]\n"
  },
  {
    "path": "test/field/neg_nilMesh.morpho",
    "content": "Field(nil,Matrix([1,0,0]))\n//expect error 'FldMshArg'"
  },
  {
    "path": "test/field/negate.morpho",
    "content": "// Negate a field\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m, Matrix([[1,0],[0,1]]))\n\nvar g = -f\n\nprint g[0]\n// expect: [ -1 0 ]\n// expect: [ 0 -1 ]\n"
  },
  {
    "path": "test/field/op.morpho",
    "content": "// Operations on Field elements\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m, Matrix([2,0,0]), grade = [1,0,1])\n\nvar g = f.op(fn (x) x.norm())\nprint g.inner(g)\n// expect: 24\n\nvar h = f.op(fn (x,y) x.inner(y), f)\nprint h.inner(h)\n// expect: 96\n"
  },
  {
    "path": "test/field/op_in_loop.morpho",
    "content": "// Operations on Field elements in a loop\n\nimport meshtools \n\nvar m = AreaMesh(fn(u,v) [u,v,0], -1..1:0.1, -1..1:0.1)\n\nvar f = Field(m, Matrix([1,0,0]))\n\nvar d_n\nfor (i in 1..1000) {\n    d_n = f.op(fn (x) x.inner(Matrix([1/2, sqrt(3)/2, 0])))\n}\n\nprint \"ok\"\n// expect: ok\n"
  },
  {
    "path": "test/field/scalar.morpho",
    "content": "// Scalar field\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\n// Create a convenience function for output\nfn write(f) { print \"[${f[0]}, ${f[1]}, ${f[2]}, ${f[3]}]\" }\n\nvar f = Field(m)\nvar g = Field(m)\n\nwrite(f)\n// expect: [0, 0, 0, 0]\n\nprint f[0]\n// expect: 0\n\n// Set elements\nfor (i in 0...4) f[i]=i\nfor (i in 0...4) g[i]=i/2\n\nwrite(f)\n// expect: [0, 1, 2, 3]\n\n// Arithmetic operations\nwrite(f + g)\n// expect: [0, 1.5, 3, 4.5]\n\nwrite(f - g)\n// expect: [0, 0.5, 1, 1.5]\n\n// Clone\nvar h = f.clone()\n\n// Accumulate\nf.acc(-1, f)\n\nwrite(f)\n// expect: [0, 0, 0, 0]\n\nwrite(h)\n// expect: [0, 1, 2, 3]\n\n// Construct by mapping a function over the vertices\nvar q = Field(m, fn (x,y,z) x )\n\nwrite(q)\n// expect: [0, 1, 0, 1]\n\n// f.op(fn (a,b) a.inner(b), g)\n\n// f.op(fn (a) a.norm())\n\nimport meshtools\n\nvar sm = LineMesh(fn (t) [t,0,0], 0..1:0.2)\n\nvar sf = Field(sm, 2.0)\nprint sf\n// expect: <Field>\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\nsf = Field(sm, 2)\nprint sf\n// expect: <Field>\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n"
  },
  {
    "path": "test/field/scalarmul.morpho",
    "content": "// Scalar multiplication of a field\n\nvar m = Mesh(\"square.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar f = Field(m)\nvar g = Field(m)\n\n// Create a convenient function for output\nfn write(f) { print \"[${f[0]}, ${f[1]}, ${f[2]}, ${f[3]}]\" }\n\n// Set elements\nfor (i in 0...4) f[i]=i\n\nwrite(f*2)\n// expect: [0, 2, 4, 6]\n\nwrite(2*f)\n// expect: [0, 2, 4, 6]\n\nwrite(f/2)\n// expect: [0, 0.5, 1, 1.5]\n\nf*=2\n\nwrite(f)\n// expect: [0, 2, 4, 6]\n"
  },
  {
    "path": "test/field/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/file/file.morpho",
    "content": "/*\n * File class\n */\n\n// Read in characters\nvar f = File(\"test.txt\")\n\nfor (var i=0; i<5; i=i+1) print f.readchar()\nf.close()\n// expect: L\n// expect: o\n// expect: r\n// expect: e\n// expect: m\n\n// Verify we read nil after the file is closed\nfor (var i=0; i<2; i=i+1) print f.readchar()\n// expect: nil\n// expect: nil\n\nf=File(\"test.txt\")\nvar p=f.lines()\nf.close()\n\nprint p[3]\n// expect: Curabitur gravida neque in neque sodales molestie.\n\nprint \"---\"\n// expect: ---\n\n// Read lines in individually\nvar g = File(\"test.txt\")\nwhile (!g.eof()) {\n  var line = g.readline();\n  if (line) print line;\n}\ng.close()\n// expect: Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n// expect: Phasellus sit amet ligula in ante porta mattis.\n// expect: Duis fringilla dui nec tristique rhoncus.\n// expect: Curabitur gravida neque in neque sodales molestie.\n// expect: Fusce ut velit pellentesque lectus porttitor malesuada a pellentesque libero.\n// expect: Ut tristique leo quis ullamcorper ornare.\n// expect: Integer cursus nunc nibh, eu interdum nisl mattis quis.\n// expect: Phasellus in lectus id dolor rhoncus cursus.\n// expect: Maecenas dapibus tincidunt turpis, nec fermentum augue ultrices sed.\n// expect: Curabitur venenatis sem sed velit varius tristique.\n"
  },
  {
    "path": "test/file/file_lines.morpho",
    "content": "/*\n * File class\n */\n\nvar f=File(\"test.txt\")\nvar p=f.lines()\nf.close()\n\nfor (var i=0; i<12; i=i+1) print p[i]\n// expect: Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n// expect: Phasellus sit amet ligula in ante porta mattis.\n// expect: Duis fringilla dui nec tristique rhoncus.\n// expect: Curabitur gravida neque in neque sodales molestie.\n// expect: Fusce ut velit pellentesque lectus porttitor malesuada a pellentesque libero.\n// expect: Ut tristique leo quis ullamcorper ornare.\n// expect: Integer cursus nunc nibh, eu interdum nisl mattis quis.\n// expect: Phasellus in lectus id dolor rhoncus cursus.\n// expect: Maecenas dapibus tincidunt turpis, nec fermentum augue ultrices sed.\n// expect: Curabitur venenatis sem sed velit varius tristique.\n// expect error 'IndxBnds'\n"
  },
  {
    "path": "test/file/file_not_found.morpho",
    "content": "/*\n * File class\n */\n\n// Read in characters\nvar f = File(\"randomcrazyfile.txt\")\n// expect error: 'FlOpnFld'\n"
  },
  {
    "path": "test/file/file_readall.morpho",
    "content": "// Test the readall method, that reads the whole file into a string. \n\nvar f=File(\"test.txt\")\nvar p=f.readall()\nf.close()\n\nprint isstring(p)\n// expect: true\n\nvar words = []\nfor (word in p.split(\" \\n\")) if (word.count()>0) words.append(word)\n\nprint words.count() \n// expect: 77\n\nvar f2=File(\"string.txt\")\nvar p2=f2.readall()\nf2.close()\n\nprint p2[p2.count()-1]\n// expect: ]\n"
  },
  {
    "path": "test/file/file_write.morpho",
    "content": "/*\n * File class\n */\n\nvar file = \"testout.txt\"\n\nvar f = File(file, \"w\")\nf.close()\n\n// Write some lines\nf = File(file, \"w\")\nfor (var i=0; i<3; i=i+1) f.write(\"I have ${i} apples.\")\nf.close()\n\n// Now read them back\nvar g = File(file, \"r\")\nwhile (!g.eof()) {\n  var line = g.readline();\n  if (line) print line;\n}\n// expect: I have 0 apples.\n// expect: I have 1 apples.\n// expect: I have 2 apples.\ng.close()\n\nf = File(file, \"append\")\nfor (var i=3; i<6; i=i+1) f.write(\"I have ${i} apples.\")\nf.close()\n\ng = File(file)\nwhile (!g.eof()) {\n  var line = g.readline();\n  if (line) print line;\n}\n// expect: I have 0 apples.\n// expect: I have 1 apples.\n// expect: I have 2 apples.\n// expect: I have 3 apples.\n// expect: I have 4 apples.\n// expect: I have 5 apples.\n"
  },
  {
    "path": "test/file/filename_missing.morpho",
    "content": "/*\n * File class\n */\n\n// Read in characters\nvar f = File()\n// expect error: 'FlNmMssng'\n"
  },
  {
    "path": "test/file/filename_not_string.morpho",
    "content": "/*\n * File class\n */\n\n// Read in characters\nvar f = File(1)\n// expect error: 'FlNmArgs'\n"
  },
  {
    "path": "test/file/folder/file1.txt",
    "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus sit amet ligula in ante porta mattis.\nDuis fringilla dui nec tristique rhoncus.\nCurabitur gravida neque in neque sodales molestie.\nFusce ut velit pellentesque lectus porttitor malesuada a pellentesque libero.\nUt tristique leo quis ullamcorper ornare.\nInteger cursus nunc nibh, eu interdum nisl mattis quis.\nPhasellus in lectus id dolor rhoncus cursus.\nMaecenas dapibus tincidunt turpis, nec fermentum augue ultrices sed.\nCurabitur venenatis sem sed velit varius tristique.\n"
  },
  {
    "path": "test/file/folder/file2.txt",
    "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus sit amet ligula in ante porta mattis.\nDuis fringilla dui nec tristique rhoncus.\nCurabitur gravida neque in neque sodales molestie.\nFusce ut velit pellentesque lectus porttitor malesuada a pellentesque libero.\nUt tristique leo quis ullamcorper ornare.\nInteger cursus nunc nibh, eu interdum nisl mattis quis.\nPhasellus in lectus id dolor rhoncus cursus.\nMaecenas dapibus tincidunt turpis, nec fermentum augue ultrices sed.\nCurabitur venenatis sem sed velit varius tristique.\n"
  },
  {
    "path": "test/file/folder/file3.txt",
    "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus sit amet ligula in ante porta mattis.\nDuis fringilla dui nec tristique rhoncus.\nCurabitur gravida neque in neque sodales molestie.\nFusce ut velit pellentesque lectus porttitor malesuada a pellentesque libero.\nUt tristique leo quis ullamcorper ornare.\nInteger cursus nunc nibh, eu interdum nisl mattis quis.\nPhasellus in lectus id dolor rhoncus cursus.\nMaecenas dapibus tincidunt turpis, nec fermentum augue ultrices sed.\nCurabitur venenatis sem sed velit varius tristique.\n"
  },
  {
    "path": "test/file/folder.morpho",
    "content": "/*\n * Folder class\n */\n\n// Check if a resource is a folder \nprint Folder.isfolder(\"folder\")\n// expect: true\n\n// Find the contents of a folder\nvar files = Folder.contents(\"folder\")\n\nprint files.count()\n// expect: 3\n\n"
  },
  {
    "path": "test/file/remote.morpho",
    "content": "var f = File(\"test.txt\", \"read\")\n\nprint f.filename() // expect: test.txt\nvar rp = f.relativepath()\nprint rp // expect: file/test.txt\n\nprint \"${rp} etc\" // expect: file/test.txt etc\n\nf.close()\n"
  },
  {
    "path": "test/file/string.txt",
    "content": "[ abcedfghijklmnopqrstuvwxyz ]"
  },
  {
    "path": "test/file/test.txt",
    "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus sit amet ligula in ante porta mattis.\nDuis fringilla dui nec tristique rhoncus.\nCurabitur gravida neque in neque sodales molestie.\nFusce ut velit pellentesque lectus porttitor malesuada a pellentesque libero.\nUt tristique leo quis ullamcorper ornare.\nInteger cursus nunc nibh, eu interdum nisl mattis quis.\nPhasellus in lectus id dolor rhoncus cursus.\nMaecenas dapibus tincidunt turpis, nec fermentum augue ultrices sed.\nCurabitur venenatis sem sed velit varius tristique.\n"
  },
  {
    "path": "test/file/testout.txt",
    "content": "I have 0 apples.\nI have 1 apples.\nI have 2 apples.\nI have 3 apples.\nI have 4 apples.\nI have 5 apples.\n"
  },
  {
    "path": "test/for/class_in_body.morpho",
    "content": "// Body must be a statement\nfor (;;) class Foo {}\n\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/for/closure_in_body.morpho",
    "content": "var f1\nvar f2\nvar f3\n\nfor (var i = 1; i < 4; i+=1) {\n  var j = i\n  fn f() {\n    print i\n    print j\n  }\n\n  if (j == 1) f1 = f\n  else if (j == 2) f2 = f\n  else f3 = f\n}\n\nf1() // expect: 4\n     // expect: 1\nf2() // expect: 4\n     // expect: 2\nf3() // expect: 4\n     // expect: 3\n"
  },
  {
    "path": "test/for/fn_in_body.morpho",
    "content": "// Body must be a statement\nfor (;;) fn foo() {}\n\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/for/return_closure.morpho",
    "content": "// Return a closure from within a loop\n\nfn f() {\n  for (;;) {\n    var i = \"i\"\n    fn g() { print i }\n    return g\n  }\n}\n\nvar h = f()\nh() // expect: i\n"
  },
  {
    "path": "test/for/return_inside.morpho",
    "content": "// Return a value inside a loop\n\nfn f() {\n  for (;;) {\n    var i = \"i\"\n    return i\n  }\n}\n\nprint f()\n// expect: i\n"
  },
  {
    "path": "test/for/scope.morpho",
    "content": "// Check scoping rules\n\n{\n  var i = \"before\"\n\n  // New variable is in inner scope.\n  for (var i = 0; i < 1; i+=1) {\n    print i // expect: 0\n\n    // Loop body is in second inner scope.\n    var i = -1\n    print i // expect: -1\n  }\n\n  print i // expect: before\n}\n\n{\n  // New variable shadows outer variable.\n  for (var i = 0; i > 0; i+=1) {}\n\n  // Goes out of scope after loop.\n  var i = \"after\"\n  print i // expect: after\n\n  // Can reuse an existing variable.\n  for (i = 0; i < 1; i+=1) {\n    print i // expect: 0\n  }\n\n  print i // expect: 1\n}\n"
  },
  {
    "path": "test/for/statement_condition.morpho",
    "content": "// Loop condition can't be a statement\n\nfor (var a = 1; var b = 2; a+=1) {}\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/for/statement_increment.morpho",
    "content": "// Increment can't be a statement\nfor (var a = 1; a < 2; print \"a\") {}\n\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/for/statement_initializer.morpho",
    "content": "// Initializer must be a statement\nfor (while (true); a < 2; a = a + 1) {}\n\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/for/syntax.morpho",
    "content": "// Single-expression body.\nfor (var c = 0; c < 3;) print c+=1\n// expect: 1\n// expect: 2\n// expect: 3\n\n// Block body.\nfor (var a = 0; a < 3; a+=1) {\n  print a\n}\n// expect: 0\n// expect: 1\n// expect: 2\n\n\n// No clauses.\nfn foo() {\n  for (;;) return \"done\"\n}\nprint foo() // expect: done\n\n\n// No variable.\nvar i = 0\nfor (; i < 2; i = i + 1) print i\n// expect: 0\n// expect: 1\n\n// No condition.\nfn bar() {\n  for (var i = 0;; i = i + 1) {\n    print i\n    if (i >= 2) return\n  }\n}\nbar()\n// expect: 0\n// expect: 1\n// expect: 2\n\n\n// No increment.\nfor (var i = 0; i < 2;) {\n  print i\n  i+=1\n}\n// expect: 0\n// expect: 1\n\n// Statement bodies.\nfor (; false;) if (true) 1; else 2\nfor (; false;) while (true) 1\nfor (; false;) for (;;) 1\n"
  },
  {
    "path": "test/for/var_in_body.morpho",
    "content": "// Body must be a statement\nfor (;;) var foo;\n\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/for_in/forin.morpho",
    "content": "// For in loops\n\nvar a = Matrix([1,2,3,4])\n\nfor (var x in a) {\n  print x\n}\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n\nfor (var x in a) print 2*x\n// expect: 2\n// expect: 4\n// expect: 6\n// expect: 8\n"
  },
  {
    "path": "test/for_in/forin_custom.morpho",
    "content": "// For in loops with a custom collection\n\nclass Collection {\n  init (n) { self.count=n }\n\n  enumerate(k) {\n    if (k>=0) return (k+1)^2\n    else return self.count\n  }\n}\n\nvar a=Collection(4)\n\nfor (var x in a) {\n  print x\n}\n// expect: 1\n// expect: 4\n// expect: 9\n// expect: 16\n"
  },
  {
    "path": "test/for_in/forin_in_function.morpho",
    "content": "// For in loops\n\nfn f(u) {\n  for (var x in u) print x\n}\n\nvar a = Matrix([1,2,3,4])\n\nf(a)\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n"
  },
  {
    "path": "test/for_in/forin_index.morpho",
    "content": "// For in loops with index parameter\n\nvar a = Matrix([1,2,3,4])\n\nfor (var x, i in a) {\n  print \"${i}: ${x}\"\n}\n// expect: 0: 1\n// expect: 1: 2\n// expect: 2: 3\n// expect: 3: 4\n\n// Optional var\nfor (x, i in a) {\n  print \"${i}: ${x}\"\n}\n// expect: 0: 1\n// expect: 1: 2\n// expect: 2: 3\n// expect: 3: 4\n"
  },
  {
    "path": "test/for_in/forin_string.morpho",
    "content": "// For in loops with strings\n\nvar a = \"Hello\"\n\nfor (var x in a) {\n  print x\n}\n// expect: H\n// expect: e\n// expect: l\n// expect: l\n// expect: o\n"
  },
  {
    "path": "test/function/anonymous.morpho",
    "content": "// Anonymous functions\n\nprint apply(fn (x) x^2, 0.5)\n// expect: 0.25\n\nvar b = fn (x) 1-x^2\n\nprint b(0.5)\n// expect: 0.75\n"
  },
  {
    "path": "test/function/anonymous_closure.morpho",
    "content": "// Anonymous closure\n\nvar a = 0.5\nprint apply(fn (x) x+a, 0.2)\n// expect: 0.7\n\nfn f(x) {\n  return fn (y) x+y\n}\n\nprint f(0.4)\n// expect: <<fn >>\n\nprint f(0.4)(0.4)\n// expect: 0.8\n"
  },
  {
    "path": "test/function/anonymous_closure_assign.morpho",
    "content": "// Anonymous closure with assignment\n\nvar a = 0.5\nvar f = fn (x) a+=x\n\nprint a\n// expect: 0.5\nf(0.5)\n\nprint a\n// expect: 1\n\nf(0.5)\nprint a\n// expect: 1.5\n"
  },
  {
    "path": "test/function/anonymous_closure_noparams.morpho",
    "content": "// Anonymous closure\n\nvar a = 0.5\nprint apply(fn () a, [])\n// expect: 0.5\n\nfn f(x) {\n  return fn () x\n}\n\nprint f(0.4)\n// expect: <<fn >>\n\nprint f(0.4)()\n// expect: 0.4\n\nfor (i in 1..5) {\n  print apply(fn () i, [])\n}\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n// expect: 5\n"
  },
  {
    "path": "test/function/anonymous_in_args.morpho",
    "content": "// Anonymous fn in the arguments of a function \n\nfn f(g, x) {\n    print g(x)\n}\n\nf(fn (x) x, \"Foo\") // expect: Foo\n\nf(fn (x) \"Boo\", \"Foo\") // expect: Boo\n\nf(fn (x) { return x[0] }, \"Foo\") // expect: F\n"
  },
  {
    "path": "test/function/anonymous_in_optional_args.morpho",
    "content": "// Anonymous fn in the optional arguments of a function \n\nfn f(x, g=nil) {\n    print g(x)\n}\n\nf(\"Foo\", g=fn (x) x) // expect: Foo\n\nf(\"Foo\", g=fn (x) \"Boo\") // expect: Boo\n\nf(\"Foo\", g=fn (x) { return x[0] }) // expect: F\n"
  },
  {
    "path": "test/function/body_must_be_block.morpho",
    "content": "\nfn f() 123\n// expect error 'FnMssngLftBrc'\n"
  },
  {
    "path": "test/function/constant_arg_optional.morpho",
    "content": "// Ensure regular args aren't checked for interned literals\n\nfn f(name, size, id=nil) {\n  print name \n}\n\nf(\"Helvetica\", 1)\n// expect: Helvetica\n"
  },
  {
    "path": "test/function/empty_body.morpho",
    "content": "fn f() {}\nprint f() // expect: nil\n"
  },
  {
    "path": "test/function/extra_arguments.morpho",
    "content": "fn f(a, b) {\n  print a\n  print b\n}\n\nf(1, 2, 3, 4) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/index_in_arguments.morpho",
    "content": "// Check that you can call a function with index access in arguments\n\nfn f(a,b) {\n  return \"${a}, ${b}\"\n}\n\nvar a = [ 1, 2 ]\n\nprint f(a[0],a[1])\n// expect: 1, 2\n"
  },
  {
    "path": "test/function/local_recursion.morpho",
    "content": "{\n  fn fib(n) {\n    if (n < 2) return n\n    return fib(n - 1) + fib(n - 2)\n  }\n\n  print fib(8) // expect: 21\n}\n"
  },
  {
    "path": "test/function/missing_arguments.morpho",
    "content": "fn f(a, b) {}\n\nf(1) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/missing_comma_in_parameters.morpho",
    "content": "fn foo(a, b c, d, e, f) {}\n// expect error 'UnknwnType'\n"
  },
  {
    "path": "test/function/mutual_recursion/duplicate_local_mutual_recursion.morpho",
    "content": "// Two forward references to the same function\n\n{\n  fn isEven(n) {\n    if (n == 0) return true\n    return isOdd(n - 1)\n  }\n\n  fn isEvenEven(n) {\n    if (n == 0) return true\n    return isOdd(n - 1)\n  }\n\n  fn isOdd(n) {\n    if (n == 0) return false\n    return isEven(n - 1)\n  }\n\n  print isEvenEven(4) // expect: true\n}\n"
  },
  {
    "path": "test/function/mutual_recursion/local_mutual_recursion.morpho",
    "content": "{\n  fn isEven(n) {\n    if (n == 0) return true\n    return isOdd(n - 1)\n  }\n\n  fn isOdd(n) {\n    if (n == 0) return false\n    return isEven(n - 1)\n  }\n\n  print isEven(4) // expect: true\n}\n"
  },
  {
    "path": "test/function/mutual_recursion/local_mutual_recursion_out_of_scope.morpho",
    "content": "{\n  fn isEven(n) {\n    if (n == 0) return true\n    return isOdd(n - 1)\n  }\n}\n\nfn isOdd(n) {\n  if (n == 0) return false\n  return isEven(n - 1)\n}\n\nisEven(4); // expect error 'UnrslvdFrwdRf'\n"
  },
  {
    "path": "test/function/mutual_recursion/mutual_recursion.morpho",
    "content": "fn isEven(n) {\n  if (n == 0) return true\n  return isOdd(n - 1)\n}\n\nfn isOdd(n) {\n  if (n == 0) return false\n  return isEven(n - 1)\n}\n\nprint isEven(4) // expect: true\nprint isOdd(3) // expect: true\n"
  },
  {
    "path": "test/function/mutual_recursion/mutual_recursion_in_closure.morpho",
    "content": "fn g() {\n  fn isEven(n) {\n    if (n == 0) return true\n    return isOdd(n - 1)\n  }\n\n  fn isOdd(n) {\n    if (n == 0) return false\n    return isEven(n - 1)\n  }\n\n  return isEven\n}\n\nvar isEv=g()\nprint isEv(3) // expect: false\nprint isEv(4) // expect: true\n"
  },
  {
    "path": "test/function/mutual_recursion/undefined_call_in_global.morpho",
    "content": "// Try to call an undefined function in global scope\n\nprint isOdd(4) // expect error 'SymblUndf'\n"
  },
  {
    "path": "test/function/mutual_recursion/unresolved_forward_ref_in_func.morpho",
    "content": "// Forward references must be resolved in the same scope\n\nfn f() {\n    return fn (u) func(u)\n}\n\nfn func(u) {\n    return u\n}\n\nvar x = f()\nprint x(1)\n\n// expect error 'UnrslvdFrwdRf'\n"
  },
  {
    "path": "test/function/mutual_recursion/unresolved_forward_reference.morpho",
    "content": "// A forward reference that is never supplied\n\nfn isEven(n) {\n  if (n == 0) return true\n  return isOdd(n - 1)\n}\n\n// expect error 'UnrslvdFrwdRf'\n"
  },
  {
    "path": "test/function/optional.morpho",
    "content": "// Optional arguments\n\nfn func(x, y=true, z=2) {\n  print x\n  print y\n  print z\n}\n\nfunc(1)\n// expect: 1\n// expect: true\n// expect: 2\n\nfunc(1, y=false)\n// expect: 1\n// expect: false\n// expect: 2\n\nfunc(1, z=3)\n// expect: 1\n// expect: true\n// expect: 3\n\nfunc(1, z=3, y=false)\n// expect: 1\n// expect: false\n// expect: 3\n\nfunc(1, 2, y=false)\n// expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/optional_invalid.morpho",
    "content": "// Function that doesn't support optional arguments called with them \n\nfn f(x,y,z,a) { return -1 }\n\nprint f(1,z=3) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/optional_non_constant_default.morpho",
    "content": "// Optional arguments\n\nfn func(x, y=2*5) {\n\n}\n\n// expect error 'OptPrmDflt'\n"
  },
  {
    "path": "test/function/optional_too_few_fixed.morpho",
    "content": "// Optional arguments\n\nfn func(x, y=true, z=2) {\n  print x\n  print y\n  print z\n}\n\nfunc(y=false)\n// expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/parameters.morpho",
    "content": "fn f0() { return 0; }\nprint f0() // expect: 0\n\nfn f1(a) { return a }\nprint f1(1) // expect: 1\n\nfn f2(a, b) { return a + b }\nprint f2(1, 2) // expect: 3\n\nfn f3(a, b, c) { return a + b + c }\nprint f3(1, 2, 3) // expect: 6\n\nfn f4(a, b, c, d) { return a + b + c + d }\nprint f4(1, 2, 3, 4) // expect: 10\n\nfn f5(a, b, c, d, e) { return a + b + c + d + e }\nprint f5(1, 2, 3, 4, 5) // expect: 15\n\nfn f6(a, b, c, d, e, f) { return a + b + c + d + e + f }\nprint f6(1, 2, 3, 4, 5, 6) // expect: 21\n\nfn f7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g }\nprint f7(1, 2, 3, 4, 5, 6, 7) // expect: 28\n\nfn f8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h }\nprint f8(1, 2, 3, 4, 5, 6, 7, 8) // expect: 36\n"
  },
  {
    "path": "test/function/print.morpho",
    "content": "// Print functions \nfn foo() {}\nprint foo // expect: <fn foo>\n\nprint clock // expect: <fn clock>\n"
  },
  {
    "path": "test/function/recursion.morpho",
    "content": "fn fib(n) {\n  if (n < 2) return n\n  return fib(n - 1) + fib(n - 2)\n}\n\nprint fib(8) // expect: 21\n"
  },
  {
    "path": "test/function/stack_overflow.morpho",
    "content": "// Cause stack overflow with a recursive function \n\nfn f(n) {\n  return f(n + 1)\n}\n\nprint f(1) // expect error 'StckOvflw'\n"
  },
  {
    "path": "test/function/too_many_arguments.morpho",
    "content": "fn foo() {}\n{\n  var a = 1\n  foo(\n     a, // 1\n     a, // 2\n     a, // 3\n     a, // 4\n     a, // 5\n     a, // 6\n     a, // 7\n     a, // 8\n     a, // 9\n     a, // 10\n     a, // 11\n     a, // 12\n     a, // 13\n     a, // 14\n     a, // 15\n     a, // 16\n     a, // 17\n     a, // 18\n     a, // 19\n     a, // 20\n     a, // 21\n     a, // 22\n     a, // 23\n     a, // 24\n     a, // 25\n     a, // 26\n     a, // 27\n     a, // 28\n     a, // 29\n     a, // 30\n     a, // 31\n     a, // 32\n     a, // 33\n     a, // 34\n     a, // 35\n     a, // 36\n     a, // 37\n     a, // 38\n     a, // 39\n     a, // 40\n     a, // 41\n     a, // 42\n     a, // 43\n     a, // 44\n     a, // 45\n     a, // 46\n     a, // 47\n     a, // 48\n     a, // 49\n     a, // 50\n     a, // 51\n     a, // 52\n     a, // 53\n     a, // 54\n     a, // 55\n     a, // 56\n     a, // 57\n     a, // 58\n     a, // 59\n     a, // 60\n     a, // 61\n     a, // 62\n     a, // 63\n     a, // 64\n     a, // 65\n     a, // 66\n     a, // 67\n     a, // 68\n     a, // 69\n     a, // 70\n     a, // 71\n     a, // 72\n     a, // 73\n     a, // 74\n     a, // 75\n     a, // 76\n     a, // 77\n     a, // 78\n     a, // 79\n     a, // 80\n     a, // 81\n     a, // 82\n     a, // 83\n     a, // 84\n     a, // 85\n     a, // 86\n     a, // 87\n     a, // 88\n     a, // 89\n     a, // 90\n     a, // 91\n     a, // 92\n     a, // 93\n     a, // 94\n     a, // 95\n     a, // 96\n     a, // 97\n     a, // 98\n     a, // 99\n     a, // 100\n     a, // 101\n     a, // 102\n     a, // 103\n     a, // 104\n     a, // 105\n     a, // 106\n     a, // 107\n     a, // 108\n     a, // 109\n     a, // 110\n     a, // 111\n     a, // 112\n     a, // 113\n     a, // 114\n     a, // 115\n     a, // 116\n     a, // 117\n     a, // 118\n     a, // 119\n     a, // 120\n     a, // 121\n     a, // 122\n     a, // 123\n     a, // 124\n     a, // 125\n     a, // 126\n     a, // 127\n     a, // 128\n     a, // 129\n     a, // 130\n     a, // 131\n     a, // 132\n     a, // 133\n     a, // 134\n     a, // 135\n     a, // 136\n     a, // 137\n     a, // 138\n     a, // 139\n     a, // 140\n     a, // 141\n     a, // 142\n     a, // 143\n     a, // 144\n     a, // 145\n     a, // 146\n     a, // 147\n     a, // 148\n     a, // 149\n     a, // 150\n     a, // 151\n     a, // 152\n     a, // 153\n     a, // 154\n     a, // 155\n     a, // 156\n     a, // 157\n     a, // 158\n     a, // 159\n     a, // 160\n     a, // 161\n     a, // 162\n     a, // 163\n     a, // 164\n     a, // 165\n     a, // 166\n     a, // 167\n     a, // 168\n     a, // 169\n     a, // 170\n     a, // 171\n     a, // 172\n     a, // 173\n     a, // 174\n     a, // 175\n     a, // 176\n     a, // 177\n     a, // 178\n     a, // 179\n     a, // 180\n     a, // 181\n     a, // 182\n     a, // 183\n     a, // 184\n     a, // 185\n     a, // 186\n     a, // 187\n     a, // 188\n     a, // 189\n     a, // 190\n     a, // 191\n     a, // 192\n     a, // 193\n     a, // 194\n     a, // 195\n     a, // 196\n     a, // 197\n     a, // 198\n     a, // 199\n     a, // 200\n     a, // 201\n     a, // 202\n     a, // 203\n     a, // 204\n     a, // 205\n     a, // 206\n     a, // 207\n     a, // 208\n     a, // 209\n     a, // 210\n     a, // 211\n     a, // 212\n     a, // 213\n     a, // 214\n     a, // 215\n     a, // 216\n     a, // 217\n     a, // 218\n     a, // 219\n     a, // 220\n     a, // 221\n     a, // 222\n     a, // 223\n     a, // 224\n     a, // 225\n     a, // 226\n     a, // 227\n     a, // 228\n     a, // 229\n     a, // 230\n     a, // 231\n     a, // 232\n     a, // 233\n     a, // 234\n     a, // 235\n     a, // 236\n     a, // 237\n     a, // 238\n     a, // 239\n     a, // 240\n     a, // 241\n     a, // 242\n     a, // 243\n     a, // 244\n     a, // 245\n     a, // 246\n     a, // 247\n     a, // 248\n     a, // 249\n     a, // 250\n     a, // 251\n     a, // 252\n     a, // 253\n     a, // 254\n     a, // 255\n     a) // expect error 'TooMnyArg'\n}\n"
  },
  {
    "path": "test/function/too_many_parameters.morpho",
    "content": "// 256 parameters.\nfn f(\n    a1,\n    a2,\n    a3,\n    a4,\n    a5,\n    a6,\n    a7,\n    a8,\n    a9,\n    a10,\n    a11,\n    a12,\n    a13,\n    a14,\n    a15,\n    a16,\n    a17,\n    a18,\n    a19,\n    a20,\n    a21,\n    a22,\n    a23,\n    a24,\n    a25,\n    a26,\n    a27,\n    a28,\n    a29,\n    a30,\n    a31,\n    a32,\n    a33,\n    a34,\n    a35,\n    a36,\n    a37,\n    a38,\n    a39,\n    a40,\n    a41,\n    a42,\n    a43,\n    a44,\n    a45,\n    a46,\n    a47,\n    a48,\n    a49,\n    a50,\n    a51,\n    a52,\n    a53,\n    a54,\n    a55,\n    a56,\n    a57,\n    a58,\n    a59,\n    a60,\n    a61,\n    a62,\n    a63,\n    a64,\n    a65,\n    a66,\n    a67,\n    a68,\n    a69,\n    a70,\n    a71,\n    a72,\n    a73,\n    a74,\n    a75,\n    a76,\n    a77,\n    a78,\n    a79,\n    a80,\n    a81,\n    a82,\n    a83,\n    a84,\n    a85,\n    a86,\n    a87,\n    a88,\n    a89,\n    a90,\n    a91,\n    a92,\n    a93,\n    a94,\n    a95,\n    a96,\n    a97,\n    a98,\n    a99,\n    a100,\n    a101,\n    a102,\n    a103,\n    a104,\n    a105,\n    a106,\n    a107,\n    a108,\n    a109,\n    a110,\n    a111,\n    a112,\n    a113,\n    a114,\n    a115,\n    a116,\n    a117,\n    a118,\n    a119,\n    a120,\n    a121,\n    a122,\n    a123,\n    a124,\n    a125,\n    a126,\n    a127,\n    a128,\n    a129,\n    a130,\n    a131,\n    a132,\n    a133,\n    a134,\n    a135,\n    a136,\n    a137,\n    a138,\n    a139,\n    a140,\n    a141,\n    a142,\n    a143,\n    a144,\n    a145,\n    a146,\n    a147,\n    a148,\n    a149,\n    a150,\n    a151,\n    a152,\n    a153,\n    a154,\n    a155,\n    a156,\n    a157,\n    a158,\n    a159,\n    a160,\n    a161,\n    a162,\n    a163,\n    a164,\n    a165,\n    a166,\n    a167,\n    a168,\n    a169,\n    a170,\n    a171,\n    a172,\n    a173,\n    a174,\n    a175,\n    a176,\n    a177,\n    a178,\n    a179,\n    a180,\n    a181,\n    a182,\n    a183,\n    a184,\n    a185,\n    a186,\n    a187,\n    a188,\n    a189,\n    a190,\n    a191,\n    a192,\n    a193,\n    a194,\n    a195,\n    a196,\n    a197,\n    a198,\n    a199,\n    a200,\n    a201,\n    a202,\n    a203,\n    a204,\n    a205,\n    a206,\n    a207,\n    a208,\n    a209,\n    a210,\n    a211,\n    a212,\n    a213,\n    a214,\n    a215,\n    a216,\n    a217,\n    a218,\n    a219,\n    a220,\n    a221,\n    a222,\n    a223,\n    a224,\n    a225,\n    a226,\n    a227,\n    a228,\n    a229,\n    a230,\n    a231,\n    a232,\n    a233,\n    a234,\n    a235,\n    a236,\n    a237,\n    a238,\n    a239,\n    a240,\n    a241,\n    a242,\n    a243,\n    a244,\n    a245,\n    a246,\n    a247,\n    a248,\n    a249,\n    a250,\n    a251,\n    a252,\n    a253,\n    a254,\n    a255, a) {} // expect error 'TooMnyPrm'\n"
  },
  {
    "path": "test/function/unknown_optional.morpho",
    "content": "// Unknown optional argument\n\nfn func(x, foo=nil) {\n  return foo\n}\n\nfunc(1, boo=\"hoo\")\n// expect error 'UnkwnOptArg'"
  },
  {
    "path": "test/function/variadic.morpho",
    "content": "// Variadic arguments\n\nfn func(x, y, ...varg) {\n  for (a in varg) print a\n}\n\nfunc(1, 2)\n\nfunc(1, 2, 3)\n// expect: 3\n\nfunc(1, 2, 3, 4)\n// expect: 3\n// expect: 4\n\nfunc(1, 2, 3, 4, 5)\n// expect: 3\n// expect: 4\n// expect: 5\n\nfunc(1)\n// expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/variadic_append.morpho",
    "content": "// Can append to variadic argument list\n\nfn func(x, y, ...varg) {\n  for (a in varg) print a\n  for (i in 1..3) varg.append(i)\n  print varg\n}\n\nfunc(1, 2, 3)\n// expect: 3\n// expect: [ 3, 1, 2, 3 ]\n"
  },
  {
    "path": "test/function/variadic_apply.morpho",
    "content": "// Variadic args with apply\n\nfn f(...x) {\n    for (i in x) print i\n}\n\nf(1,2,3)\n// expect: 1\n// expect: 2\n// expect: 3\n\napply(f, [1,2,3])\n// expect: 1\n// expect: 2\n// expect: 3\n"
  },
  {
    "path": "test/function/variadic_in_method.morpho",
    "content": "// Variadic args in a method\n\nclass Foo {\n    foo(...u) {\n        print u\n    }\n\n    boo() {\n        var a = [1,2,3]\n        apply(self.foo, a)\n    }\n}\n\nvar a = Foo()\n\na.foo(1,2,3)\n// expect: [ 1, 2, 3 ]\n\na.boo()\n// expect: [ 1, 2, 3 ]\n\napply(a.foo, [1,2,3])\n// expect: [ 1, 2, 3 ]"
  },
  {
    "path": "test/function/variadic_last.morpho",
    "content": "// Variadic parameter must be last\n\nfn bad(...varg, x, y) {\n\n}\n\n// expect Error: 'VarPrLst'\n"
  },
  {
    "path": "test/function/variadic_only.morpho",
    "content": "// Variadic args \n\nfn func(...a) {\n  for (x in a) print x \n}\n\nfunc(1)\n// expect: 1\n\nfunc(1, 2)\n// expect: 1\n// expect: 2\n\nfunc(1, 2, 3)\n// expect: 1\n// expect: 2\n// expect: 3\n"
  },
  {
    "path": "test/function/variadic_optional.morpho",
    "content": "// Variadic arguments\n\nfn func(x, y, ...varg, q=0) {\n  print \"q=${q}\"\n  for (a in varg) print a\n}\n\nfunc(1, 2)\n// expect: q=0\n\nfunc(1, 2, 3, 4)\n// expect: q=0\n// expect: 3\n// expect: 4\n\nfunc(1, 2, 3, 4, 5, q=\"Hi\")\n// expect: q=Hi\n// expect: 3\n// expect: 4\n// expect: 5\n\nfunc(1, q=4)\n// expect error 'InvldArgs'\n"
  },
  {
    "path": "test/function/variadic_too_many.morpho",
    "content": "// Can only have one variadic parameter\n\nfn bad(x, y, ...varg, ...warg) {\n\n}\n\n// expect Error: 'OneVarPr'\n"
  },
  {
    "path": "test/function/veneer.morpho",
    "content": "// Test veneer class \n\nfn func(x) {\n  return x \n}\n\nprint func // expect: <fn func>\n\nprint func.clss() // expect: @Function\n\nprint func.clone() // expect error 'ObjCantClone'\n"
  },
  {
    "path": "test/functionals/area/area.morpho",
    "content": "import \"../numericalderivatives.morpho\"\n\nvar m = Mesh(\"triangle.mesh\")\nvar a = Area()\n\nprint m\n// expect: <Mesh: 3 vertices>\n\nprint (a.integrand(m) - Matrix([ 1.29904 ])).norm() < 1e-5\n// expect: true\n\nprint (a.gradient(m)-numericalgradient(a, m)).norm() < 1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/area/area2.morpho",
    "content": "import \"../numericalderivatives.morpho\"\n\nvar m = Mesh(\"square.mesh\")\nvar a = Area()\n\nprint m\n// expect: <Mesh: 4 vertices>\nprint (a.total(m) - 1) < 1e-5\n// expect: true\n\nprint (a.integrand(m) - Matrix([[ 0.5, 0.5 ]]) ).norm() < 1e-5\n// expect: true\n\nprint (a.gradient(m)-numericalgradient(a, m)).norm() < 1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/area/area2d.morpho",
    "content": "import meshtools \n\nimport \"../numericalderivatives.morpho\"\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\n\nmb.addface([0,1,2])\n\nvar m = mb.build() \n\nvar a = Area() \n\nprint m\n// expect: <Mesh: 3 vertices>\n\nprint (a.integrand(m) - Matrix([ 0.5 ])).norm() < 1e-5\n// expect: true\n\nprint (a.gradient(m) - numericalgradient(a, m)).norm() < 1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/area/area_hessian.morpho",
    "content": "\nimport meshtools\nimport \"../numericalderivatives.morpho\"\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\n\nmb.addface([0,1,2])\n\nvar m = mb.build() \n\nvar a = Area() \n\nvar h = a.hessian(m)\nvar h2 = numericalhessian(a, m)\n\nprint (Matrix(h) - h2).norm() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/area/area_integrandForElement.morpho",
    "content": "import meshtools\n\nvar m = Mesh(\"square.mesh\")\nvar a = Area()\n\n\nprint a.integrandForElement(m, 1) // expect: 0.5\n"
  },
  {
    "path": "test/functionals/area/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/functionals/area/triangle.mesh",
    "content": "vertices\n\n1 1 0 0\n2 -0.5 0.866025 0\n3 -0.5 -0.866025 0\n\nfaces\n\n1 1 2 3\n"
  },
  {
    "path": "test/functionals/areaenclosed/areaenclosed.morpho",
    "content": "\nimport meshtools\n\nvar np = 40\nvar m=LineMesh(fn (t) [cos(t), sin(t), 0], 0...2*Pi:2*Pi/np, closed=true)\n\nvar a = AreaEnclosed()\n\nprint m\n// expect: <Mesh: 40 vertices>\n\nprint a.integrand(m)\n// expect: [ 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 0.0782172 ]\n\nprint a.total(m)\n// expect: 3.12869\n\nvar grad = a.gradient(m)\nvar value = Matrix([[ 0.156434, 0.154508, 0.148778, 0.139384, 0.126558, 0.110616, 0.0919499, 0.0710198, 0.0483409, 0.0244717, 0, -0.0244717, -0.0483409, -0.0710198, -0.0919499, -0.110616, -0.126558, -0.139384, -0.148778, -0.154508, -0.156434, -0.154508, -0.148778, -0.139384, -0.126558, -0.110616, -0.0919499, -0.0710198, -0.0483409, -0.0244717, -1.03037e-16, 0.0244717, 0.0483409, 0.0710198, 0.0919499, 0.110616, 0.126558, 0.139384, 0.148778, 0.154508 ],[ -1.21667e-16, 0.0244717, 0.0483409, 0.0710198, 0.0919499, 0.110616, 0.126558, 0.139384, 0.148778, 0.154508, 0.156434, 0.154508, 0.148778, 0.139384, 0.126558, 0.110616, 0.0919499, 0.0710198, 0.0483409, 0.0244717, 1.03037e-16, -0.0244717, -0.0483409, -0.0710198, -0.0919499, -0.110616, -0.126558, -0.139384, -0.148778, -0.154508, -0.156434, -0.154508, -0.148778, -0.139384, -0.126558, -0.110616, -0.0919499, -0.0710198, -0.0483409, -0.0244717 ],[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]])\nvar diff = grad-value\nprint diff.norm()<5e-6\n\n// expect: true"
  },
  {
    "path": "test/functionals/areaenclosed/areaenclosed_hessian.morpho",
    "content": "\nimport meshtools\n\nvar m = LineMesh(fn (t) [cos(t), sin(t)], -Pi...Pi:Pi/2, closed=true)\n\nvar a = AreaEnclosed()\n\nprint a.total(m)\n// expect: 2\n\nprint a.hessian(m)\n// expect: [ 0 0 0 0.5 0 0 0 -0.5 ]\n// expect: [ 0 0 -0.5 0 0 0 0.5 0 ]\n// expect: [ 0 -0.5 0 0 0 0.5 0 0 ]\n// expect: [ 0.5 0 0 0 -0.5 0 0 0 ]\n// expect: [ 0 0 0 -0.5 0 0 0 0.5 ]\n// expect: [ 0 0 0.5 0 0 0 -0.5 0 ]\n// expect: [ 0 0.5 0 0 0 -0.5 0 0 ]\n// expect: [ -0.5 0 0 0 0.5 0 0 0 ]"
  },
  {
    "path": "test/functionals/areaenclosed/areaenclosed_integrandForElement.morpho",
    "content": "import meshtools\n\nvar m = LineMesh(fn(t) [cos(t), sin(t), 0], 0..3*Pi/2:Pi/2, closed=true)\n\nm.addgrade(1)\n\nvar a = AreaEnclosed()\n\nprint a.integrandForElement(m, 1) // expect: 0.5\n"
  },
  {
    "path": "test/functionals/areaintegral/areaintegral.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = AreaMesh(fn (x,y) [x,y,0], 0..1:0.5, 0..1:0.5)\n\nvar f = Field(m, fn (x,y,z) Matrix([x,y,z]))\n\n// A line integral with only spatial dependence\nprint AreaIntegral(fn (x) x[0]*x[1]).total(m)\n// expect: 0.25\n\nprint AreaIntegral(fn (x) x[0]^2*(1-x[1]^2)).total(m)\n// expect: 0.222222\n\nprint AreaIntegral(fn (x) (x[0]-1/2)^4*(x[1]-1/2)^4).total(m)\n// expect: 0.00015625\n\nprint AreaIntegral(fn (x) x[0]^6*x[1]^6).total(m)\n// expect: 0.0204082\n\nprint AreaIntegral(fn (x, n) n[0]*n[1], f).total(m)\n// expect: 0.25\n\nprint AreaIntegral(fn (x, n) n[0]^2*(1-n[1]^2), f).total(m)\n// expect: 0.222222\n"
  },
  {
    "path": "test/functionals/areaintegral/cg2fieldgradient.morpho",
    "content": "// Calculate field gradient for a field with higher grades\n\nimport meshtools \nimport \"../numericalderivatives.morpho\"\n\nvar mb=MeshBuilder()\nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\nvar m = mb.build() \nm.addgrade(1)\n\nvar f = Field(m, fn (x,y) Matrix([x^2,y^2]), finiteelementspace=FiniteElementSpace(\"CG2\", grade=2))\n\nfn elasticity(x, f) {\n    var g = grad(f)\n    return g[0].inner(g[0])+g[1].inner(g[1]) \n}\n\nvar lel = AreaIntegral(elasticity, f, method={})\n\nprint (lel.fieldgradient(m, f) - numericalfieldgradient(lel, m, f)).__linearize().norm() < 1e-6\n// expect: true\n"
  },
  {
    "path": "test/functionals/areaintegral/cgtensor.morpho",
    "content": "// Cauchy-Green tensor in integrals \n\nimport constants\nimport meshtools\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\n\nvar m = mb.build() \n\nvar mref = m.clone() \n\nm.setvertexmatrix(2*m.vertexmatrix())\n\nfn integrand(x) {\n    var cg = cgtensor()\n    return cg.trace()\n}\n\nvar a = AreaIntegral(integrand, reference=mref)\nprint a.total(m) // expect: 6\n\nvar b = AreaIntegral(integrand, reference=mref, weightByReference=true)\nprint b.total(m) // expect: 1.5\n\n// Ensure equivalence of LinearElasticity and AreaIntegral formulations\nvar nu = 0.3 \nvar mu = 1/2/(1+nu)\nvar lambda = nu/(1+nu)/(1-2*nu)\n\nfn elasticity(x) {\n    var cg = cgtensor()\n\n    var trCG=cg.trace()\n    var trCGCG = (cg * cg).trace() \n\n    return mu*trCGCG + lambda*trCG^2/2 \n}\n\nprint (LinearElasticity(mref).total(m) -\n       AreaIntegral(elasticity, reference=mref, weightByReference=true).total(m)) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/functionals/areaintegral/grad.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = AreaMesh(fn (x,y) [x,y,0], 0..1:0.5, 0..1:0.5)\n\nvar f = Field(m, fn (x,y,z) 2*x) \n\nprint AreaIntegral(fn (x, f) grad(f).norm(), f).total(m)\n// expect: 2\n\n\n"
  },
  {
    "path": "test/functionals/areaintegral/grad2.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = AreaMesh(fn (x,y) [x,y,0], 0..1:0.5, 0..1:0.5)\n\nvar f = Field(m, fn (x,y,z) x)\nvar g = Field(m, fn (x,y,z) 2*y)\nvar g2 = Field(m, fn (x,y,z) 2*y+0.001*x)\nvar h = Field(m, fn (x,y,z) x+4*y)\n\nfn integrand(x, n) {\n    var g = grad(n) \n    return g.inner(g)\n}\n\nprint AreaIntegral(integrand, f).total(m)\n// expect: 1\n\nprint AreaIntegral(fn (x, fl, gl) grad(g).norm(), f, g).total(m)\n// expect: 2\n\nprint AreaIntegral(fn (x, fl, gl) grad(f).inner(grad(g2)), f, g2).total(m)\n// expect: 0.001\n\nprint AreaIntegral(fn (x, g, h) grad(g2).inner(grad(h)), g2, h).total(m)\n// expect: 8.001\n\nprint AreaIntegral(fn (x, fl, gl) grad(fl).inner(grad(g)), f, g).total(m)\n// expect error 'IntgrlAmbgsFld'"
  },
  {
    "path": "test/functionals/areaintegral/gradvector.morpho",
    "content": "import constants\nimport meshtools\n\nvar mb = MeshBuilder()\nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\n\nvar m = mb.build()\n\nvar f = Field(m, fn (x,y) Matrix([x,2*y]))\n\nvar r = [ Matrix([[1],[0]]), Matrix([[0],[2]]) ]\nvar out = true\n\nfn integrand(x, n) {\n    var g = grad(n)\n    for (gg, k in g) if ((gg-r[k]).norm()>1e-4) out = false\n    return 0\n}\n\nprint AreaIntegral(integrand, f).total(m)\n// expect: 0\n\nprint out // expect: true\n"
  },
  {
    "path": "test/functionals/areaintegral/normal.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = AreaMesh(fn (x,y) [x,y,0], 0..1:0.5, 0..1:0.5)\n\nvar f = Field(m, fn (x,y,z) Matrix([0,0,1]))\n\nprint AreaIntegral(fn (x, n) n.inner(normal())^2 , f).total(m)\n// expect: 1\n"
  },
  {
    "path": "test/functionals/cube.mesh",
    "content": "vertices\n\n1 -0.5 -0.5 -0.5\n2 0.5 -0.5 -0.5\n3 -0.5 0.5 -0.5\n4 0.5 0.5 -0.5\n5 -0.5 -0.5 0.5\n6 0.5 -0.5 0.5\n7 -0.5 0.5 0.5\n8 0.5 0.5 0.5\n9 0 0 -0.5\n10 0 0 0.5\n11 0 -0.5 0\n12 0 0.5 0\n13 -0.5 0 0\n14 0.5 0 0\n\nedges\n\n1 1 2\n2 3 4\n3 1 3\n4 2 4\n5 5 6\n6 7 8\n7 5 7\n8 6 8\n9 1 5\n10 2 6\n11 3 7\n12 4 8\n13 9 1\n14 9 2\n15 9 4\n16 9 3\n17 10 5\n18 10 6\n19 10 8\n20 10 7\n21 11 1\n22 11 2\n23 11 6\n24 11 5\n25 12 3\n26 12 4\n27 12 8\n28 12 7\n29 13 1\n30 13 3\n31 13 7\n32 13 5\n33 14 2\n34 14 4\n35 14 8\n36 14 6\n\nfaces\n\n1 9 2 1\n2 9 4 2\n3 9 3 4\n4 9 1 3\n5 10 5 6\n6 10 6 8\n7 10 8 7\n8 10 7 5\n9 11 1 2\n10 11 2 6\n11 11 6 5\n12 11 5 1\n13 12 4 3\n14 12 8 4\n15 12 7 8\n16 12 3 7\n17 13 3 1\n18 13 7 3\n19 13 5 7\n20 13 1 5\n21 14 2 4\n22 14 4 8\n23 14 8 6\n24 14 6 2\n"
  },
  {
    "path": "test/functionals/cubeout.mesh",
    "content": "vertices\n\n1 -0.430974 -0.430974 -0.430974 \n2 0.430974 -0.430974 -0.430974 \n3 -0.430974 0.430974 -0.430974 \n4 0.430974 0.430974 -0.430974 \n5 -0.430974 -0.430974 0.430974 \n6 0.430974 -0.430974 0.430974 \n7 -0.430974 0.430974 0.430974 \n8 0.430974 0.430974 0.430974 \n9 1.99469e-17 -1.08442e-17 -0.672989 \n10 3.28745e-17 5.96567e-17 0.672989 \n11 3.67119e-18 -0.672989 2.74001e-17 \n12 -1.984e-17 0.672989 6.29326e-18 \n13 -0.672989 -3.10151e-17 -2.05836e-17 \n14 0.672989 2.60876e-17 1.15108e-17 \n\nedges\n\n1 1 2 \n2 3 4 \n3 1 3 \n4 2 4 \n5 5 6 \n6 7 8 \n7 5 7 \n8 6 8 \n9 1 5 \n10 2 6 \n11 3 7 \n12 4 8 \n13 1 9 \n14 2 9 \n15 4 9 \n16 3 9 \n17 5 10 \n18 6 10 \n19 8 10 \n20 7 10 \n21 1 11 \n22 2 11 \n23 6 11 \n24 5 11 \n25 3 12 \n26 4 12 \n27 8 12 \n28 7 12 \n29 1 13 \n30 3 13 \n31 7 13 \n32 5 13 \n33 2 14 \n34 4 14 \n35 8 14 \n36 6 14 \nfaces\n\n1 1 2 9 \n2 2 4 9 \n3 3 4 9 \n4 1 3 9 \n5 5 6 10 \n6 6 8 10 \n7 7 8 10 \n8 5 7 10 \n9 1 2 11 \n10 2 6 11 \n11 5 6 11 \n12 1 5 11 \n13 3 4 12 \n14 4 8 12 \n15 7 8 12 \n16 3 7 12 \n17 1 3 13 \n18 3 7 13 \n19 5 7 13 \n20 1 5 13 \n21 2 4 14 \n22 4 8 14 \n23 6 8 14 \n24 2 6 14 \n"
  },
  {
    "path": "test/functionals/equielement/equielement.morpho",
    "content": "// Test equielements\n\nimport meshtools\nimport optimize\n\nfn numericalgrad(func, m, eps=1e-8) {\n   var x=m.vertexmatrix()\n   var grad=x.clone()\n   var dim = x.dimensions()[0]\n\n   for (i in 0...m.count()) {\n      for (j in 0...dim) {\n         var temp = x[j,i]\n         x[j,i]=temp+eps\n         var fr=func.total(m)\n         x[j,i]=temp-eps\n         var fl=func.total(m)\n         x[j,i]=temp\n         grad[j,i]=(fr-fl)/(2*eps)\n      }\n   }\n\n   return grad\n} \n\nvar m = LineMesh(fn (t) [t, 0, 0], 0..1:0.5)\n\nvar le = EquiElement()\n\nvar v = m.vertexmatrix()\nv.setcolumn(1, Matrix([0.3, 0, 0]))\n\nprint le.integrand(m)\n// expect: [ 0 0.32 0 ]\n\nprint le.total(m)\n// expect: 0.32\n\nprint (le.gradient(m)-numericalgrad(le, m)).norm() < 1e-4\n// expect: true\n\nvar problem = OptimizationProblem(m)\nproblem.addenergy(le)\n\nvar opt=ShapeOptimizer(problem, m)\n\nopt.quiet=true\nopt.relax(20)\n\nprint abs(opt.totalenergy())<1e-8\n// expect: true\n"
  },
  {
    "path": "test/functionals/equielement/equielement_gradient.morpho",
    "content": "\nimport meshgen\nimport plot\nimport meshtools\n\nimport \"../numericalderivatives.morpho\"\n\n// Some random points\nvar pts = [ [ 0.118625, 0.966611 ],\n            [ 0.868882, 0.854463 ],\n            [ 0.237799, 0.284121 ],\n            [ 0.17586, 0.534928 ],\n            [ 0.864168, 0.820575 ],\n            [ 0.436567, 0.0629146 ],\n            [ 0.770178, 0.529049 ],\n            [ 0.554193, 0.844503 ],\n            [ 0.418181, 0.414727 ],\n            [ 0.652704, 0.682881 ] ]\n\nvar x = []\nfor (p in pts) x.append(Matrix(p))\n\nvar mesh = DelaunayMesh(x)\n//mesh.addgrade(1)\n\nvar leq = EquiElement()\nvar ngrad=numericalgradient(leq, mesh)\nvar grad=leq.gradient(mesh)\nprint (ngrad-grad).norm()<1e-4\n// expect: true\n"
  },
  {
    "path": "test/functionals/equielement/equielement_hessian.morpho",
    "content": "\nimport meshtools\nimport \"../numericalderivatives.morpho\"\n\n/*\nvar m = LineMesh(fn (t) [cos(t^2), sin(t^2)], 0...sqrt(2*Pi):sqrt(2*Pi)/10, closed=true)\n\nvar a = EquiElement()\n\nprint a.total(m)\n\nprint a.hessian(m)\n\nprint \"\"\nprint numericalhessian(a, m, eps=1e-3)\n*/"
  },
  {
    "path": "test/functionals/equielement/hex.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0.5 0.866025 0\n4 -0.5 0.866025 0\n5 -1 0 0\n6 -0.5 -0.866025 0\n7 0.5 -0.866025 0\n\nfaces\n\n1 1 2 3\n2 1 3 4\n3 1 4 5\n4 1 5 6\n5 1 6 7\n6 1 7 2\n"
  },
  {
    "path": "test/functionals/equielement/weight.morpho",
    "content": "// Test equielements with weights\n\nimport meshtools\nimport optimize\nimport plot\n\nvar m = Mesh(\"hex.mesh\")\nm.addgrade(1)\n\nm.setvertexposition(0, Matrix([0, 0.2, 0]))\n\nvar bnd = Selection(m, boundary=true)\nbnd.addgrade(0) // Boundary vertices\n\nvar w = Field(m, grade=2)\nfor (i in 0...w.count()) w[2,i]=1\n\nvar le = EquiElement(grade=2, weight=w)\nvar la = Area()\n\nvar problem = OptimizationProblem(m)\nproblem.addenergy(le)\n\nvar opt=ShapeOptimizer(problem, m)\nopt.fix(bnd)\n\nopt.quiet=true\nopt.relax(10)\n\nvar xc=m.vertexposition(0)\nprint xc.inner(xc)<1e-9\n// expect: true\n"
  },
  {
    "path": "test/functionals/err_functional_req_mesh.morpho",
    "content": "var m = Mesh(\"triangle.mesh\")\nvar a = Area()\n\nprint a.integrand()\n// expect error 'FnctlIntMsh'\n"
  },
  {
    "path": "test/functionals/err_integrand.morpho",
    "content": "import meshtools\nimport plot\nimport functionals\n\nvar L = 1.0\nvar dx = 0.1\n\nvar m = AreaMesh(fn (u, v) [u, v, 0], -L/2..L/2:dx, -L/2..L/2:dx)\nm.addgrade(1)\n\nfn mhdgrad(x,y,z,x0,y0,varphi){\nreturn Matrix([(y-y0)/((x-x0)^2+(y-y0)^2)/2,-(x-x0)/((x-x0)^2+(y-y0)^2)/2])\n}\nfn phdgrad(x,y,z,x0,y0,varphi){\nreturn Matrix([-(y-y0)/((x-x0)^2+(y-y0)^2)/2,(x-x0)/((x-x0)^2+(y-y0)^2)/2])\n}\nvar defectsGrad = Field(m, fn(x,y,z) phdgrad(x, y, z, 0.25, 0, 0)+mhdgrad(x, y, z, -0.25, 0, 0) )\n\nvar directorIntegral = LineIntegral(fn (x,n) 1/(2*Pi) * n.inner(tangent()), defectsGrad)\n\nvar fe = directorIntegral.integrand(m)\n//expect error 'MtrxIncmptbl'"
  },
  {
    "path": "test/functionals/gausscurvature/disk.mesh",
    "content": "vertices\n\n1 -1. 0. 0\n2 -0.951057 -0.309017 0\n3 -0.951057 0.309017 0\n4 -0.809017 -0.587785 0\n5 -0.809017 0.587785 0\n6 -0.587785 -0.809017 0\n7 -0.587785 0.809017 0\n8 -0.5 -0.5 0\n9 -0.5 0. 0\n10 -0.5 0.5 0\n11 -0.309017 -0.951057 0\n12 -0.309017 0.951057 0\n13 0. -1. 0\n14 0. -0.5 0\n15 0. 0. 0\n16 0. 0.5 0\n17 0. 1. 0\n18 0.309017 -0.951057 0\n19 0.309017 0.951057 0\n20 0.5 -0.5 0\n21 0.5 0. 0\n22 0.5 0.5 0\n23 0.587785 -0.809017 0\n24 0.587785 0.809017 0\n25 0.809017 -0.587785 0\n26 0.809017 0.587785 0\n27 0.951057 -0.309017 0\n28 0.951057 0.309017 0\n29 1. 0. 0\n\nedges\n\n1 8 2\n2 2 4\n3 4 8\n4 4 6\n5 6 8\n6 11 13\n7 13 14\n8 14 11\n9 8 9\n10 9 2\n11 14 8\n12 8 11\n13 13 18\n14 18 14\n15 6 11\n16 14 9\n17 3 10\n18 10 5\n19 5 3\n20 9 3\n21 3 1\n22 1 9\n23 10 7\n24 7 5\n25 10 9\n26 9 15\n27 15 10\n28 12 7\n29 10 12\n30 10 16\n31 16 12\n32 1 2\n33 14 15\n34 14 20\n35 20 15\n36 18 20\n37 18 23\n38 23 20\n39 20 21\n40 21 15\n41 27 20\n42 20 25\n43 25 27\n44 21 27\n45 27 29\n46 29 21\n47 23 25\n48 21 22\n49 22 15\n50 16 22\n51 22 19\n52 19 16\n53 16 15\n54 19 17\n55 17 16\n56 22 24\n57 24 19\n58 17 12\n59 26 22\n60 22 28\n61 28 26\n62 26 24\n63 21 28\n64 29 28\n\nfaces\n\n1 8 2 4\n2 8 4 6\n3 11 13 14\n4 2 8 9\n5 14 8 11\n6 13 18 14\n7 11 8 6\n8 9 8 14\n9 3 10 5\n10 9 3 1\n11 10 7 5\n12 10 9 15\n13 12 7 10\n14 10 16 12\n15 10 3 9\n16 1 2 9\n17 15 9 14\n18 14 20 15\n19 20 14 18\n20 20 18 23\n21 15 20 21\n22 27 20 25\n23 21 27 29\n24 21 20 27\n25 25 20 23\n26 15 21 22\n27 16 22 19\n28 16 15 22\n29 19 17 16\n30 22 24 19\n31 16 17 12\n32 26 22 28\n33 24 22 26\n34 22 21 28\n35 29 28 21\n36 15 16 10\n"
  },
  {
    "path": "test/functionals/gausscurvature/gausscurvature.morpho",
    "content": "// Gaussian curvature \nimport constants\nimport implicitmesh\nimport plot\n\n// Make a sphere\nvar impl = ImplicitMeshBuilder(fn (x,y,z) x^2+y^2+z^2-1)\nvar mesh = impl.build(stepsize=0.25)\n\n// Gauss curvature\nvar lgc = GaussCurvature()\n\nprint abs(lgc.total(mesh)-4*Pi)<1e-6\n// expect: true\n"
  },
  {
    "path": "test/functionals/gausscurvature/geodesiccurvature.morpho",
    "content": "// Geodesic curvature \nimport constants\nimport meshtools \nimport meshgen \nimport implicitmesh\nimport plot\n\n// Computes the euler characteristic of a mesh\nfn euler(mesh) {\n    return mesh.count(0) - mesh.count(1) + mesh.count(2)\n}\n\n// For surfaces with zero Gaussian curvature, the geodesic curvature integral will be equal to 2Pi * Euler characteristic of the surface.\nfn testzerogaussmesh(mesh, lgc) {\n    mesh.addgrade(1)\n    var bnd = Selection(mesh, boundary=true)\n\n    print abs(lgc.total(mesh, bnd)-2*Pi*euler(mesh))<1e-6\n}\n\nvar lgc = GaussCurvature()\nlgc.geodesic = true\n\n// Disk mesh (Euler characteristic = 1)\nvar mesh = Mesh(\"disk.mesh\")\ntestzerogaussmesh(mesh, lgc)\n// expect: true\n\n// Tube mesh (Euler characteristic = 0)\n\nvar mesh = AreaMesh(fn (u, v) [v, cos(u), sin(u)], -Pi...Pi:Pi/4,-1..1:0.1, closed=[true, false])\ntestzerogaussmesh(mesh, lgc)\n// expect: true\n\n// Half-slice of a sphere\n\nfn disktohemisphere(mesh) {\n\n    var vert = mesh.vertexmatrix()\n    var hemisphere = mesh.clone()\n\n    for (i in 0...mesh.count()) {\n        var pos = vert.column(i)\n        var x = pos[0]\n        var y = pos[1]\n        var z2 = 1 - x^2 - y^2\n        var z \n        if (z2<0) z = 0\n        else z = sqrt(z2)\n        var posnew = Matrix([x, y, z])\n        \n        hemisphere.setvertexposition(i, posnew)\n    }\n\n    return hemisphere\n}\n\n// Create an approximate hemisphere\nvar mesh = Mesh(\"disk.mesh\")\nmesh = refinemesh(mesh)\nmesh.addgrade(1)\nvar hemisphere = disktohemisphere(mesh)\nhemisphere.addgrade(1)\nvar bnd = Selection(hemisphere, boundary=true)\n\nvar whole = Selection(hemisphere, fn(x,y,z) true)\nvar interior = whole.difference(bnd)\n\n// The hemisphere is an approximate one, so the value is off, but the gauss integral and the geodesic integral add up to 2Pi.\nvar g1 = GaussCurvature().total(hemisphere, interior)\n\nvar g2 = lgc.total(hemisphere, bnd)\nvar lhs = g1+g2\nprint abs(lhs-2*Pi*euler(hemisphere))<1e-6 \n// expect: true\n"
  },
  {
    "path": "test/functionals/gausscurvature/gradient.morpho",
    "content": "// Gaussian curvature gradient\nimport constants\nimport implicitmesh\nimport plot\n\n// Make a sphere\nvar impl = ImplicitMeshBuilder(fn (x,y,z) x^2+y^2+z^2-1)\nvar m = impl.build(stepsize=0.25)\n\n// Mean Squared curvature\nvar lc = GaussCurvature()\n\nvar grad = lc.gradient(m)\n\nvar dim = grad.dimensions()\nvar ngrad = Matrix(dim[0], dim[1])\n\n// Manually calculate the gradient\nvar vert = m.vertexmatrix()\nvar eps = 1e-8\n\nfor (i in 0...dim[0]) {\n  for (j in 0...dim[1]) {\n    var v = vert[i, j]\n    vert[i, j] = v + eps\n    var fp = lc.total(m)\n    vert[i, j] = v - eps\n    var fm = lc.total(m)\n    ngrad[i,j] = (fp-fm)/(2*eps)\n  }\n}\n\nprint (grad-ngrad).norm()/grad.count() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/gausscurvature/symm.morpho",
    "content": "// Gaussian curvature with symmetry\nimport constants\nimport implicitmesh\nimport plot\nimport symmetry\n\nvar L = 2\n\nvar m = AreaMesh(fn (t, x) [x, cos(t), sin(t)], 0...2*Pi:2*Pi/40, -L..L:0.125, closed=[true,false])\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(x-L)<1e-10 || abs(x+L)<1e-10)\n\nvar t = Translate(Matrix([2*L,0,0]))\nm.addsymmetry(t, s)\n\n// Mean Squared curvature\nvar lmc = GaussCurvature()\n\nprint abs(lmc.total(m))<1e-6\n// expect: true\n"
  },
  {
    "path": "test/functionals/gausscurvature/torus.morpho",
    "content": "// Gaussian curvature with torus\nimport constants\nimport implicitmesh\nimport plot\n\n// Make a sphere\n// Torus\nvar r=1\nvar a=0.35\n\nvar impl = ImplicitMeshBuilder(fn (x,y,z) (x^2+y^2+z^2+r^2-a^2)^2 - 4*r^2*(x^2+y^2))\nvar mesh = impl.build(start=Matrix([1,0,0.5]), stepsize=0.25, maxiterations=400)\n\n// Mean Squared curvature\nvar lmc = GaussCurvature()\n\nprint abs(lmc.total(mesh))<1e-6\n// expect: true\n"
  },
  {
    "path": "test/functionals/gradsq/disk.mesh",
    "content": "vertices\n\n1 -1 0 0 \n2 -0.951057 -0.309017 0 \n3 -0.951057 0.309017 0 \n4 -0.809017 -0.587785 0 \n5 -0.809017 0.587785 0 \n6 -0.587785 -0.809017 0 \n7 -0.587785 0.809017 0 \n8 -0.5 -0.5 0 \n9 -0.5 0 0 \n10 -0.5 0.5 0 \n11 -0.309017 -0.951057 0 \n12 -0.309017 0.951057 0 \n13 0 -1 0 \n14 0 -0.5 0 \n15 0 0 0 \n16 0 0.5 0 \n17 0 1 0 \n18 0.309017 -0.951057 0 \n19 0.309017 0.951057 0 \n20 0.5 -0.5 0 \n21 0.5 0 0 \n22 0.5 0.5 0 \n23 0.587785 -0.809017 0 \n24 0.587785 0.809017 0 \n25 0.809017 -0.587785 0 \n26 0.809017 0.587785 0 \n27 0.951057 -0.309017 0 \n28 0.951057 0.309017 0 \n29 1 0 0 \n30 -0.25 0.5 0 \n31 0.725529 0.404508 0 \n32 -0.725529 -0.404508 0 \n33 -0.154508 0.725529 0 \n34 0.891007 0.45399 0 \n35 -0.891007 -0.45399 0 \n36 -0.987688 -0.156434 0 \n37 0.707107 0.707107 0 \n38 -0.654508 -0.543893 0 \n39 0 -0.25 0 \n40 0.725529 0.154508 0 \n41 -0.707107 -0.707107 0 \n42 0.25 -0.5 0 \n43 0.987688 0.156434 0 \n44 -0.543893 -0.654508 0 \n45 0.25 -0.25 0 \n46 -0.156434 -0.987688 0 \n47 0.404508 -0.725529 0 \n48 0 -0.75 0 \n49 0.45399 -0.891007 0 \n50 -0.154508 -0.725529 0 \n51 0.543893 -0.654508 0 \n52 -0.5 -0.25 0 \n53 0.5 -0.25 0 \n54 -0.725529 -0.154508 0 \n55 0.25 0 0 \n56 -0.25 -0.5 0 \n57 0.725529 -0.404508 0 \n58 -0.404508 -0.725529 0 \n59 0.654508 -0.543893 0 \n60 0.156434 -0.987688 0 \n61 0.891007 -0.45399 0 \n62 0.154508 -0.725529 0 \n63 0.725529 -0.154508 0 \n64 -0.45399 -0.891007 0 \n65 0.987688 -0.156434 0 \n66 -0.25 -0.25 0 \n67 0.75 0 0 \n68 -0.725529 0.404508 0 \n69 0.707107 -0.707107 0 \n70 -0.654508 0.543893 0 \n71 0.5 0.25 0 \n72 -0.891007 0.45399 0 \n73 0.25 0.25 0 \n74 -0.725529 0.154508 0 \n75 0.25 0.5 0 \n76 -0.987688 0.156434 0 \n77 0.404508 0.725529 0 \n78 -0.75 0 0 \n79 0.154508 0.725529 0 \n80 -0.543893 0.654508 0 \n81 0 0.25 0 \n82 -0.707107 0.707107 0 \n83 0.156434 0.987688 0 \n84 -0.5 0.25 0 \n85 0 0.75 0 \n86 -0.25 0 0 \n87 0.543893 0.654508 0 \n88 -0.25 0.25 0 \n89 0.45399 0.891007 0 \n90 -0.45399 0.891007 0 \n91 -0.156434 0.987688 0 \n92 -0.404508 0.725529 0 \n93 0.654508 0.543893 0 \n94 0.375 -0.375 0 \n95 0.233919 -0.972256 0 \n96 -0.972256 0.233919 0 \n97 -0.233919 0.972256 0 \n98 0.612764 0.202254 0 \n99 -0.496147 0.767273 0 \n100 0.862764 -0.0772542 0 \n101 -0.375 0.5 0 \n102 0.125 -0.125 0 \n103 0.852385 -0.522914 0 \n104 -0.996955 0.0779731 0 \n105 -0.452254 0.612764 0 \n106 -0.327254 -0.612764 0 \n107 -0.426455 0.802783 0 \n108 0.737764 -0.0772542 0 \n109 -0.125 0.5 0 \n110 -0.233919 -0.972256 0 \n111 0.924066 -0.382233 0 \n112 0.452254 0.612764 0 \n113 -0.356763 0.838293 0 \n114 -0.279508 -0.725529 0 \n115 -0.474201 0.690018 0 \n116 0.612764 -0.202254 0 \n117 0.612764 0.452254 0 \n118 -0.0779731 -0.996955 0 \n119 0.231763 -0.838293 0 \n120 0.356763 0.838293 0 \n121 0.731763 0.565839 0 \n122 -0.202254 -0.612764 0 \n123 -0.202254 0.612764 0 \n124 0.725529 -0.279508 0 \n125 0.838293 0.356763 0 \n126 0.356763 -0.838293 0 \n127 0.0772542 -0.612764 0 \n128 -0.875 0 0 \n129 0.577254 0.521946 0 \n130 0.850529 0.154508 0 \n131 -0.279508 0.725529 0 \n132 0.612764 -0.327254 0 \n133 -0.612764 -0.452254 0 \n134 0.452254 -0.612764 0 \n135 0.612764 -0.0772542 0 \n136 -0.625 0 0 \n137 0.496147 0.767273 0 \n138 0.862764 0.0772542 0 \n139 -0.327254 0.612764 0 \n140 0.676455 -0.621147 0 \n141 -0.838293 -0.356763 0 \n142 0 -0.875 0 \n143 0.838293 -0.231763 0 \n144 0.231763 0.838293 0 \n145 0.426455 0.802783 0 \n146 0.737764 0.0772542 0 \n147 -0.612764 0.327254 0 \n148 0.621147 -0.676455 0 \n149 -0.0772542 0.612764 0 \n150 0 -0.625 0 \n151 -0.522914 -0.852385 0 \n152 0.0772542 0.612764 0 \n153 0.474201 0.690018 0 \n154 0.154508 -0.850529 0 \n155 -0.612764 0.202254 0 \n156 0.599201 -0.599201 0 \n157 -0.231763 0.838293 0 \n158 0.382233 -0.924066 0 \n159 -0.382233 -0.924066 0 \n160 -0.521946 0.577254 0 \n161 -0.802783 -0.426455 0 \n162 0.0772542 -0.737764 0 \n163 -0.725529 0.279508 0 \n164 0.375 0.25 0 \n165 0.924066 0.382233 0 \n166 0.522914 -0.852385 0 \n167 0.972256 -0.233919 0 \n168 -0.565839 0.731763 0 \n169 -0.767273 -0.496147 0 \n170 0.0772542 -0.862764 0 \n171 -0.850529 -0.154508 0 \n172 0.25 0.125 0 \n173 0.852385 0.522914 0 \n174 -0.0772542 -0.612764 0 \n175 0.996955 -0.0779731 0 \n176 0 0.375 0 \n177 -0.690018 -0.474201 0 \n178 -0.125 0.25 0 \n179 -0.862764 -0.0772542 0 \n180 0.375 0.125 0 \n181 -0.924066 -0.382233 0 \n182 -0.231763 -0.838293 0 \n183 -0.125 -0.375 0 \n184 0 0.125 0 \n185 -0.154508 0.850529 0 \n186 -0.25 0.375 0 \n187 -0.737764 -0.0772542 0 \n188 0.327254 0.612764 0 \n189 -0.852385 -0.522914 0 \n190 0.565839 -0.731763 0 \n191 -0.375 -0.125 0 \n192 -0.649077 0.760723 0 \n193 -0.0772542 0.737764 0 \n194 -0.125 0.375 0 \n195 -0.125 -0.125 0 \n196 0.279508 0.725529 0 \n197 -0.996955 -0.0779731 0 \n198 0.521946 -0.577254 0 \n199 0.875 0 0 \n200 -0.760723 0.649077 0 \n201 -0.0772542 0.862764 0 \n202 -0.426455 -0.802783 0 \n203 -0.125 -0.25 0 \n204 0.202254 0.612764 0 \n205 -0.972256 -0.233919 0 \n206 -0.5 -0.375 0 \n207 0.625 0 0 \n208 0.233919 0.972256 0 \n209 -0.621147 -0.676455 0 \n210 -0.496147 -0.767273 0 \n211 -0.25 -0.125 0 \n212 0.125 0.25 0 \n213 0.760723 0.649077 0 \n214 -0.5 -0.125 0 \n215 -0.838293 0.356763 0 \n216 0.0779731 0.996955 0 \n217 -0.599201 -0.599201 0 \n218 -0.474201 -0.690018 0 \n219 0.25 -0.375 0 \n220 0.125 0.375 0 \n221 0.649077 0.760723 0 \n222 0.5 -0.375 0 \n223 -0.612764 0.452254 0 \n224 -0.5 0.375 0 \n225 -0.676455 -0.621147 0 \n226 -0.375 -0.25 0 \n227 0.125 -0.25 0 \n228 0.25 0.375 0 \n229 -0.731763 -0.565839 0 \n230 0.5 -0.125 0 \n231 0.649077 -0.760723 0 \n232 -0.5 0.125 0 \n233 0.690018 0.474201 0 \n234 -0.25 -0.375 0 \n235 0.125 -0.375 0 \n236 0.0772542 0.862764 0 \n237 -0.577254 -0.521946 0 \n238 -0.612764 -0.0772542 0 \n239 0.760723 -0.649077 0 \n240 0 0.875 0 \n241 0.802783 0.426455 0 \n242 -0.375 -0.375 0 \n243 0.327254 -0.612764 0 \n244 0.0772542 0.737764 0 \n245 0 -0.375 0 \n246 -0.838293 -0.231763 0 \n247 -0.577254 0.521946 0 \n248 0 0.625 0 \n249 0.767273 0.496147 0 \n250 -0.690018 0.474201 0 \n251 0.279508 -0.725529 0 \n252 0.154508 0.850529 0 \n253 0 -0.125 0 \n254 0.375 0 0 \n255 -0.731763 0.565839 0 \n256 -0.375 0 0 \n257 -0.0772542 -0.862764 0 \n258 -0.767273 0.496147 0 \n259 0.202254 -0.612764 0 \n260 0.612764 0.0772542 0 \n261 0.125 0 0 \n262 0.5 0.125 0 \n263 -0.125 0 0 \n264 -0.0772542 -0.737764 0 \n265 -0.802783 0.426455 0 \n266 0.496147 -0.767273 0 \n267 0.838293 0.231763 0 \n268 -0.125 -0.5 0 \n269 0.5 0.375 0 \n270 0.521946 0.577254 0 \n271 -0.154508 -0.850529 0 \n272 -0.850529 0.154508 0 \n273 0.474201 -0.690018 0 \n274 -0.760723 -0.649077 0 \n275 -0.375 -0.5 0 \n276 -0.852385 0.522914 0 \n277 0.565839 0.731763 0 \n278 0.621147 0.676455 0 \n279 -0.862764 0.0772542 0 \n280 0.426455 -0.802783 0 \n281 -0.649077 -0.760723 0 \n282 0.838293 -0.356763 0 \n283 -0.924066 0.382233 0 \n284 -0.125 0.125 0 \n285 0.676455 0.621147 0 \n286 -0.737764 0.0772542 0 \n287 0.375 -0.125 0 \n288 0.125 -0.5 0 \n289 0.612764 -0.452254 0 \n290 0.375 0.375 0 \n291 -0.375 0.375 0 \n292 0.599201 0.599201 0 \n293 -0.621147 0.676455 0 \n294 0.25 -0.125 0 \n295 0.375 -0.5 0 \n296 -0.452254 -0.612764 0 \n297 0.125 0.125 0 \n298 0.522914 0.852385 0 \n299 -0.612764 -0.202254 0 \n300 -0.676455 0.621147 0 \n301 0.375 -0.25 0 \n302 0.996955 0.0779731 0 \n303 -0.356763 -0.838293 0 \n304 -0.612764 0.0772542 0 \n305 0.382233 0.924066 0 \n306 -0.725529 -0.279508 0 \n307 -0.599201 0.599201 0 \n308 0.690018 -0.474201 0 \n309 0.972256 0.233919 0 \n310 0.577254 -0.521946 0 \n311 -0.838293 0.231763 0 \n312 -0.382233 0.924066 0 \n313 -0.612764 -0.327254 0 \n314 -0.375 0.125 0 \n315 0.767273 -0.496147 0 \n316 -0.565839 -0.731763 0 \n317 0.731763 -0.565839 0 \n318 0.125 0.5 0 \n319 -0.522914 0.852385 0 \n320 0.725529 0.279508 0 \n321 -0.25 0.125 0 \n322 0.802783 -0.426455 0 \n323 -0.521946 -0.577254 0 \n324 0.0779731 -0.996955 0 \n325 0.375 0.5 0 \n326 -0.0779731 0.996955 0 \n327 0.612764 0.327254 0 \n328 -0.375 0.25 0 \n329 0.850529 -0.154508 0 \n\nedges\n\n1 295 222 \n2 219 301 \n3 60 95 \n4 95 18 \n5 3 96 \n6 96 76 \n7 91 97 \n8 97 12 \n9 71 98 \n10 98 40 \n11 168 115 \n12 319 107 \n13 65 100 \n14 100 67 \n15 10 101 \n16 101 30 \n17 227 294 \n18 253 261 \n19 25 103 \n20 103 61 \n21 76 104 \n22 104 1 \n23 10 105 \n24 105 92 \n25 56 106 \n26 106 58 \n27 90 107 \n28 107 92 \n29 67 108 \n30 108 63 \n31 30 109 \n32 109 16 \n33 11 110 \n34 110 46 \n35 61 111 \n36 111 27 \n37 22 112 \n38 112 77 \n39 92 113 \n40 113 12 \n41 58 114 \n42 114 50 \n43 92 115 \n44 115 80 \n45 53 116 \n46 116 63 \n47 22 117 \n48 117 31 \n49 46 118 \n50 118 13 \n51 18 119 \n52 119 62 \n53 77 120 \n54 120 19 \n55 26 121 \n56 121 93 \n57 50 122 \n58 122 56 \n59 30 123 \n60 123 33 \n61 63 124 \n62 124 57 \n63 31 125 \n64 125 28 \n65 18 126 \n66 126 47 \n67 62 127 \n68 127 14 \n69 1 128 \n70 128 78 \n71 93 129 \n72 129 22 \n73 40 130 \n74 130 43 \n75 33 131 \n76 131 92 \n77 57 132 \n78 132 53 \n79 8 133 \n80 133 32 \n81 47 134 \n82 134 20 \n83 21 135 \n84 135 63 \n85 78 136 \n86 136 9 \n87 277 153 \n88 298 145 \n89 43 138 \n90 138 67 \n91 92 139 \n92 139 30 \n93 59 140 \n94 140 69 \n95 32 141 \n96 141 2 \n97 13 142 \n98 142 48 \n99 63 143 \n100 143 27 \n101 19 144 \n102 144 79 \n103 89 145 \n104 145 77 \n105 67 146 \n106 146 40 \n107 68 147 \n108 147 84 \n109 69 148 \n110 148 51 \n111 16 149 \n112 149 33 \n113 48 150 \n114 150 14 \n115 6 151 \n116 151 64 \n117 79 152 \n118 152 16 \n119 77 153 \n120 153 87 \n121 60 154 \n122 154 62 \n123 84 155 \n124 155 74 \n125 51 156 \n126 156 59 \n127 33 157 \n128 157 12 \n129 18 158 \n130 158 49 \n131 64 159 \n132 159 11 \n133 10 160 \n134 160 80 \n135 32 161 \n136 161 35 \n137 62 162 \n138 162 48 \n139 74 163 \n140 163 68 \n141 71 164 \n142 164 73 \n143 28 165 \n144 165 34 \n145 49 166 \n146 166 23 \n147 27 167 \n148 167 65 \n149 80 168 \n150 168 7 \n151 161 189 \n152 229 177 \n153 48 170 \n154 170 60 \n155 54 171 \n156 171 36 \n157 73 172 \n158 172 55 \n159 34 173 \n160 173 26 \n161 14 174 \n162 174 50 \n163 65 175 \n164 175 29 \n165 16 176 \n166 176 81 \n167 38 177 \n168 177 32 \n169 81 178 \n170 178 88 \n171 36 179 \n172 179 78 \n173 172 254 \n174 262 164 \n175 2 181 \n176 181 35 \n177 50 182 \n178 182 11 \n179 245 268 \n180 203 234 \n181 81 184 \n182 184 15 \n183 91 185 \n184 185 33 \n185 88 186 \n186 186 30 \n187 78 187 \n188 187 54 \n189 75 188 \n190 188 77 \n191 35 189 \n192 189 4 \n193 23 190 \n194 190 51 \n195 211 226 \n196 256 214 \n197 7 192 \n198 192 82 \n199 33 193 \n200 193 85 \n201 109 186 \n202 176 178 \n203 211 263 \n204 203 253 \n205 77 196 \n206 196 79 \n207 1 197 \n208 197 36 \n209 51 198 \n210 198 20 \n211 29 199 \n212 199 67 \n213 82 200 \n214 200 5 \n215 85 201 \n216 201 91 \n217 58 202 \n218 202 64 \n219 39 203 \n220 203 66 \n221 79 204 \n222 204 75 \n223 36 205 \n224 205 2 \n225 8 206 \n226 206 52 \n227 67 207 \n228 207 21 \n229 19 208 \n230 208 83 \n231 41 209 \n232 209 44 \n233 151 202 \n234 316 218 \n235 66 211 \n236 211 86 \n237 73 212 \n238 212 81 \n239 26 213 \n240 213 37 \n241 52 214 \n242 214 9 \n243 3 215 \n244 215 68 \n245 83 216 \n246 216 17 \n247 44 217 \n248 217 38 \n249 44 218 \n250 218 58 \n251 42 219 \n252 219 45 \n253 176 212 \n254 318 228 \n255 37 221 \n256 221 24 \n257 20 222 \n258 222 53 \n259 68 223 \n260 223 10 \n261 10 224 \n262 224 84 \n263 38 225 \n264 225 41 \n265 52 226 \n266 226 66 \n267 45 227 \n268 227 39 \n269 75 228 \n270 228 73 \n271 4 229 \n272 229 38 \n273 53 230 \n274 230 21 \n275 23 231 \n276 231 69 \n277 84 232 \n278 232 9 \n279 93 233 \n280 233 31 \n281 66 234 \n282 234 56 \n283 227 245 \n284 288 219 \n285 83 236 \n286 236 85 \n287 38 237 \n288 237 8 \n289 9 238 \n290 238 54 \n291 69 239 \n292 239 25 \n293 17 240 \n294 240 85 \n295 31 241 \n296 241 34 \n297 234 275 \n298 206 226 \n299 42 243 \n300 243 47 \n301 85 244 \n302 244 79 \n303 14 245 \n304 245 39 \n305 54 246 \n306 246 2 \n307 10 247 \n308 247 70 \n309 85 248 \n310 248 16 \n311 173 241 \n312 121 233 \n313 68 250 \n314 250 70 \n315 47 251 \n316 251 62 \n317 79 252 \n318 252 83 \n319 39 253 \n320 253 15 \n321 21 254 \n322 254 55 \n323 70 255 \n324 255 5 \n325 9 256 \n326 256 86 \n327 46 257 \n328 257 48 \n329 250 255 \n330 276 265 \n331 62 259 \n332 259 42 \n333 21 260 \n334 260 40 \n335 55 261 \n336 261 15 \n337 21 262 \n338 262 71 \n339 86 263 \n340 263 15 \n341 48 264 \n342 264 50 \n343 72 265 \n344 265 68 \n345 166 280 \n346 190 273 \n347 40 267 \n348 267 28 \n349 14 268 \n350 268 56 \n351 71 269 \n352 269 22 \n353 22 270 \n354 270 87 \n355 50 271 \n356 271 46 \n357 74 272 \n358 272 76 \n359 51 273 \n360 273 47 \n361 4 274 \n362 274 41 \n363 56 275 \n364 275 8 \n365 5 276 \n366 276 72 \n367 87 277 \n368 277 24 \n369 87 278 \n370 278 37 \n371 76 279 \n372 279 78 \n373 47 280 \n374 280 49 \n375 41 281 \n376 281 6 \n377 27 282 \n378 282 57 \n379 72 283 \n380 283 3 \n381 184 263 \n382 178 321 \n383 37 285 \n384 285 93 \n385 78 286 \n386 286 74 \n387 230 301 \n388 254 294 \n389 14 288 \n390 288 42 \n391 57 289 \n392 289 20 \n393 269 325 \n394 164 228 \n395 186 328 \n396 101 224 \n397 93 292 \n398 292 87 \n399 80 293 \n400 293 82 \n401 55 294 \n402 294 45 \n403 42 295 \n404 295 20 \n405 8 296 \n406 296 58 \n407 172 212 \n408 261 184 \n409 24 298 \n410 298 89 \n411 52 299 \n412 299 54 \n413 82 300 \n414 300 70 \n415 45 301 \n416 301 53 \n417 29 302 \n418 302 43 \n419 58 303 \n420 303 11 \n421 9 304 \n422 304 74 \n423 89 305 \n424 305 19 \n425 54 306 \n426 306 32 \n427 70 307 \n428 307 80 \n429 57 308 \n430 308 59 \n431 43 309 \n432 309 28 \n433 20 310 \n434 310 59 \n435 74 311 \n436 311 3 \n437 12 312 \n438 312 90 \n439 32 313 \n440 313 52 \n441 232 328 \n442 256 321 \n443 308 317 \n444 103 322 \n445 6 316 \n446 316 44 \n447 59 317 \n448 317 25 \n449 16 318 \n450 318 75 \n451 90 319 \n452 319 7 \n453 40 320 \n454 320 31 \n455 86 321 \n456 321 88 \n457 61 322 \n458 322 57 \n459 44 323 \n460 323 8 \n461 13 324 \n462 324 60 \n463 75 325 \n464 325 22 \n465 17 326 \n466 326 91 \n467 31 327 \n468 327 71 \n469 88 328 \n470 328 84 \n471 63 329 \n472 329 65 \n473 39 183 \n474 203 183 \n475 183 245 \n476 24 137 \n477 298 137 \n478 137 277 \n479 86 191 \n480 211 191 \n481 191 256 \n482 120 305 \n483 89 120 \n484 145 120 \n485 66 195 \n486 211 195 \n487 195 203 \n488 87 112 \n489 153 112 \n490 112 270 \n491 295 94 \n492 94 219 \n493 42 94 \n494 137 145 \n495 77 137 \n496 153 137 \n497 253 102 \n498 102 227 \n499 39 102 \n500 141 181 \n501 35 141 \n502 161 141 \n503 288 235 \n504 235 245 \n505 14 235 \n506 4 169 \n507 229 169 \n508 169 189 \n509 45 235 \n510 227 235 \n511 235 219 \n512 237 133 \n513 133 177 \n514 38 133 \n515 295 134 \n516 134 243 \n517 243 295 \n518 161 169 \n519 169 177 \n520 32 169 \n521 126 251 \n522 251 119 \n523 119 126 \n524 157 97 \n525 97 185 \n526 185 157 \n527 288 259 \n528 259 127 \n529 127 288 \n530 149 193 \n531 193 248 \n532 248 149 \n533 251 259 \n534 259 243 \n535 243 251 \n536 326 201 \n537 201 240 \n538 240 326 \n539 23 266 \n540 190 266 \n541 266 166 \n542 185 193 \n543 193 201 \n544 201 185 \n545 134 198 \n546 51 134 \n547 273 134 \n548 281 316 \n549 316 209 \n550 209 281 \n551 49 126 \n552 280 126 \n553 126 158 \n554 237 323 \n555 323 217 \n556 217 237 \n557 266 273 \n558 47 266 \n559 280 266 \n560 274 225 \n561 225 229 \n562 229 274 \n563 21 287 \n564 254 287 \n565 287 230 \n566 209 217 \n567 217 225 \n568 225 209 \n569 102 261 \n570 55 102 \n571 294 102 \n572 129 117 \n573 117 233 \n574 93 117 \n575 53 94 \n576 301 94 \n577 94 222 \n578 125 165 \n579 34 125 \n580 241 125 \n581 287 294 \n582 45 287 \n583 301 287 \n584 26 249 \n585 121 249 \n586 249 173 \n587 289 310 \n588 59 289 \n589 308 289 \n590 31 249 \n591 241 249 \n592 249 233 \n593 25 315 \n594 103 315 \n595 315 317 \n596 118 142 \n597 142 257 \n598 257 118 \n599 111 282 \n600 282 322 \n601 61 282 \n602 150 174 \n603 174 264 \n604 264 150 \n605 308 315 \n606 315 322 \n607 57 315 \n608 182 110 \n609 110 271 \n610 271 182 \n611 143 167 \n612 167 329 \n613 329 143 \n614 257 264 \n615 264 271 \n616 271 257 \n617 175 199 \n618 199 100 \n619 100 175 \n620 277 221 \n621 221 278 \n622 278 277 \n623 207 135 \n624 135 108 \n625 108 207 \n626 213 285 \n627 285 121 \n628 121 213 \n629 329 100 \n630 100 108 \n631 108 329 \n632 270 292 \n633 292 129 \n634 129 270 \n635 135 116 \n636 116 230 \n637 230 135 \n638 285 292 \n639 292 278 \n640 278 285 \n641 282 124 \n642 124 143 \n643 143 282 \n644 214 238 \n645 238 299 \n646 299 214 \n647 222 132 \n648 132 289 \n649 289 222 \n650 141 246 \n651 246 306 \n652 306 141 \n653 124 132 \n654 132 116 \n655 116 124 \n656 206 313 \n657 313 133 \n658 133 206 \n659 317 239 \n660 239 140 \n661 140 317 \n662 299 306 \n663 306 313 \n664 313 299 \n665 231 148 \n666 148 190 \n667 190 231 \n668 125 267 \n669 267 320 \n670 320 125 \n671 310 156 \n672 156 198 \n673 198 310 \n674 117 327 \n675 327 269 \n676 269 117 \n677 148 156 \n678 156 140 \n679 140 148 \n680 260 98 \n681 98 262 \n682 262 260 \n683 269 290 \n684 290 164 \n685 71 290 \n686 320 327 \n687 327 98 \n688 98 320 \n689 261 297 \n690 297 172 \n691 55 297 \n692 275 296 \n693 296 106 \n694 106 275 \n695 262 180 \n696 180 254 \n697 21 180 \n698 182 303 \n699 303 114 \n700 114 182 \n701 73 180 \n702 172 180 \n703 180 164 \n704 268 122 \n705 122 174 \n706 174 268 \n707 325 112 \n708 112 188 \n709 188 325 \n710 106 114 \n711 114 122 \n712 122 106 \n713 120 144 \n714 144 196 \n715 196 120 \n716 267 309 \n717 309 130 \n718 130 267 \n719 152 318 \n720 318 204 \n721 204 152 \n722 302 138 \n723 138 199 \n724 199 302 \n725 188 196 \n726 196 204 \n727 204 188 \n728 260 146 \n729 146 207 \n730 207 260 \n731 297 184 \n732 81 297 \n733 212 297 \n734 138 146 \n735 146 130 \n736 130 138 \n737 176 220 \n738 220 318 \n739 16 220 \n740 95 119 \n741 119 154 \n742 154 95 \n743 290 228 \n744 75 290 \n745 325 290 \n746 150 127 \n747 127 162 \n748 162 150 \n749 220 228 \n750 73 220 \n751 212 220 \n752 324 170 \n753 170 142 \n754 142 324 \n755 216 240 \n756 240 236 \n757 236 216 \n758 154 162 \n759 162 170 \n760 170 154 \n761 152 248 \n762 248 244 \n763 244 152 \n764 284 178 \n765 81 284 \n766 184 284 \n767 208 252 \n768 252 144 \n769 144 208 \n770 30 291 \n771 186 291 \n772 291 101 \n773 236 244 \n774 244 252 \n775 252 236 \n776 176 194 \n777 194 109 \n778 16 194 \n779 186 194 \n780 194 178 \n781 88 194 \n782 303 159 \n783 64 303 \n784 202 303 \n785 151 210 \n786 210 316 \n787 6 210 \n788 296 218 \n789 44 296 \n790 323 296 \n791 210 218 \n792 58 210 \n793 202 210 \n794 214 191 \n795 191 226 \n796 52 191 \n797 183 234 \n798 56 183 \n799 268 183 \n800 206 242 \n801 242 275 \n802 8 242 \n803 234 242 \n804 242 226 \n805 66 242 \n806 223 247 \n807 70 223 \n808 250 223 \n809 5 258 \n810 276 258 \n811 258 255 \n812 283 215 \n813 215 265 \n814 72 215 \n815 250 258 \n816 258 265 \n817 68 258 \n818 311 96 \n819 96 272 \n820 272 311 \n821 104 128 \n822 128 279 \n823 279 104 \n824 136 304 \n825 304 286 \n826 286 136 \n827 272 279 \n828 279 286 \n829 286 272 \n830 168 192 \n831 192 293 \n832 293 168 \n833 255 200 \n834 200 300 \n835 300 255 \n836 160 307 \n837 307 247 \n838 247 160 \n839 293 300 \n840 300 307 \n841 307 293 \n842 9 314 \n843 256 314 \n844 314 232 \n845 263 284 \n846 284 321 \n847 86 284 \n848 291 224 \n849 84 291 \n850 328 291 \n851 314 321 \n852 88 314 \n853 328 314 \n854 7 99 \n855 319 99 \n856 99 168 \n857 113 312 \n858 90 113 \n859 107 113 \n860 105 115 \n861 80 105 \n862 160 105 \n863 92 99 \n864 115 99 \n865 99 107 \n866 109 149 \n867 149 123 \n868 123 109 \n869 113 157 \n870 157 131 \n871 131 113 \n872 101 139 \n873 139 105 \n874 105 101 \n875 123 131 \n876 131 139 \n877 139 123 \n878 224 147 \n879 147 223 \n880 223 224 \n881 304 155 \n882 155 232 \n883 232 304 \n884 215 163 \n885 163 311 \n886 311 215 \n887 155 163 \n888 163 147 \n889 147 155 \n890 246 205 \n891 205 171 \n892 171 246 \n893 197 179 \n894 179 128 \n895 128 197 \n896 238 187 \n897 187 136 \n898 136 238 \n899 179 187 \n900 187 171 \n901 171 179 \n902 15 195 \n903 253 195 \n904 195 263 \n\nfaces\n\n1 245 39 183 \n2 203 234 183 \n3 14 245 268 \n4 39 203 183 \n5 277 24 137 \n6 298 145 137 \n7 87 277 153 \n8 24 298 137 \n9 86 191 256 \n10 66 211 226 \n11 191 256 214 \n12 211 86 191 \n13 120 19 305 \n14 89 305 120 \n15 77 120 145 \n16 89 120 145 \n17 66 195 203 \n18 211 263 86 \n19 195 203 253 \n20 66 195 211 \n21 270 87 112 \n22 77 153 112 \n23 22 270 112 \n24 87 112 153 \n25 295 20 222 \n26 219 301 94 \n27 42 94 219 \n28 42 295 94 \n29 298 89 145 \n30 145 77 137 \n31 277 153 137 \n32 77 153 137 \n33 253 102 261 \n34 227 294 45 \n35 39 102 227 \n36 39 102 253 \n37 141 2 181 \n38 181 35 141 \n39 32 141 161 \n40 35 141 161 \n41 288 219 235 \n42 227 245 235 \n43 14 235 245 \n44 14 288 235 \n45 189 4 169 \n46 229 177 169 \n47 161 189 169 \n48 4 229 169 \n49 219 45 235 \n50 227 39 245 \n51 288 42 219 \n52 45 227 235 \n53 237 8 133 \n54 177 32 133 \n55 38 133 177 \n56 38 237 133 \n57 295 20 134 \n58 243 47 134 \n59 42 243 295 \n60 134 243 295 \n61 161 35 189 \n62 229 38 177 \n63 177 32 169 \n64 32 161 169 \n65 126 47 251 \n66 119 62 251 \n67 18 126 119 \n68 126 251 119 \n69 157 12 97 \n70 91 97 185 \n71 33 157 185 \n72 97 185 157 \n73 288 42 259 \n74 62 259 127 \n75 14 288 127 \n76 259 127 288 \n77 33 193 149 \n78 85 248 193 \n79 16 149 248 \n80 193 248 149 \n81 62 259 251 \n82 42 243 259 \n83 47 251 243 \n84 259 243 251 \n85 201 91 326 \n86 85 201 240 \n87 17 326 240 \n88 326 201 240 \n89 166 23 266 \n90 190 273 266 \n91 49 166 280 \n92 23 190 266 \n93 185 33 193 \n94 193 85 201 \n95 201 91 185 \n96 185 193 201 \n97 134 20 198 \n98 51 198 134 \n99 47 134 273 \n100 51 134 273 \n101 281 6 316 \n102 209 44 316 \n103 41 281 209 \n104 281 316 209 \n105 158 49 126 \n106 47 280 126 \n107 18 158 126 \n108 49 126 280 \n109 237 8 323 \n110 44 323 217 \n111 38 237 217 \n112 323 217 237 \n113 190 51 273 \n114 273 47 266 \n115 166 280 266 \n116 47 280 266 \n117 225 41 274 \n118 38 225 229 \n119 4 274 229 \n120 274 225 229 \n121 230 21 287 \n122 254 294 287 \n123 53 230 301 \n124 21 254 287 \n125 209 44 217 \n126 217 38 225 \n127 225 41 209 \n128 209 217 225 \n129 253 261 15 \n130 55 261 102 \n131 102 227 294 \n132 55 102 294 \n133 129 22 117 \n134 233 31 117 \n135 93 117 233 \n136 93 129 117 \n137 222 53 94 \n138 219 301 45 \n139 295 222 94 \n140 53 94 301 \n141 125 28 165 \n142 165 34 125 \n143 31 125 241 \n144 34 125 241 \n145 254 55 294 \n146 294 45 287 \n147 230 301 287 \n148 45 301 287 \n149 173 26 249 \n150 121 233 249 \n151 34 173 241 \n152 26 121 249 \n153 289 20 310 \n154 310 59 289 \n155 57 289 308 \n156 59 289 308 \n157 233 31 249 \n158 173 241 249 \n159 121 93 233 \n160 31 241 249 \n161 317 25 315 \n162 103 322 315 \n163 308 317 315 \n164 25 103 315 \n165 118 13 142 \n166 257 48 142 \n167 46 118 257 \n168 118 142 257 \n169 111 27 282 \n170 322 57 282 \n171 61 282 322 \n172 61 111 282 \n173 150 14 174 \n174 264 50 174 \n175 48 150 264 \n176 150 174 264 \n177 308 59 317 \n178 103 61 322 \n179 322 57 315 \n180 57 308 315 \n181 182 11 110 \n182 271 46 110 \n183 50 182 271 \n184 182 110 271 \n185 143 27 167 \n186 329 65 167 \n187 63 143 329 \n188 143 167 329 \n189 257 48 264 \n190 264 50 271 \n191 271 46 257 \n192 257 264 271 \n193 175 29 199 \n194 100 67 199 \n195 65 175 100 \n196 175 199 100 \n197 277 24 221 \n198 278 37 221 \n199 87 278 277 \n200 221 278 277 \n201 207 21 135 \n202 108 63 135 \n203 67 207 108 \n204 207 135 108 \n205 213 37 285 \n206 121 93 285 \n207 26 213 121 \n208 213 285 121 \n209 329 65 100 \n210 100 67 108 \n211 108 63 329 \n212 329 100 108 \n213 270 87 292 \n214 93 292 129 \n215 22 270 129 \n216 292 129 270 \n217 135 63 116 \n218 53 116 230 \n219 21 135 230 \n220 116 230 135 \n221 93 292 285 \n222 87 278 292 \n223 37 285 278 \n224 292 278 285 \n225 282 57 124 \n226 63 124 143 \n227 27 282 143 \n228 124 143 282 \n229 214 9 238 \n230 299 54 238 \n231 52 214 299 \n232 214 238 299 \n233 222 53 132 \n234 57 132 289 \n235 20 222 289 \n236 132 289 222 \n237 141 2 246 \n238 54 246 306 \n239 32 141 306 \n240 246 306 141 \n241 57 132 124 \n242 53 116 132 \n243 63 124 116 \n244 132 116 124 \n245 313 52 206 \n246 32 313 133 \n247 8 206 133 \n248 206 313 133 \n249 317 25 239 \n250 140 69 239 \n251 59 140 317 \n252 239 140 317 \n253 299 54 306 \n254 306 32 313 \n255 313 52 299 \n256 299 306 313 \n257 231 69 148 \n258 190 51 148 \n259 23 231 190 \n260 231 148 190 \n261 125 28 267 \n262 40 267 320 \n263 31 125 320 \n264 267 320 125 \n265 310 59 156 \n266 51 156 198 \n267 20 310 198 \n268 156 198 310 \n269 31 327 117 \n270 71 269 327 \n271 22 117 269 \n272 327 269 117 \n273 51 156 148 \n274 59 140 156 \n275 69 148 140 \n276 156 140 148 \n277 98 40 260 \n278 71 98 262 \n279 21 260 262 \n280 260 98 262 \n281 269 22 325 \n282 164 228 290 \n283 71 290 164 \n284 71 269 290 \n285 320 31 327 \n286 327 71 98 \n287 98 40 320 \n288 320 327 98 \n289 261 297 184 \n290 172 212 73 \n291 55 297 172 \n292 55 297 261 \n293 275 8 296 \n294 106 58 296 \n295 56 275 106 \n296 275 296 106 \n297 262 164 180 \n298 172 254 180 \n299 21 180 254 \n300 21 262 180 \n301 182 11 303 \n302 58 303 114 \n303 50 182 114 \n304 303 114 182 \n305 164 73 180 \n306 172 55 254 \n307 262 71 164 \n308 73 172 180 \n309 122 56 268 \n310 50 122 174 \n311 14 268 174 \n312 268 122 174 \n313 325 22 112 \n314 188 77 112 \n315 75 325 188 \n316 325 112 188 \n317 106 58 114 \n318 114 50 122 \n319 122 56 106 \n320 106 114 122 \n321 120 19 144 \n322 196 79 144 \n323 77 120 196 \n324 120 144 196 \n325 267 28 309 \n326 130 43 309 \n327 40 130 267 \n328 309 130 267 \n329 152 16 318 \n330 204 75 318 \n331 79 152 204 \n332 152 318 204 \n333 302 43 138 \n334 199 67 138 \n335 29 302 199 \n336 302 138 199 \n337 188 77 196 \n338 196 79 204 \n339 204 75 188 \n340 188 196 204 \n341 260 40 146 \n342 67 146 207 \n343 21 260 207 \n344 146 207 260 \n345 261 184 15 \n346 81 184 297 \n347 297 172 212 \n348 212 81 297 \n349 67 146 138 \n350 40 130 146 \n351 43 138 130 \n352 146 130 138 \n353 176 81 212 \n354 318 228 220 \n355 16 220 318 \n356 16 176 220 \n357 95 18 119 \n358 154 62 119 \n359 60 95 154 \n360 95 119 154 \n361 164 228 73 \n362 75 228 290 \n363 269 325 290 \n364 75 290 325 \n365 150 14 127 \n366 62 127 162 \n367 48 150 162 \n368 127 162 150 \n369 318 228 75 \n370 73 220 228 \n371 176 212 220 \n372 73 220 212 \n373 170 60 324 \n374 48 170 142 \n375 13 324 142 \n376 324 170 142 \n377 216 17 240 \n378 236 85 240 \n379 83 216 236 \n380 216 240 236 \n381 154 62 162 \n382 162 48 170 \n383 170 60 154 \n384 154 162 170 \n385 152 16 248 \n386 85 248 244 \n387 79 152 244 \n388 248 244 152 \n389 284 178 321 \n390 81 178 284 \n391 184 263 15 \n392 81 284 184 \n393 252 83 208 \n394 79 252 144 \n395 19 208 144 \n396 208 252 144 \n397 30 291 101 \n398 88 186 328 \n399 291 101 224 \n400 186 30 291 \n401 236 85 244 \n402 244 79 252 \n403 252 83 236 \n404 236 244 252 \n405 176 194 178 \n406 109 186 30 \n407 16 194 109 \n408 16 194 176 \n409 194 109 186 \n410 176 178 81 \n411 88 194 178 \n412 88 194 186 \n413 303 11 159 \n414 64 159 303 \n415 58 202 303 \n416 202 64 303 \n417 151 64 202 \n418 316 218 210 \n419 6 210 316 \n420 6 151 210 \n421 296 58 218 \n422 44 218 296 \n423 8 296 323 \n424 44 296 323 \n425 316 218 44 \n426 58 210 218 \n427 151 202 210 \n428 58 210 202 \n429 256 214 9 \n430 211 191 226 \n431 52 226 191 \n432 52 191 214 \n433 203 66 234 \n434 234 56 183 \n435 245 268 183 \n436 56 183 268 \n437 206 242 226 \n438 234 242 275 \n439 8 242 275 \n440 8 242 206 \n441 234 275 56 \n442 206 226 52 \n443 66 242 226 \n444 66 242 234 \n445 223 10 247 \n446 247 70 223 \n447 68 223 250 \n448 70 223 250 \n449 255 5 258 \n450 276 265 258 \n451 250 255 258 \n452 5 276 258 \n453 283 3 215 \n454 265 68 215 \n455 72 215 265 \n456 72 283 215 \n457 250 70 255 \n458 276 72 265 \n459 265 68 258 \n460 68 250 258 \n461 311 3 96 \n462 272 76 96 \n463 74 311 272 \n464 311 96 272 \n465 104 1 128 \n466 279 78 128 \n467 76 104 279 \n468 104 128 279 \n469 136 9 304 \n470 286 74 304 \n471 78 136 286 \n472 136 304 286 \n473 272 76 279 \n474 279 78 286 \n475 286 74 272 \n476 272 279 286 \n477 168 7 192 \n478 293 82 192 \n479 80 168 293 \n480 168 192 293 \n481 255 5 200 \n482 82 200 300 \n483 70 255 300 \n484 200 300 255 \n485 307 80 160 \n486 70 307 247 \n487 10 160 247 \n488 160 307 247 \n489 293 82 300 \n490 300 70 307 \n491 307 80 293 \n492 293 300 307 \n493 232 9 314 \n494 256 321 314 \n495 84 232 328 \n496 9 256 314 \n497 184 284 263 \n498 178 88 321 \n499 86 284 321 \n500 86 263 284 \n501 101 224 10 \n502 224 84 291 \n503 186 291 328 \n504 84 291 328 \n505 256 86 321 \n506 321 88 314 \n507 232 328 314 \n508 88 328 314 \n509 7 99 168 \n510 319 99 107 \n511 168 115 80 \n512 319 7 99 \n513 113 12 312 \n514 312 90 113 \n515 107 92 113 \n516 90 107 113 \n517 105 92 115 \n518 115 80 105 \n519 10 105 160 \n520 80 105 160 \n521 92 99 107 \n522 99 168 115 \n523 90 319 107 \n524 92 99 115 \n525 109 16 149 \n526 123 33 149 \n527 30 109 123 \n528 109 149 123 \n529 113 12 157 \n530 33 157 131 \n531 92 113 131 \n532 157 131 113 \n533 139 30 101 \n534 92 139 105 \n535 10 101 105 \n536 101 139 105 \n537 123 33 131 \n538 131 92 139 \n539 139 30 123 \n540 123 131 139 \n541 224 84 147 \n542 68 147 223 \n543 10 224 223 \n544 147 223 224 \n545 304 74 155 \n546 84 155 232 \n547 9 304 232 \n548 155 232 304 \n549 215 68 163 \n550 74 163 311 \n551 3 215 311 \n552 163 311 215 \n553 74 163 155 \n554 68 147 163 \n555 84 155 147 \n556 163 147 155 \n557 246 2 205 \n558 171 36 205 \n559 54 171 246 \n560 205 171 246 \n561 197 36 179 \n562 128 78 179 \n563 1 197 128 \n564 197 179 128 \n565 238 54 187 \n566 78 187 136 \n567 9 238 136 \n568 187 136 238 \n569 78 187 179 \n570 54 171 187 \n571 36 179 171 \n572 187 171 179 \n573 15 195 263 \n574 203 253 39 \n575 211 195 263 \n576 253 15 195 \n"
  },
  {
    "path": "test/functionals/gradsq/gradsq.morpho",
    "content": "// Test gradsq on a single triangle\n\nimport meshtools\nimport shapeopt\n\nvar m = Mesh(\"triangle.mesh\")\n\nvar phi = Field(m)\nphi[1]=1\n\nvar lel = GradSq(phi)\n\nprint lel.integrand(m)\n// expect: [ 0.57735 ]\n\nprint lel.total(m)\n// expect: 0.57735\n\nvar grad = lel.gradient(m)\nvar value = Matrix([[ 0.192452, 0.192452, -0.3849 ],[ 0.333333, -0.333335, 5.55112e-07 ],[ 0, 0, 0 ]])\nprint (grad-value).norm()<1e-5\n// expect: true\n\nvar grd=lel.fieldgradient(m, phi)\n\nprint abs(grd[0] - (-0.57735))<5e-6\nprint abs(grd[1] - (1.1547))<5e-6\nprint abs(grd[2] - (-0.57735))<5e-6\n// expect: true\n// expect: true\n// expect: true\n"
  },
  {
    "path": "test/functionals/gradsq/gradsq1d.morpho",
    "content": "\n// Currently morpho doesn't support GradSq on 1D elements. \n// This test will in future check that this works once implemented\n\n/*import meshtools \n\nvar mb = MeshBuilder()\nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0])\nmb.addedge([0,1])\nvar m = mb.build() \n\nvar f = Field(m, fn (x,y,z) x)\n\nvar lgs = GradSq(f) \n\nprint lgs.total(m) */"
  },
  {
    "path": "test/functionals/gradsq/gradsq3d.morpho",
    "content": "// Test gradsq on a single tetrahedron\nimport meshtools\nimport shapeopt\nimport plot\n\nvar tol = 2e-4\n\nvar mb = MeshBuilder()\nmb.addvertex(Matrix([0,0,0]))\nmb.addvertex(Matrix([2,3,0]))\nmb.addvertex(Matrix([-1,2,1]))\nmb.addvertex(Matrix([1,4,5]))\nmb.addelement(3,[0,1,2,3])\n\nvar m = mb.build()\nm.addgrade(1)\n\nvar phi = Field(m, Matrix(2))\nphi[0]=Matrix([0,1])\nphi[1]=Matrix([8,2])\nphi[2]=Matrix([7/2,3])\nphi[3]=Matrix([23/2,4])\n\nvar lel = GradSq(phi)\n\nvar expected = 29.8611\nvar tol = 2e-4\n\nprint abs(lel.integrand(m)[0]-expected)<tol\n// expect: true\n\nprint abs(lel.total(m)-expected)<tol\n// expect: true\n\nexpected = Matrix([ [ 10.6944, 0.972271, -13.5417, 1.87502 ],\n                    [ 1.9908, -4.0278, 5.3241, -3.28704 ],\n                    [ 7.96296, -8.47221, -6.13422, 6.64349 ] ])\n\nprint (lel.gradient(m)-expected).norm()<tol\n// expect: true\n\nexpected = [ [-6.66663, -3.88891], [5.00007, 0], [0.833325, 4.44448], [0.833325, -0.555556] ]\nvar grd=lel.fieldgradient(m, phi)\nfor (x,k in grd) print (x-Matrix(expected[k])).norm()<tol\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n"
  },
  {
    "path": "test/functionals/gradsq/integral.morpho",
    "content": "// Test grad sq with a simple integral\n// f = x^2 + y^2 -> |grad f|^2 = 4 (x^2+y^2) -> integral over unit disk is 2 pi\n\nimport meshtools\nimport shapeopt\n\nvar m = Mesh(\"disk.mesh\")\n\nvar phi = Field(m, fn (x,y,z) x^2 + y^2)\n\nvar lel = GradSq(phi)\n\nprint lel.total(m)\n// expect: 6.21862\n"
  },
  {
    "path": "test/functionals/gradsq/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/functionals/gradsq/triangle.mesh",
    "content": "vertices\n\n1 1 0 0\n2 -0.5 0.866025 0\n3 -0.5 -0.866025 0\n\nfaces\n\n1 1 2 3\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel.morpho",
    "content": "// Hydrogel mixing energy (modeled from Flory-Rehner theory)\nvar m0 = Mesh(\"tetrahedron.mesh\")\n\nvar vol0 = Volume().total(m0)\nvar phi0 = 0.5\nvar phiref = 0.1\nvar a = 0.1\nvar b = 1.0\nvar c = 0.25\nvar d = 1.0\nvar lh = Hydrogel(m0,\n                  a = a,\n                  b = b,\n                  c = c,\n                  d = d,\n                  phiref=phiref,\n                  phi0=phi0)\n\nvar m = Mesh(\"tetrahedron.mesh\")\n// Expand m by a linear factor\nvar f = 1.2\nvar vert = m.vertexmatrix()\nfor (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i))\n\nvar phi = phi0/(f^3) // New phi will be inversely proportional to the volume\nvar vol = vol0 * f^3\n\n\nvar e1 = vol * (a*phi*log(phi) + b*(1-phi)*log((1-phi)) + c*phi*(1-phi))\nvar e2 = vol0 * d * ( log(phiref/phi)/3 - (phiref/phi)^(2/3) + 1 )\nvar expected = e1 + e2\n\nvar tol = 1e-6\nprint abs(lh.integrand(m)[0]-expected)<tol\n// expect: true\n\nprint abs(lh.total(m)-expected)<tol\n// expect: true\n\nexpected = Matrix([ [ 0, 0.00561671, 0.00561671, -0.0112336 ],\n                    [ 0, 0.00972852, -0.00972849, 0 ],\n                    [ -0.011915, 0.00397177, 0.00397167, 0.00397174 ]])\n\nprint (lh.gradient(m)-expected).norm()<tol\n// expect: true\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel1D.morpho",
    "content": "// Hydrogel mixing energy in 1D (modeled from Flory-Rehner theory)\nimport meshtools \n\nvar mb = MeshBuilder()\n\nmb.addvertex(Matrix([-1/2,0,0]))\nmb.addvertex(Matrix([1/2,0,0]))\nmb.addedge([0,1])\nvar m0 = mb.build()\n\nvar vol0 = Length().total(m0)\nvar phi0 = 0.5\nvar phiref = 0.1\nvar a = 0.1\nvar b = 1.0\nvar c = 0.25\nvar d = 1.0\nvar lh = Hydrogel(m0,\n                  a = a,\n                  b = b,\n                  c = c,\n                  d = d,\n                  phiref=phiref,\n                  phi0=phi0)\n\nvar m = mb.build()\n// Expand m by a linear factor\nvar f = 1.2\nvar vert = m.vertexmatrix()\nfor (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i))\n\nvar phi = phi0/(f) // New phi will be inversely proportional to the length\nvar vol = vol0 * f\n\n\nvar e1 = vol * (a*phi*log(phi) + b*(1-phi)*log((1-phi)) + c*phi*(1-phi))\nvar e2 = vol0 * d * ( log(phiref/phi)/3 - (phiref/phi)^(2/3) + 1 )\nvar expected = e1 + e2\n\nvar tol = 1e-6\nprint abs(lh.integrand(m)[0]-expected)<tol\n// expect: true\n\nprint abs(lh.total(m)-expected)<tol\n// expect: true\n\nvar grad = (-a * phi + b * ( phi + log(1-phi) ) + c * phi*phi +\n            d * (phiref/phi0) * ((phi/phiref)/3.0 - (2.0/3) * (phi/phiref)^(1.0/3) ) )\n\nvar lengthgrad = Length().gradient(m)\n\nexpected = grad * lengthgrad\n\nprint (lh.gradient(m)-expected).norm()<tol\n// expect: true\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel1D_2elements.morpho",
    "content": "// Hydrogel mixing energy in 1D for a mesh with 2 segments (modeled from\n// Flory-Rehner theory)\n// Testing whether elementwise scaling by the gradient works\nimport meshtools \n\nvar mb = MeshBuilder()\n\nmb.addvertex(Matrix([-0.6,0.1,0.3]))\nmb.addvertex(Matrix([0.2,-0.2,-0.1]))\nmb.addvertex(Matrix([1/2,0.3,0.1]))\nmb.addedge([0,1])\nmb.addedge([0,2])\nvar m0 = mb.build()\n\nvar vol0 = Length().total(m0)\nvar phi0 = 0.5\nvar phiref = 0.1\nvar a = 0.1\nvar b = 1.0\nvar c = 0.25\nvar d = 1.0\nvar lh = Hydrogel(m0,\n                  a = a,\n                  b = b,\n                  c = c,\n                  d = d,\n                  phiref=phiref,\n                  phi0=phi0)\n\nvar m = mb.build()\n// Expand m by a linear factor\nvar f = 1.2\nvar vert = m.vertexmatrix()\nfor (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i))\n\nvar expected = Matrix([[ 0.10421, -0.0486482, -0.0555622 ],\n                       [ -0.00814071, 0.0182433, -0.0101026 ],\n                       [ -0.0344261, 0.0243235, 0.0101026 ]])\n\nvar tol = 1e-5\nprint (lh.gradient(m)-expected).norm()<tol\n// expect: true\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel2D.morpho",
    "content": "// Hydrogel mixing energy in 2D (modeled from Flory-Rehner theory)\nimport meshtools \n\nvar mb = MeshBuilder()\n\nmb.addvertex(Matrix([-1/2,0,0]))\nmb.addvertex(Matrix([1/2,0,0]))\nmb.addvertex(Matrix([0,sqrt(3)/2,0]))\nmb.addedge([0,1])\nmb.addedge([1,2])\nmb.addedge([2,0])\nmb.addface([0,1,2])\n\nvar m0 = mb.build()\n\nvar vol0 = Area().total(m0)\nvar phi0 = 0.5\nvar phiref = 0.1\nvar a = 0.1\nvar b = 1.0\nvar c = 0.25\nvar d = 1.0\nvar lh = Hydrogel(m0,\n                  a = a,\n                  b = b,\n                  c = c,\n                  d = d,\n                  phiref=phiref,\n                  phi0=phi0)\n\nvar m = mb.build()\n// Expand m by a linear factor\nvar f = 1.2\nvar vert = m.vertexmatrix()\nfor (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i))\n\nvar phi = phi0/(f^2) // New phi will be inversely proportional to the area\nvar vol = vol0 * f^2\n\n\nvar e1 = vol * (a*phi*log(phi) + b*(1-phi)*log((1-phi)) + c*phi*(1-phi))\nvar e2 = vol0 * d * ( log(phiref/phi)/3 - (phiref/phi)^(2/3) + 1 )\nvar expected = e1 + e2\n\nvar tol = 1e-6\nprint abs(lh.integrand(m)[0]-expected)<tol\n// expect: true\n\nprint abs(lh.total(m)-expected)<tol\n// expect: true\n\nvar grad = (-a * phi + b * ( phi + log(1-phi) ) + c * phi*phi +\n            d * (phiref/phi0) * ((phi/phiref)/3.0 - (2.0/3) * (phi/phiref)^(1.0/3) ) )\n\nvar areagrad = Area().gradient(m)\n\nexpected = grad * areagrad\n\nprint (lh.gradient(m)-expected).norm()<tol\n// expect: true\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel2D_2elements.morpho",
    "content": "// Hydrogel mixing energy in 2D for a mesh with 2 facets (modeled from\n// Flory-Rehner theory)\n// Testing whether elementwise scaling by the gradient works\nimport meshtools \n\nvar mb = MeshBuilder()\n\nmb.addvertex(Matrix([-1/2 ,0.1,0.1]))\nmb.addvertex(Matrix([0.6,-0.07,0]))\nmb.addvertex(Matrix([0,0.76,-0.2]))\nmb.addvertex(Matrix([0.25,-0.4,0.05]))\nmb.addedge([0,1])\nmb.addedge([1,2])\nmb.addedge([2,0])\nmb.addedge([3,0])\nmb.addedge([3,1])\nmb.addface([0,1,2])\nmb.addface([0,1,3])\n\nvar m0 = mb.build()\n\nvar vol0 = Area().total(m0)\nvar phi0 = 0.5\nvar phiref = 0.1\nvar a = 0.1\nvar b = 1.0\nvar c = 0.25\nvar d = 1.0\nvar lh = Hydrogel(m0,\n                  a = a,\n                  b = b,\n                  c = c,\n                  d = d,\n                  phiref=phiref,\n                  phi0=phi0)\n\nvar m = m0.clone()\nvar f = 1.2\nvar vert = m.vertexmatrix()\nfor (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i))\n\nvar expected = Matrix([[ 0.0381971, -0.0394159, -0.00413374, 0.0053525 ],\n                       [ 0.00593282, -0.00787234, -0.0340045, 0.035944 ],\n                       [ -0.0104877, 0.000378424, 0.0123365, -0.00222724 ]])\nvar tol = 1e-6\n\nprint (lh.gradient(m)-expected).norm()<tol\n// expect: true\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel3D_2elements.morpho",
    "content": "// Hydrogel mixing energy in 2D for a mesh with 2 facets (modeled from\n// Flory-Rehner theory)\n// Testing whether elementwise scaling by the gradient works\nimport meshtools \n\nvar mb = MeshBuilder()\n\nmb.addvertex(Matrix([-1/2 ,0.1,0.1]))\nmb.addvertex(Matrix([0.6,-0.07,0]))\nmb.addvertex(Matrix([0,0.76,-0.2]))\nmb.addvertex(Matrix([0.35,0.3,0.85]))\nmb.addvertex(Matrix([0.29,0.34,-0.79]))\nmb.addvolume([0,1,2,3])\nmb.addvolume([0,1,2,4])\nvar m0 = mb.build()\n\nm0.addgrade(1)\nm0.addgrade(2)\n\nvar vol0 = Volume().total(m0)\nvar phi0 = 0.5\nvar phiref = 0.1\nvar a = 0.1\nvar b = 1.0\nvar c = 0.25\nvar d = 1.0\nvar lh = Hydrogel(m0,\n                  a = a,\n                  b = b,\n                  c = c,\n                  d = d,\n                  phiref=phiref,\n                  phi0=phi0)\n\nvar m = m0.clone()\nvar f = 1.2\nvar vert = m.vertexmatrix()\nfor (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i))\n\nvar tol = 1e-6\n\nvar expected = Matrix([[ 0.0186177, -0.0147269, -0.0038909, -0.00160984, 0.00160958 ],\n                       [ 0.0133731, 0.0115293, -0.0249025, -0.0038523, 0.00385234 ],\n                       [ -0.000354994, 0.000820056, -0.000465045, -0.0111579, 0.011158 ]])\n\nprint (lh.gradient(m)-expected).norm()<tol\n// expect: true\n"
  },
  {
    "path": "test/functionals/hydrogel/hydrogel_field_lacks_grade.morpho",
    "content": "// Create a hydrogel phi0 with the wrong type of Field \n\nimport plot \nimport optimize\nimport meshtools\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0])\nmb.addvertex([0,1,0])\nmb.addvertex([0,0,1])\nmb.addelement(3,[0,1,2,3])\n\nvar m=mb.build() \nvar mref = m.clone() \n\nvar phi0 = Field(m, grade=2) // Needs to have elements in grade 3 \n\nvar fha = 0.0 \nvar fhb = 1.0\nvar fhc = 0.499 \nvar fhd = -1.0 \nvar phir = 0.0359465\n\nvar lh=Hydrogel(mref,\n    a = fha,\n    b = fhb,\n    c = fhc,\n    d = fhd,\n    phiref=phir,\n    phi0=phi0)\n\nlh.total(m)\n// expect error 'HydrglFldGrd' \n"
  },
  {
    "path": "test/functionals/hydrogel/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/functionals/length/length.morpho",
    "content": "\nimport \"../numericalderivatives.morpho\"\n\nvar m = Mesh(\"line.mesh\")\nvar a = Length()\n\nprint m\n// expect: <Mesh: 2 vertices>\n\nprint (a.total(m) - 2*sqrt(2)) < 1e-5\n// expect: true\n\nprint (a.integrand(m) - Matrix([ 2*sqrt(2) ])).norm() < 1e-5\n// expect: true\n\nprint (a.gradient(m) - numericalgradient(a, m)).norm() < 1e-5\n// expect: true"
  },
  {
    "path": "test/functionals/length/length2d.morpho",
    "content": "import meshtools \nimport \"../numericalderivatives.morpho\"\n\nvar mb = MeshBuilder()\nmb.addvertex([-1,-1])\nmb.addvertex([1,1])\nmb.addedge([0,1])\nvar m = mb.build() \n\nvar a = Length()\n\nprint m\n// expect: <Mesh: 2 vertices>\n\nprint (a.total(m) - 2*sqrt(2)) < 1e-5\n// expect: true\n\nprint (a.integrand(m) - Matrix([ 2*sqrt(2) ])).norm() < 1e-5\n// expect: true\n\nprint (a.gradient(m) - numericalgradient(a, m)).norm() < 1e-5\n// expect: true"
  },
  {
    "path": "test/functionals/length/length_hessian.morpho",
    "content": "\nimport meshtools\nimport \"../numericalderivatives.morpho\"\n\nvar m = LineMesh(fn (t) [cos(t), sin(t)], -Pi...Pi:Pi/2, closed=true)\n\nvar a = Length()\nprint abs(a.total(m) - 4*sqrt(2)) < 1e-10\n// expect: true\n\nprint (a.gradient(m)-numericalgradient(a, m)).norm()<1e-6\n// expect: true\n\nprint (Matrix(a.hessian(m))-numericalhessian(a, m, eps=1e-4)).norm() < 1e-2\n// expect: true\n"
  },
  {
    "path": "test/functionals/length/length_integrandForElement.morpho",
    "content": "import meshtools\n\nvar m = LineMesh(fn(t) [t^2, 0, 0], 0..10)\n\nvar l = Length()\n\nprint l.integrandForElement(m, 5) // expect: 11\n"
  },
  {
    "path": "test/functionals/length/line.mesh",
    "content": "vertices\n\n1 -1 -1 0\n2 1 1 0\n\nedges\n\n1 1 2\n"
  },
  {
    "path": "test/functionals/linearelasticity/linearelasticity.morpho",
    "content": "\nvar m = Mesh(\"stretchsquare.mesh\")\nvar mshear = Mesh(\"shearsquare.mesh\")\nvar mscale = Mesh(\"scalesquare.mesh\")\nvar mref = Mesh(\"square.mesh\")\n\nvar e = LinearElasticity(mref)\n\ne.poissonratio = 0.2\n\nprint e.reference\n// expect: <Mesh: 4 vertices>\nprint e.grade\n// expect: 2\n\nprint e.integrand(m)\n// expect: [ 0.625 0.625 ]\n\nprint e.total(m)\n// expect: 1.25\n\nprint e.integrand(mshear)\n// expect: [ 0.0303819 0.0303819 ]\n\nprint e.integrand(mscale)\n// expect: [ 1.5625 1.5625 ]\n\nprint e.gradient(mscale)\n// expect: [ -2.08333 2.08333 -2.08333 2.08333 ]\n// expect: [ -2.08333 -2.08333 2.08333 2.08333 ]\n// expect: [ 0 0 0 0 ]\n"
  },
  {
    "path": "test/functionals/linearelasticity/relax.morpho",
    "content": "import shapeopt\n\nvar mstretch = Mesh(\"stretchsquare.mesh\")\nvar mshear = Mesh(\"shearsquare.mesh\")\nvar mscale = Mesh(\"scalesquare.mesh\")\nvar mref = Mesh(\"square.mesh\")\n\nvar e = LinearElasticity(mref)\ne.poissonratio = 0.4\n\nvar s = ShapeOptimizer(mscale)\ns.addenergy(e)\ns.quiet = true\n\n// Relax the scaled square\ns.relax(100)\nprint abs(e.total(mscale))<1e-8\n// expect: true\n\n// Relax the sheared square\ns.mesh = mshear\ns.relax(200)\nprint abs(e.total(mshear))<1e-8\n// expect: true\n\n// Relax the stretched square\ns.mesh=mstretch\ns.relax(200)\nprint abs(e.total(mstretch))<1e-8\n// expect: true\n"
  },
  {
    "path": "test/functionals/linearelasticity/scalesquare.mesh",
    "content": "vertices\n\n1 0 0 0\n2 2 0 0\n3 0 2 0\n4 2 2 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/functionals/linearelasticity/shearsquare.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0.5 1 0\n4 1.5 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/functionals/linearelasticity/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/functionals/linearelasticity/stretchsquare.mesh",
    "content": "vertices\n\n1 0 0 0\n2 2 0 0\n3 0 1 0\n4 2 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/gradient.morpho",
    "content": "// Line curvature Sq gradient\nimport constants\nimport meshtools\n\nimport \"../numericalderivatives.morpho\"\n\nvar np=10\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t),0], 0...2*Pi:2*Pi/np, closed=true)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\n\nprint lc.total(m)\n// expect: 6.38774\n\nvar grad = lc.gradient(m)\nvar ngrad = numericalgradient(lc, m)\n\nprint (grad-ngrad).norm()/grad.count() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/gradient_symm.morpho",
    "content": "// Line curvature Sq gradient\nimport constants\nimport meshtools\nimport plot\nimport symmetry\n\nvar np=10\nvar L=1\n\nvar m = LineMesh(fn (t) [t, 0.2*cos(2*Pi*t), 0], -L..L:2/np)\n//Show(plotmesh(m))\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(x+L)<1e-10 || abs(x-L)<1e-10)\nvar t = Translate(Matrix([2*L,0,0]))\nm.addsymmetry(t, s)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\n\nvar grad = lc.gradient(m)\n\nvar dim = grad.dimensions()\nvar ngrad = Matrix(dim[0], dim[1])\n\n// Manually calculate the gradient\nvar vert = m.vertexmatrix()\n\nvar eps = 1e-10\n\nfor (i in 0...dim[0]) {\n  for (j in 0...dim[1]) {\n    var v = vert[i, j]\n    vert[i, j] = v + eps\n    var fp = lc.total(m)\n    vert[i, j] = v - eps\n    var fm = lc.total(m)\n    ngrad[i,j] = (fp-fm)/(2*eps)\n  }\n}\n\nvar sym=ngrad.column(0)+ngrad.column(np)\nngrad.setcolumn(0, sym)\nngrad.setcolumn(np, sym)\n\n//print (grad-ngrad).norm()\n\nprint (grad-ngrad).norm()/grad.count() < 1e-5 // expect: true\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/hessian.morpho",
    "content": "// Line curvature Sq hessian\nimport constants\nimport meshtools\n\nimport \"../numericalderivatives.morpho\"\n\nvar np=10\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t),0], 0...2*Pi:2*Pi/np, closed=true)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\n\nprint abs(lc.total(m) - 6.38774) < 1e-5\n// expect: true\n\nvar h = lc.hessian(m) \nvar h2 = numericalhessian(lc, m)\n\n//print Matrix(h).format(\"%10.4g\") \n//print h2.format(\"%10.4g\") \n\nprint (Matrix(h) - h2).norm()/h2.count() < 1e-5 // expect: true\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/linecurvaturesq.morpho",
    "content": "// Line curvature Sq\nimport constants\nimport meshtools\n\nvar np=10\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t),0], 0...2*Pi:2*Pi/np, closed=true)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\n\n//print lc.integrand(m)\nprint lc.total(m)\n// expect: 6.38774\n\nprint 2*Pi*R*(1/R)^2\n// expect: 6.28319\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/linecurvaturesq.xmorpho",
    "content": "// Line curvature Sq\nimport constants\nimport meshtools\n\nvar np=10\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t),0], 0...2*Pi:2*Pi/np, closed=true)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\n\n//print lc.integrand(m)\nprint lc.total(m)\n// expect: 6.38774\n\nprint 2*Pi*R*(1/R)^2\n// expect: 6.28319\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/linecurvaturesq_integrandForElement.morpho",
    "content": "// Line curvature Sq\nimport constants\nimport meshtools\n\nvar np=10\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t),0], 0...2*Pi:2*Pi/np, closed=true)\n\nvar lc = LineCurvatureSq()\n\nprint abs(lc.integrandForElement(m,1) - 0.638774) < 1e-5 // expect: true\nlc.integrandonly = true\nprint abs(lc.integrandForElement(m,1)-1.03356) < 1e-5  // expect: true\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/linecurvaturesq_symm.morpho",
    "content": "// Symmetries\nimport constants\nimport meshtools\nimport plot\nimport symmetry\n\nvar np=50\nvar L=1\n\nvar m = LineMesh(fn (t) [t, 0.2*cos(Pi*t), 0], -L..L:2/np)\n//Show(plotmesh(m))\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(x+L)<1e-10 || abs(x-L)<1e-10)\nvar t = Translate(Matrix([2*L,0,0]))\nm.addsymmetry(t, s)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\nprint lc.total(m)\n// expect: 3.16856\n\nlc.integrandonly = true\nprint lc.integrand(m)\n// expect: [ 3.87607 3.74578 3.38492 2.8701 2.29371 1.73455 1.24313 0.841609 0.531919 0.305208 0.149083 0.0520402 0.00569076 0.00569076 0.0520402 0.149083 0.305208 0.531919 0.841609 1.24313 1.73455 2.29371 2.8701 3.38492 3.74578 3.87607 3.74578 3.38492 2.8701 2.29371 1.73455 1.24313 0.841609 0.531919 0.305208 0.149083 0.0520402 0.00569076 0.00569076 0.0520402 0.149083 0.305208 0.531919 0.841609 1.24313 1.73455 2.29371 2.8701 3.38492 3.74578 0 ]\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/linecurvaturesq_symm.xmorpho",
    "content": "// Symmetries\nimport constants\nimport meshtools\nimport plot\nimport \"symmetry.xmorpho\"\n\nvar np=10\nvar L=1\n\nvar m = LineMesh(fn (t) [t, cos(Pi*t), 0], -L..L:2/np)\n//Show(plotmesh(m))\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(x+L)<1e-10 || abs(x-L)<1e-10)\nvar t = Translate(Matrix([2*L,0,0]))\nm.addsymmetry(t, s)\n\n//print m.connectivitymatrix(0,0)\n\n// Create the manifold\nvar lc = LineCurvatureSq()\n\nprint lc.integrand(m)\n"
  },
  {
    "path": "test/functionals/linecurvaturesq/symmetry.xmorpho",
    "content": "// Symmetries\n\n/** Translations by a constant vector */\nclass Translate {\n  init (vec) { // Store the translation vector\n    self.vec = vec\n  }\n\n  transform(posn) { // Forward transformation\n    return posn+self.vec\n  }\n\n  inverse(posn) { // Inverse transformation\n    return posn-self.vec\n  }\n}\n"
  },
  {
    "path": "test/functionals/lineintegral/fields_incorrect_args.morpho",
    "content": "// Check what happens if insufficient fields supplied to LineIntegral\n\nimport constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:0.1)\n\nvar f = Field(m, fn (x,y,z) x)\n\n// A line integral with a field\nvar lcf = LineIntegral(fn (x, f, g) (f*(1-f))^2, f, List())\n// expect error 'IntgrlArgs'\n"
  },
  {
    "path": "test/functionals/lineintegral/fields_insufficient_args.morpho",
    "content": "// Check what happens if insufficient fields supplied to LineIntegral\n\nimport constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:0.1)\n\nvar f = Field(m, fn (x,y,z) x)\n\n// A line integral with a field\nvar lcf = LineIntegral(fn (x, f, g) (f*(1-f))^2, f)\n// expect error 'IntgrlNFlds'\n"
  },
  {
    "path": "test/functionals/lineintegral/fields_toomany_args.morpho",
    "content": "// Check what happens if insufficient fields supplied to LineIntegral\n\nimport constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:0.1)\n\nvar f = Field(m, fn (x,y,z) x)\n\n// A line integral with a field\nvar lcf = LineIntegral(fn (x, f, g) (f*(1-f))^2, f, f, f)\n// expect error 'IntgrlNFlds'\n"
  },
  {
    "path": "test/functionals/lineintegral/lineintegral.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:0.1)\n\nvar f = Field(m, fn (x,y,z) x)\n\n// A line integral with only spatial dependence\nvar lc = LineIntegral(fn (x) (x[0]*(1-x[0]))^2)\n\nprint lc.integrand(m)\n// expect: [ 0.000285333 0.00164533 0.00350533 0.00514533 0.00608533 0.00608533 0.00514533 0.00350533 0.00164533 0.000285333 ]\n\nprint lc.total(m)\n// expect: 0.0333333\n\n// A line integral with a field\nvar lcf = LineIntegral(fn (x, f) (f*(1-f))^2, f)\n\nprint lcf.integrand(m)\n// expect: [ 0.000285333 0.00164533 0.00350533 0.00514533 0.00608533 0.00608533 0.00514533 0.00350533 0.00164533 0.000285333 ]\n\nprint lcf.total(m)\n// expect: 0.0333333\n\nvar badLiField = LineIntegral(fn (x,f) (f*(1-f))^2, nil)\n// expect error 'IntgrlArgs'\n"
  },
  {
    "path": "test/functionals/lineintegral/lineintegral_fieldgradient.morpho",
    "content": "import meshtools \n\nvar m = LineMesh(fn (u) [u,0,0], 0..1:0.1)\n\nvar f = Field(m, fn (x,y,z) x)\n\nvar ll = LineIntegral(fn (x, f) f^2, f)\n\nvar grad = ll.fieldgradient(f)\n\nvar ngrad = f.clone() \n\nvar eps = 1e-8\nvar fl, fr \nfor (i in 0...f.count()) {\n    var f0 = f[i]\n    f[i]=f0+eps \n    fr=ll.total(m)\n    f[i]=f0-eps\n    fl=ll.total(m)\n    f[i]=f0 \n    ngrad[i]=(fr-fl)/(2*eps)\n}\n\nprint (grad-ngrad).linearize().norm() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/lineintegral/lineintegral_hessian.morpho",
    "content": "\nimport meshtools\nimport \"../numericalderivatives.morpho\"\n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:1)\n\n// A line integral with only spatial dependence\nvar lc = LineIntegral(fn (x) (x[0]*(1-x[0]))^2)\n\nvar h = lc.hessian(m)\nvar h2 = numericalhessian(lc, m)\n\nprint (Matrix(h) - h2).norm() < 1e-5 // expect: true\n"
  },
  {
    "path": "test/functionals/lineintegral/selection.morpho",
    "content": "// Test selections for fieldgradient\nimport constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [t,0,0], 0..1:0.1)\n\nvar s = Selection(m, fn (x,y,z) x<=0.5)\ns.addgrade(1)\n\nvar f = Field(m, fn (x,y,z) x)\n\n// A line integral with only spatial dependence\nvar lc = LineIntegral(fn (x, f) (f*(1-f))^2, f)\n\nvar int = lc.integrand(f, s)\n\nvar exp = Matrix([[ 0.000285333, 0.00164533, 0.00350533, 0.00514533, 0.00608533, 0, 0, 0, 0, 0 ]])\n\nprint (int-exp).norm() < 1e-8\n// expect: true\n\nprint abs(lc.total(f, s) - 0.0166667) < 1e-6\n// expect: true\n\nvar grad=lc.fieldgradient(f, s).linearize()\n\nvar ngrad = Matrix([ 0.00285333, 0.0136, 0.0186, 0.0164, 0.0094, 0.00164666, 0, 0, 0, 0, 0 ])\n\nprint (grad-ngrad).norm() < 1e-6\n// expect: true\n"
  },
  {
    "path": "test/functionals/lineintegral/tangent.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [cos(t),sin(t),0], 0..Pi:Pi/10)\n\nvar nn = Field(m, fn (x,y,z) Matrix([1,0,0]))\n\n// A line integral with a element call\nvar lc = LineIntegral(fn (x, n) (n.inner(tangent()))^2, nn)\n\nvar ans = Matrix([[ 0.00765645, 0.0644846, 0.156434, 0.248384, 0.305212, 0.305212, 0.248384, 0.156434, 0.0644846, 0.00765645 ]])\nprint (lc.integrand(m)-ans).norm() < 1e-5 \n// expect: true\n\nprint abs(lc.total(m) - 1.56434) <1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/lineintegral/tangent2d.morpho",
    "content": "import constants\nimport meshtools\n\nvar m = LineMesh(fn (t) [cos(t),sin(t)], 0..Pi:Pi/10)\n\nvar nn = Field(m, fn (x,y) Matrix([1,0]))\n\n// A line integral with a element call\nvar lc = LineIntegral(fn (x, n) (n.inner(tangent()))^2, nn)\n\nprint lc.integrand(m)\n// expect: [ 0.00765645 0.0644846 0.156434 0.248384 0.305212 0.305212 0.248384 0.156434 0.0644846 0.00765645 ]\n\nprint lc.total(m)\n// expect: 1.56434\n"
  },
  {
    "path": "test/functionals/linetorsionsq/gradient.morpho",
    "content": "// Line torsion Sq\nimport constants\nimport meshtools\n\nimport \"../numericalderivatives.morpho\"\n\nvar np=20\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t), t], 0..2*Pi:2*Pi/np, closed=false)\n\n// Create the manifold\nvar lc = LineTorsionSq()\n\nvar grad = lc.gradient(m)\nvar ngrad = numericalgradient(lc, m)\n\nprint (grad-ngrad).norm()/grad.count() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/linetorsionsq/gradient_symm.morpho",
    "content": "// Line torsion Sq\nimport constants\nimport meshtools\nimport symmetry\n\nvar np=10\nvar R=1\nvar L=2\n\nvar m = LineMesh(fn (t) [R*cos(2*Pi*t), R*sin(2*Pi*t), t], 0..L:L/np, closed=false)\n// Show(plotmesh(m, grade=1))\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(z)<1e-10 || abs(z-L)<1e-10)\nvar t = Translate(Matrix([0,0,L]))\nm.addsymmetry(t, s)\n\n// Create the manifold\nvar lc = LineTorsionSq()\n\nvar grad = lc.gradient(m)\n\nvar dim = grad.dimensions()\nvar ngrad = Matrix(dim[0], dim[1])\n\n// Manually calculate the gradient\nvar vert = m.vertexmatrix()\nvar eps = 1e-8\n\nfor (i in 0...dim[0]) {\n  for (j in 0...dim[1]) {\n    var v = vert[i, j]\n    vert[i, j] = v + eps\n    var fp = lc.total(m)\n    vert[i, j] = v - eps\n    var fm = lc.total(m)\n    ngrad[i,j] = (fp-fm)/(2*eps)\n  }\n}\n\n// Account for symmetry\nvar sym=ngrad.column(0)+ngrad.column(np)\nngrad.setcolumn(0, sym)\nngrad.setcolumn(np, sym)\n\nprint (grad-ngrad).norm()/grad.count() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/linetorsionsq/hessian.morpho",
    "content": "// Line torsion Sq\nimport constants\nimport meshtools\n\nimport \"../numericalderivatives.morpho\"\n\nvar np=4\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t), t], 0..2*Pi:2*Pi/np, closed=false)\n\n// Create the manifold\nvar lc = LineTorsionSq()\n\nvar h = lc.hessian(m) \nvar h2 = numericalhessian(lc, m)\n\nprint (Matrix(h) - h2).norm()/h2.count() < 1e-5 // expect: true"
  },
  {
    "path": "test/functionals/linetorsionsq/linetorsionsq.morpho",
    "content": "// Line torsion Sq\nimport constants\nimport meshtools\n\nvar np=20\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t), t], 0..2*Pi:2*Pi/np, closed=false)\n\n// Create the manifold\nvar lc = LineTorsionSq()\n\nprint lc.total(m)\n// expect: 2.0282\n"
  },
  {
    "path": "test/functionals/linetorsionsq/linetorsionsq.xmorpho",
    "content": "// Line torsion Sq\nimport constants\nimport meshtools\n\nvar np=10\nvar R=1\n\nvar m = LineMesh(fn (t) [R*cos(t), R*sin(t),t], 0...2*Pi:2*Pi/np, closed=true)\n\n/*// Create the manifold\nvar lc = LineCurvatureSq()\n\n//print lc.integrand(m)\nprint lc.total(m)\n// expect: 6.38774\n\nprint 2*Pi*R*(1/R)^2\n// expect: 6.28319\n*/\n"
  },
  {
    "path": "test/functionals/linetorsionsq/linetorsionsq_symm.morpho",
    "content": "// Line torsion Sq\nimport constants\nimport meshtools\nimport symmetry\nimport plot\n\nvar np=1000\nvar R=1\nvar L=2\n\nvar m = LineMesh(fn (t) [R*cos(2*Pi*t), R*sin(2*Pi*t), t], 0..L:L/np, closed=false)\n// Show(plotmesh(m, grade=1))\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(z)<1e-10 || abs(z-L)<1e-10)\nvar t = Translate(Matrix([0,0,L]))\nm.addsymmetry(t, s)\n\n// Create the torsionsq\nvar lc = LineTorsionSq()\n\nprint abs(lc.total(m) - 0.306601) < 1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/meancurvaturesq/gradient.morpho",
    "content": "// Mean Sq curvature\nimport implicitmesh\nimport plot\n\n// Make a sphere\nvar impl = ImplicitMeshBuilder(fn (x,y,z) x^2+y^2+z^2-1)\nvar m = impl.build(stepsize=0.25)\n\n// Mean Squared curvature\nvar lc = MeanCurvatureSq()\nvar grad = lc.gradient(m)\n\nvar dim = grad.dimensions()\nvar ngrad = Matrix(dim[0], dim[1])\n\n// Manually calculate the gradient\nvar vert = m.vertexmatrix()\nvar eps = 1e-8\n\nfor (i in 0...dim[0]) {\n  for (j in 0...dim[1]) {\n    var v = vert[i, j]\n    vert[i, j] = v + eps\n    var fp = lc.total(m)\n    vert[i, j] = v - eps\n    var fm = lc.total(m)\n    ngrad[i,j] = (fp-fm)/(2*eps)\n  }\n}\n\nprint (grad-ngrad).norm()/grad.count() < 1e-4 // expect: true\n"
  },
  {
    "path": "test/functionals/meancurvaturesq/gradient_symm.morpho",
    "content": "// Mean Sq curvature with a translational symmetry\nimport constants\nimport meshtools\nimport plot\nimport symmetry\n\nvar L = 2\n\nvar m = AreaMesh(fn (t, x) [x, cos(t), sin(t)], 0...2*Pi:2*Pi/10, -L..L:0.5, closed=[true,false])\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(x-L)<1e-10 || abs(x+L)<1e-10)\n\nvar t = Translate(Matrix([2*L,0,0]))\nm.addsymmetry(t, s)\n\n// Mean Squared curvature\nvar lc = MeanCurvatureSq()\nvar grad = lc.gradient(m)\n\nvar dim = grad.dimensions()\nvar ngrad = Matrix(dim[0], dim[1])\n\n// Manually calculate the gradient\nvar vert = m.vertexmatrix()\nvar eps = 1e-8\n\nfor (i in 0...dim[0]) {\n  for (j in 0...dim[1]) {\n    var v = vert[i, j]\n    vert[i, j] = v + eps\n    var fp = lc.total(m)\n    vert[i, j] = v - eps\n    var fm = lc.total(m)\n    ngrad[i,j] = (fp-fm)/(2*eps)\n  }\n}\n\n// Correct for symmetry vertices\nfor (i in 0..9) {\n  var sym=ngrad.column(i)+ngrad.column(80+i)\n  ngrad.setcolumn(i, sym)\n  ngrad.setcolumn(80+i, sym)\n}\n\nprint (grad-ngrad).norm()/grad.count() < 1e-4 // expect: true\n"
  },
  {
    "path": "test/functionals/meancurvaturesq/meancurvaturesq.morpho",
    "content": "// Mean Sq curvature\nimport implicitmesh\nimport plot\n\n// Make a sphere\nvar impl = ImplicitMeshBuilder(fn (x,y,z) x^2+y^2+z^2-1)\nvar mesh = impl.build(stepsize=0.25)\n\n// Mean Squared curvature\nvar lmc = MeanCurvatureSq()\nprint (lmc.total(mesh)-12.477)<1e-4 // expect: true\n"
  },
  {
    "path": "test/functionals/meancurvaturesq/symm.morpho",
    "content": "// Mean Sq curvature with a translational symmetry\nimport constants\nimport meshtools\nimport plot\nimport symmetry\n\nvar L = 2\n\nvar m = AreaMesh(fn (t, x) [x, cos(t), sin(t)], 0...2*Pi:2*Pi/40, -L..L:0.125, closed=[true,false])\n\n// Add translational symmetry to the exterior vertices\nvar s = Selection(m, fn (x,y,z) abs(x-L)<1e-10 || abs(x+L)<1e-10)\n\nvar t = Translate(Matrix([2*L,0,0]))\nm.addsymmetry(t, s)\n\n// Mean Squared curvature\nvar lmc = MeanCurvatureSq()\n\nprint (lmc.total(m)-6.27673)<1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/nematic/nematic.morpho",
    "content": "// Test nematic\n\nimport meshtools\nimport optimize\n\nvar m = Mesh(\"square.mesh\")\n\nvar nn = Field(m, Matrix([1,0,0]))\n\n//var psi = 0.2\n//nn[2]=Matrix([cos(psi),0,sin(psi)])\n//nn[3]=Matrix([cos(psi),0,sin(psi)])\nnn[0]=Matrix([1/sqrt(2),1/sqrt(2),0])\nnn[2]=Matrix([1/sqrt(2),1/sqrt(2),0])\nnn[3]=Matrix([0,1,0])\n\nvar lnn = Nematic(nn)\n\nprint lnn.integrand(m)\n// expect: [ 0.288706 0.288706 ]\n\nprint lnn.total(m)\n// expect: 0.577411\n\n\nvar grad = lnn.gradient(m)\nvar value = Matrix([[ -0.288705, -0.288706, 0.288705, 0.288706], [ -0.288705, 0.288706, 0.288705, -0.288706], [ 0, 0, 0, 0]])\nvar diff = grad-value\nprint diff.norm()<1e-5\n// expect: true\n\nvar fg=lnn.fieldgradient(nn,m)\nfor (x in fg) print x\n// expect: [ -0.275634 ]\n// expect: [ -0.275634 ]\n// expect: [ 0 ]\n// expect: [ 0.337521 ]\n// expect: [ -0.676777 ]\n// expect: [ 0 ]\n// expect: [ 0.724366 ]\n// expect: [ 0.724366 ]\n// expect: [ 0 ]\n// expect: [ -0.676777 ]\n// expect: [ 0.337521 ]\n// expect: [ 0 ]\n"
  },
  {
    "path": "test/functionals/nematic/nematic3d.morpho",
    "content": "// Test nematic\n\nvar tol = 1e-6\n\nvar m = Mesh(\"tetrahedron.mesh\")\n\nvar nn = Field(m, Matrix([1,0,0]))\n\nnn[0]=Matrix([1/sqrt(2),1/sqrt(2),0])\nnn[2]=Matrix([1/sqrt(2),0,1/sqrt(2)])\nnn[3]=Matrix([0,1,0])\n\nvar lnn = Nematic(nn)\n\nvar expected = 0.192873\nvar tol = 1e-5\n\nprint abs(lnn.integrand(m)[0]-expected)<tol\n// expect: true\n\nprint abs(lnn.total(m)-expected)<tol\n// expect: true\n\nexpected = Matrix([ [ 0.00681066, -0.0122614, 0.127771, -0.122321 ],\n                    [ 0.146216, -0.187767, 0.104294, -0.0627433 ],\n                    [ 0.184703, -0.13233, 0.0545992, -0.106972 ] ])\n\nprint (lnn.gradient(m)-expected).norm()<tol\n// expect: true\n\nexpected =  [ [ 0.100994, 0.0116086, -0.173546 ], \n            [ 0.231387, 0.117958, 0.0782424 ],\n            [ 0.00116726, -0.182391, 0.127534 ],\n            [ -0.156362, 0.178114, 0.0196655 ] ]\n\nvar fg=lnn.fieldgradient(nn,m)\nfor (x,k in fg) print (x-Matrix(expected[k])).norm()<tol\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n"
  },
  {
    "path": "test/functionals/nematic/nematicdim.morpho",
    "content": "import meshtools \n\n// Mesh embedded in 2D\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\nvar m = mb.build() \n\nvar nn = Field(m, Matrix([1,0,0]))\nnn[0,1]=Matrix([1/sqrt(3),1/sqrt(3),1/sqrt(3)])\nnn[0,2]=Matrix([0,0,1])\n\nvar lnem = Nematic(nn)\nvar Ea=lnem.total(m)\n\n// Mesh embedded in 3D\nvar mb2 = MeshBuilder() \nmb2.addvertex([0,0,0])\nmb2.addvertex([1,0,0])\nmb2.addvertex([0,1,0])\nmb2.addface([0,1,2])\nvar m2 = mb2.build() \n\nvar nn2 = Field(m2, Matrix([1,0,0]))\nnn2[0,1]=Matrix([1/sqrt(3),1/sqrt(3),1/sqrt(3)])\nnn2[0,2]=Matrix([0,0,1])\n\nvar lnem2 = Nematic(nn2)\nvar Eb=lnem2.total(m2)\n\nprint abs(Ea-Eb)<1e-8 // expect: true\n\nprint abs(Nematic(nn2, ksplay=1, ktwist=0, kbend=0).total(m2)-Nematic(nn, ksplay=1, ktwist=0, kbend=0).total(m))<1e-8 // expect: true\nprint abs(Nematic(nn2, ksplay=0, ktwist=1, kbend=0).total(m2)-Nematic(nn, ksplay=0, ktwist=1, kbend=0).total(m))<1e-8 // expect: true\nprint abs(Nematic(nn2, ksplay=0, ktwist=0, kbend=1).total(m2)-Nematic(nn, ksplay=0, ktwist=0, kbend=1).total(m))<1e-8 // expect: true\n\nprint (lnem.gradient(m)-lnem2.gradient(m2)[0..1,0..2]).norm()<1e-8 // expect: true\n\nprint (lnem.fieldgradient(nn)-lnem2.fieldgradient(nn2)).linearize().norm()<1e-8 // expect: true"
  },
  {
    "path": "test/functionals/nematic/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1.0 0 0\n3 1.0 1.0 0\n4 0.0 1.0 0\n\nfaces\n\n1 1 2 3\n2 1 3 4\n"
  },
  {
    "path": "test/functionals/nematic/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/functionals/nematicelectric/nematicelectric.morpho",
    "content": "// Nematic-Electric coupling\n\nimport meshtools\nimport plot\nimport \"../numericalderivatives.morpho\"\n\nvar L = 1\nvar np = 10\n\n// Create mesh and fields\n\nvar b = MeshBuilder()\nb.addvertex(Matrix([0,0,0]))\nb.addvertex(Matrix([1,0,0]))\nb.addvertex(Matrix([0,1,0]))\nb.addface([0,1,2])\n\nvar m = b.build()\n\nvar nn = Field(m, Matrix([1/sqrt(2),1/sqrt(2),0])) // Nematic director\nvar phi = Field(m, 0) // Electric potential\nphi[0,0] = 0\nphi[0,1] = 1\nphi[0,2] = 0\n\nvar lne = NematicElectric(nn, phi)\n\nprint lne.integrand(m)\n// expect: [ 0.25 ]\n\nprint abs(lne.total(m) - 0.25) < 1e-8\n// expect: true\n\nprint (lne.gradient(m)-numericalgradient(lne, m)).norm() < 1e-6\n// expect: true\n\nprint lne.fieldgradient(nn)\n// expect: <Field>\n// expect: [ 0.235702 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0.235702 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0.235702 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n\nprint lne.fieldgradient(phi)\n// expect: <Field>\n// expect: [ -1 ]\n// expect: [ 0.5 ]\n// expect: [ 0.5 ]\n"
  },
  {
    "path": "test/functionals/normsq/normsq.morpho",
    "content": "// Test norm sq\n\nimport meshtools\nimport shapeopt\n\nvar m = Mesh(\"triangle.mesh\")\n\nvar nn = Field(m, Matrix([1,1,1/2]))\n\nvar lnn = NormSq(nn)\n\nprint lnn.integrand(m)\n// expect: [ 2.25 2.25 2.25 ]\n\nprint lnn.total(m)\n// expect: 6.75\n\nprint lnn.gradient(m)\n// expect: [ 0 0 0 ]\n// expect: [ 0 0 0 ]\n// expect: [ 0 0 0 ]\n\nvar fg = lnn.fieldgradient(m)\n\nfor (f in fg) print f\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 2 ]\n// expect: [ 1 ]\n"
  },
  {
    "path": "test/functionals/normsq/triangle.mesh",
    "content": "vertices\n\n1 1 0 0\n2 -0.5 0.866025 0\n3 -0.5 -0.866025 0\n\nfaces\n\n1 1 2 3\n"
  },
  {
    "path": "test/functionals/numericalderivatives.morpho",
    "content": "// Numerical derivatives to test functionals\n\nfn numericalgradient(func, m, eps=1e-8) {\n   var x=m.vertexmatrix()\n   var grad=x.clone()\n   var dim = x.dimensions()[0]\n\n   for (i in 0...m.count()) {\n      for (j in 0...dim) {\n         var temp = x[j,i]\n         x[j,i]=temp+eps\n         var fr=func.total(m)\n         x[j,i]=temp-eps\n         var fl=func.total(m)\n         x[j,i]=temp\n         grad[j,i]=(fr-fl)/(2*eps)\n      }\n   }\n\n   return grad\n}\n\nfn numericalfieldgradient(func, m, f, eps=1e-8) {\n    var x=f.__linearize()\n    var grad=f.clone() \n    var g=grad.__linearize()\n\n    for (i in 0...x.count()) {\n        var temp = x[i]\n        x[i]=temp+eps\n        var fr=func.total(m)\n        x[i]=temp-eps\n        var fl=func.total(m)\n        x[i]=temp\n        g[i]=(fr-fl)/(2*eps)\n    }\n\n    return grad\n}\n\nfn _hessianelement(func, m, x, i, j, k, l, dim, eps) {\n    var s = i*dim+k, t = j*dim+l\n\n    if (i==j && k==l) {\n      var temp=x[k,i]\n      var fc=func.total(m)\n      x[k,i]=temp+eps\n      var fr=func.total(m)\n      x[k,i]=temp-eps\n      var fl=func.total(m)\n      x[k,i]=temp\n      return (fr + fl - 2*fc)/(eps^2)\n    }\n\n    var tempi=x[k,i], tempj=x[l,j]\n    x[k,i]=tempi+eps\n    x[l,j]=tempj+eps\n    var frr=func.total(m)\n    x[k,i]=tempi-eps\n    x[l,j]=tempj-eps\n    var fll=func.total(m)\n    x[k,i]=tempi-eps\n    x[l,j]=tempj+eps\n    var flr=func.total(m)\n    x[k,i]=tempi+eps\n    x[l,j]=tempj-eps\n    var frl=func.total(m)\n    x[k,i]=tempi\n    x[l,j]=tempj\n\n    return (frr + fll - flr - frl)/(4*eps^2)\n}\n\nfn numericalhessian(func, m, eps=1e-3) {\n  var x=m.vertexmatrix()\n  var dim = x.dimensions()\n  var hess = Matrix(dim[0]*dim[1], dim[0]*dim[1])\n\n  for (i in 0...m.count()) {\n    for (j in 0...m.count()) {\n      for (k in 0...dim[0]) {\n        for (l in 0...dim[0]) {\n          hess[i*dim[0]+k, j*dim[0]+l]=_hessianelement(func, m, x, i, j, k, l, dim[0], eps)\n        }\n      }\n    }\n  }\n\n  return hess\n}\n"
  },
  {
    "path": "test/functionals/on_selection.morpho",
    "content": "// Test functional calculations on a selection\n\nvar m = Mesh(\"square.mesh\")\n\nfn f(x,y,z) {\n  return y>0.5\n}\n\nvar s = Selection(m, f)\nprint s // expect: <Selection>\n\nfn phi(x,y,z) {\n  return x+y+z\n}\n\nfn gradphi(x,y,z) {\n  return Matrix([1,1,1])\n}\n\nvar lp = ScalarPotential(phi, gradphi)\n\nprint lp.integrand(m)\n// expect: [ 0 1 1 2 ]\n\nprint lp.integrand(m, s)\n// expect: [ 0 0 1 2 ]\n\nprint lp.total(m)\n// expect: 4\n\nprint lp.total(m, s)\n// expect: 3\n\nprint lp.gradient(m)\n// expect: [ 1 1 1 1 ]\n// expect: [ 1 1 1 1 ]\n// expect: [ 1 1 1 1 ]\n\nprint lp.gradient(m, s)\n// expect: [ 0 0 1 1 ]\n// expect: [ 0 0 1 1 ]\n// expect: [ 0 0 1 1 ]\n"
  },
  {
    "path": "test/functionals/relax.morpho",
    "content": "\nvar m = Mesh(\"cube.mesh\")\nvar la = Area()\nvar lv = VolumeEnclosed()\n\nprint m\n// expect: <Mesh: 14 vertices>\n\nprint \"Area: ${la.total(m)} Volume: ${lv.total(m)}\"\n// expect: Area: 6 Volume: 1\n\nvar fa = la.gradient(m)\nvar fv = lv.gradient(m)\n\nprint fa\n// expect: [ -1 1 -1 1 -1 1 -1 1 0 0 0 0 0 0 ]\n// expect: [ -1 -1 1 1 -1 -1 1 1 0 0 0 0 0 0 ]\n// expect: [ -1 -1 -1 -1 1 1 1 1 0 0 0 0 0 0 ]\nprint fv\n// expect: [ -0.166667 0.166667 -0.166667 0.166667 -0.166667 0.166667 -0.166667 0.166667 0 0 0 0 -0.333333 0.333333 ]\n// expect: [ -0.166667 -0.166667 0.166667 0.166667 -0.166667 -0.166667 0.166667 0.166667 0 0 -0.333333 0.333333 0 0 ]\n// expect: [ -0.166667 -0.166667 -0.166667 -0.166667 0.166667 0.166667 0.166667 0.166667 -0.333333 0.333333 0 0 0 0 ]\n\nvar ainnerv=fa.inner(fv)\nvar vinnerv=fv.inner(fv)\nprint \"${ainnerv}, ${vinnerv}\"\n// expect: 4, 1.33333\n\nvar ft=fa.clone()\nft.acc(-ainnerv/vinnerv, fv)\n\nprint ft\n// expect: [ -0.5 0.5 -0.5 0.5 -0.5 0.5 -0.5 0.5 0 0 0 0 1 -1 ]\n// expect: [ -0.5 -0.5 0.5 0.5 -0.5 -0.5 0.5 0.5 0 0 1 -1 0 0 ]\n// expect: [ -0.5 -0.5 -0.5 -0.5 0.5 0.5 0.5 0.5 1 -1 0 0 0 0 ]\n\nvar v = m.vertexmatrix()\n\nv.acc(-0.01,ft)\n\nprint la.total(m)\nprint lv.total(m)\n// expect: 5.8833\n// expect: 0.999702\n"
  },
  {
    "path": "test/functionals/relax2.morpho",
    "content": "/* Test relaxation problem */\n\nvar m = Mesh(\"cube.mesh\")\nvar la = Area()\nvar lv = VolumeEnclosed()\n\nvar vtarget = 1\nvar vtol = 1e-10\n\nvar ftol = 1e-10\nvar etol = 1e-16\n\nvar stepsize = 0.1\n\nvar v = m.vertexmatrix()\nvar energy = la.total(m)\n\nfor (i in 1..40) {\n  var fa = la.gradient(m)\n  var fv = lv.gradient(m)\n\n  // Subtract component of fa parallel to the constraint\n  var lambda=fa.inner(fv)/fv.inner(fv)\n  var ft=fa.clone()\n  ft.acc(-lambda, fv)\n\n  // Move downhill\n  v.acc(-stepsize, ft)\n\n  // Reproject onto volume constraint\n  var dv=vtarget-lv.total(m);\n  do {\n    fv=lv.gradient(m)\n    var normfv=fv.inner(fv)\n    v.acc(dv/normfv,fv)\n    dv=vtarget-lv.total(m)\n  } while (abs(dv)>vtol)\n\n  // Monitor energy, change in energy and the norm of the force\n  var newenergy=la.total(m)\n  var de = abs(newenergy-energy)\n  var fnorm = sqrt(ft.inner(ft))\n\n  // print \"Iteration ${i}. Area: ${la.total(m)} Volume: ${lv.total(m)}\"\n  // print \"              delta E: ${de} |force|: ${fnorm}\"\n\n  if (de<etol || fnorm<ftol) break\n\n  energy=newenergy\n}\n\nprint (energy-5.11249)<1e-4\n// expect: true\n\n//m.save(\"cubeout.mesh\")\n"
  },
  {
    "path": "test/functionals/scalarpotential/scalarpotential.morpho",
    "content": "\nvar m = Mesh(\"tetrahedron.mesh\")\n\nfn phi(x,y,z) {\n  return x^2+y^2+z^2\n}\n\nfn gradphi(x,y,z) {\n  return Matrix([2*x,2*y,2*z])\n}\n\nvar a = ScalarPotential(phi, gradphi)\nprint a.function\n// expect: <fn phi>\n\nprint a.integrand(m)\n// expect: [ 0.374999 0.375 0.375 0.375 ]\n\nprint a.total(m)\n// expect: 1.5\n\nprint a.gradient(m)\n// expect: [ 0 -0.57735 -0.57735 1.1547 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 1.22474 -0.408248 -0.408248 -0.408248 ]\n"
  },
  {
    "path": "test/functionals/scalarpotential/scalarpotential_hessian.morpho",
    "content": "\nimport \"../numericalderivatives.morpho\"\n\nvar m = Mesh(\"tetrahedron.mesh\")\n\nfn phi(x,y,z) {\n  return x^2+y^2+0.5*z^2 + x*y\n}\n\nvar a = ScalarPotential(phi)\n\nvar h = a.hessian(m) \nvar h2 = numericalhessian(a, m)\n\nprint (Matrix(h) - h2).norm() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/scalarpotential/scalarpotential_ndiff.morpho",
    "content": "\nvar m = Mesh(\"tetrahedron.mesh\")\n\nfn phi(x,y,z) {\n  return x^2+y^2+z^2\n}\n\nfn gradphi(x,y,z) {\n  return Matrix([2*x,2*y,2*z])\n}\n\nvar a = ScalarPotential(phi)\n\nvar mc = Matrix([ 0.374999, 0.375, 0.375, 0.375 ])\nvar mi = Matrix(4)\n\nfor (k in 0...m.count()) {\n  var x = m.vertexposition(k)\n  mi[k]=phi(x[0], x[1], x[2])\n}\n\nprint (mi-mc).norm()<1e-5\n// expect: true\n\nprint a.total(m)\n// expect: 1.5\n\nvar ngrad = [[ 0, -0.57735, -0.57735, 1.1547 ],\n             [ 0, -1, 1, 0 ],\n             [ 1.22474, -0.408248, -0.408248, -0.408248 ]]\n\nprint (a.gradient(m)-Matrix(Array(ngrad))).norm() < 1e-5\n// expect: true\n"
  },
  {
    "path": "test/functionals/scalarpotential/scalarpotential_notcallable.morpho",
    "content": "// Err. when a level set function isn't callable\n\nvar a = ScalarPotential(0.4)\n// expect Error 'SclrPtFnCllbl'\n"
  },
  {
    "path": "test/functionals/scalarpotential/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/functionals/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/functionals/triangle.mesh",
    "content": "vertices\n\n1 1 0 0\n2 -0.5 0.866025 0\n3 -0.5 -0.866025 0\n\nfaces\n\n1 1 2 3\n"
  },
  {
    "path": "test/functionals/volume/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/functionals/volume/volume.morpho",
    "content": "\nvar m = Mesh(\"tetrahedron.mesh\")\nvar a = Volume()\n\nprint m\n// expect: <Mesh: 4 vertices>\n\nprint a.integrand(m)\n// expect: [ 0.117851 ]\n\nprint a.total(m)\n// expect: 0.117851\n\nprint a.gradient(m)\n// expect: [ 0 -0.0680413 -0.0680413 0.136083 ]\n// expect: [ 0 -0.117851 0.117851 0 ]\n// expect: [ 0.144338 -0.0481125 -0.0481125 -0.0481125 ]\n"
  },
  {
    "path": "test/functionals/volume/volume_hessian.morpho",
    "content": "\nimport \"../numericalderivatives.morpho\"\n\nvar m = Mesh(\"tetrahedron.mesh\")\nvar a = Volume()\n\nvar h = a.hessian(m)\nvar h2 = numericalhessian(a, m)\n\nprint (Matrix(h) - h2).norm() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/volumeenclosed/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nfaces\n\n1 1 2 3\n2 2 3 4\n3 3 4 1\n4 2 1 4\n"
  },
  {
    "path": "test/functionals/volumeenclosed/volencl.morpho",
    "content": "\nvar m = Mesh(\"tetrahedron.mesh\")\nvar a = VolumeEnclosed()\n\nprint m\n// expect: <Mesh: 4 vertices>\n\nprint a.integrand(m)\n// expect: [ 0.0294627 0.0294627 0.0294627 0.0294627 ]\n\nprint a.total(m)\n// expect: 0.117851\n\nprint a.gradient(m)\n// expect: [ 0 -0.0680413 -0.0680413 0.136083 ]\n// expect: [ 0 -0.117851 0.117851 0 ]\n// expect: [ 0.144338 -0.0481125 -0.0481125 -0.0481125 ]\n"
  },
  {
    "path": "test/functionals/volumeenclosed/volencl_hessian.morpho",
    "content": "\nimport \"../numericalderivatives.morpho\"\n\nvar m = Mesh(\"tetrahedron.mesh\")\nvar a = VolumeEnclosed()\n\nvar h = a.hessian(m)\nvar h2 = numericalhessian(a, m)\n\nprint (Matrix(h) - h2).norm() < 1e-6 // expect: true\n"
  },
  {
    "path": "test/functionals/volumeenclosed/volencl_zeroelement.morpho",
    "content": "import meshtools\nimport optimize\n\nvar vertexlist = [[0.5,0.5,0.5], [0.5,1.5,0.5], [1.5,1.5,0.5], [1.5,0.5,0.5], [0.5,0.5,1.5],[0.5,1.5,1.5], [1.5,1.5,1.5], [1.5,0.5,1.5]]\n\nvar facelist = [[0,3,2,1], [0,4,5,1], [0,3,7,4], [2,3,7,6], [4,5,6,7], [1,2,6,5]]\n\nvar m = PolyhedronMesh(vertexlist, facelist)\n\nfor (id in 0...m.count()) {\n    var x = m.vertexposition(id)\n    x -= Matrix([1,1,1])/2\n    m.setvertexposition(id, x)\n}\n\nvar problem = OptimizationProblem(m)\n\nvar la = Area()\nvar lv = VolumeEnclosed()\n\nproblem.addenergy(la)\nproblem.addconstraint(lv)\n\nvar opt = ShapeOptimizer(problem, m)\n\nopt.relax(1) // expect error 'VolEnclZero'"
  },
  {
    "path": "test/functionals/volumeintegral/grad.morpho",
    "content": "import constants\nimport meshtools\n\n// Build unit tetrahedron\nvar mb = MeshBuilder() \nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0])\nmb.addvertex([0,1,0])\nmb.addvertex([0,0,1])\nmb.addvolume([0,1,2,3])\nvar m = mb.build()\n\n// Construct a field \nvar f = Field(m, fn (x,y,z) x)\n\nprint abs(VolumeIntegral(fn (x, f) grad(f).norm(), f).total(m) - 1/6) < 1e-6\n// expect: true\n\n// Construct a field \nvar g = Field(m, fn (x,y,z) x+y+z)\nprint abs(VolumeIntegral(fn (x, f) grad(f).norm(), g).total(m) - sqrt(3)/6) < 1e-6\n// expect: true\n"
  },
  {
    "path": "test/functionals/volumeintegral/testintegrals.morpho",
    "content": "// Test that integrators give correct estimates for simple integrals \n\nimport meshtools \nimport plot \n\nvar a = AreaMesh(fn (u,v) [u,v], 0..1:0.1, 0..1:0.1)\n\nvar eps = 1e-10\n\nprint abs(AreaIntegral(fn (x) x[0]).total(a) - 1/2) < eps // expect: true\nprint abs(AreaIntegral(fn (x) x[0]*x[1]).total(a) - 1/4) < eps // expect: true\nprint abs(AreaIntegral(fn (x) (x[0]*x[1])^2).total(a) - 1/9) < eps // expect: true\n\nvar pts = []\nfor (x in 0..1:0.25) for (y in 0..1:0.25) for (z in 0..1:0.25) pts.append(Matrix([x,y,z]))\nvar m = DelaunayMesh(pts)\n\nprint abs(VolumeIntegral(fn (x) x[0]).total(m) - 1/2) < eps // expect: true\nprint abs(VolumeIntegral(fn (x) x[0]*x[1]*x[2]).total(m) - 1/8)  < eps // expect: true\nprint abs(VolumeIntegral(fn (x) (x[0]*x[1]*x[2])^2).total(m) - 1/27)  < eps // expect: true\n"
  },
  {
    "path": "test/functionals/volumeintegral/volume_integral.morpho",
    "content": "// Check volume integral \n\nimport constants\nimport plot \n\n// Build unit tetrahedron\nvar mb = MeshBuilder() \nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0])\nmb.addvertex([0,1,0])\nmb.addvertex([0,0,1])\nmb.addvolume([0,1,2,3])\nvar m = mb.build()\n\n// Construct a field \nvar f = Field(m, fn (x,y,z) y)\n\nvar neval = 0\n\n// Integrand \nfn q(x, f) { \n    neval+=1 \n    return (sin(2*Pi*x[0]*f)^2) \n}\n\nvar lv = VolumeIntegral(q, f)\nvar est = lv.total(m) \n\nprint abs(est-0.02482305536688542)<1e-8\n// expect: true\n"
  },
  {
    "path": "test/help.py",
    "content": "#!/usr/bin/env python3\n# Check for undocumented functions\n# T J Atherton May 2025\n\nimport subprocess\nimport re \nimport colored\nfrom colored import stylize\n\nmorphocmd = \"morpho6\"\n\n# Calls the morpho terminal application and perform a help query by piping into stdin\ndef morphoHelp(query):\n    result = subprocess.run([\"echo ? \"+query+\" | \"+morphocmd], shell=True, capture_output=True, text=True)\n    return result.stdout\n\n# Check if a help query failed\ndef checkResult(result):\n    return re.search(\"No help found for\",result)==None\n\n# Identify methods provided by a morpho class by using introspection\ndef morphoMethods(clss):\n    cmd = \"print \" + clss + \".respondsto()\"\n    result = subprocess.run([\"echo \\\"\"+cmd+\"\\\" | \"+morphocmd ], shell=True, capture_output=True, text=True)\n    out = result.stdout\n    if (re.search(\"Error\",out)==None): # Check the query succeeded\n        return [item.strip() for item in out.strip(\"[]\\n\").split(\",\")] # Separate into list\n    return [] \n\n\nclasses = [ \"Array\", \"Bool\", \"Complex\", \"Dictionary\", \"Error\", \"File\", \"Float\", \"Function\", \"Int\",  \"Invocation\", \"JSON\", \"List\", \"Range\", \"String\", \"System\", \"Tuple\" ]\n\nresults = []\n\nprint(\"--Begin testing---------------------\")\n\nfor clss in classes: \n    methods = morphoMethods(clss)\n    for m in methods: \n        query = clss + \".\"+m\n        success=checkResult(morphoHelp(query))\n        results.append([query, success])\n\ncountSuccess = 0\n\nfor r in results:\n    success = ( stylize(\"Passed\",colored.fg(\"green\")) if r[1] else stylize(\"Failed\",colored.fg(\"red\")))\n    print(r[0] + \":\" + success)\n    if r[1]: countSuccess+=1\n    \nprint(\"--End testing-----------------------\")\nprint(countSuccess, 'out of', len(results), 'tests passed.')\n"
  },
  {
    "path": "test/if/class_in_else.morpho",
    "content": "if (true) \"ok\" else class Foo {}\n// expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/if/class_in_then.morpho",
    "content": "\nif (true) class Foo {}\n// expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/if/dangling_else.morpho",
    "content": "// A dangling else binds to the right-most if.\nif (true) if (false) print \"bad\" else print \"good\"\n// expect: good\nif (false) if (true) print \"bad\" else print \"bad\"\n// prints nothing\n"
  },
  {
    "path": "test/if/else.morpho",
    "content": "// Evaluate the 'else' expression if the condition is false.\nif (true) print \"good\" else print \"bad\" // expect: good\nif (false) print \"bad\" else print \"good\" // expect: good\n\n// Allow block body.\nif (false) nil else { print \"block\" } // expect: block\n"
  },
  {
    "path": "test/if/fn_in_else.morpho",
    "content": "if (true) \"ok\" else fn foo() {}\n// expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/if/fn_in_then.morpho",
    "content": "if (true) fn foo() {}\n// expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/if/if.morpho",
    "content": "// Evaluate the 'then' expression if the condition is true.\nif (true) print \"good\" // expect: good\nif (false) print \"bad\"\n\n// Allow block body.\nif (true) { print \"block\" } // expect: block\n\n// Assignment in if condition.\nvar a = false\nif (a = true) print a // expect: true\n"
  },
  {
    "path": "test/if/if_in_method.morpho",
    "content": "// Detect a bug around returns enclosed in conditionals\n\nclass Foo {\n  init () {\n    self.a = 1\n  }\n\n  method() {\n    var df = self.other()\n    if (df<self.a) {\n      print \"Bloop\"\n      return nil // <--- this was previously misdetected as a final return\n    }\n  }\n\n  other() {\n    return 3\n  }\n\n}\n\nvar f = Foo()\nf.method()\n\nprint \"oops\"\n// expect: oops\n"
  },
  {
    "path": "test/if/truth.morpho",
    "content": "// False and nil are false.\nif (false) print \"bad\" else print \"false\" // expect: false\nif (nil) print \"bad\" else print \"nil\" // expect: nil\n\n// Everything else is true.\nif (true) print true // expect: true\nif (0) print 0 // expect: 0\nif (\"\") print \"empty\" // expect: empty\n"
  },
  {
    "path": "test/if/var_in_else.morpho",
    "content": "if (true) \"ok\" else var foo\n// expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/if/var_in_then.morpho",
    "content": "if (true) var foo;\n// expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/import/as.morpho",
    "content": "// Import modules into a namespace\n\nimport color as col\n\nprint col.Red\n// expect: <Color>\n\n//print Red\n"
  },
  {
    "path": "test/import/as_not_imported.morpho",
    "content": "// Import modules into a namespace\n\nimport color as col\n\nprint Red\n// expect error 'SymblUndf'\n\n"
  },
  {
    "path": "test/import/file_not_found.morpho",
    "content": "\nimport \"madeupfile.morpho\"\n// expect error: 'FlNtFnd'\n"
  },
  {
    "path": "test/import/for_clause.morpho",
    "content": "// Selectively import symbols from a module\nimport color for Color, HueMap\n\nvar gray = Color(0.5,0.5,0.5)\n\nprint gray // expect: <Color>\nprint gray.red(0.1) // expect: 0.5\nprint gray.green(0.1) // expect: 0.5\nprint gray.blue(0.1) // expect: 0.5\n"
  },
  {
    "path": "test/import/for_clause_restrict.morpho",
    "content": "// Ensure symbols not included in a for clause are NOT imported\nimport color for Color, HueMap\n\nprint Red\n// expect error: 'SymblUndf'\n"
  },
  {
    "path": "test/import/for_string_after_for.morpho",
    "content": "// Ensure whatever is after for is a symbol\n\nimport color for \"Ooops\"\n// expect error: 'ExpctSymblAftrFr'\n"
  },
  {
    "path": "test/import/import_class_extends.morpho",
    "content": "// Import a class and extend it\nimport color \n\nclass Beep is Color {\n\n}\n\nprint Beep(1,1,1) \n// expect: <Beep>\n"
  },
  {
    "path": "test/import/import_file.morpho",
    "content": "fn f(x) { print x }\n\n// You can import a file\nimport \"importtest.m\"\n\nf(cat(\"Hello\",\"Goodbye\"))\n// expect: HelloGoodbye\n"
  },
  {
    "path": "test/import/import_for_class.morpho",
    "content": "// Import for a class \nimport color for Color \n\nprint Color(1,1,1) \n// expect: <Color>\n"
  },
  {
    "path": "test/import/import_module.morpho",
    "content": "// Import everything from a distinct module...\nimport color\n\nprint Red // expect: <Color>\nprint Red.red(0.1) // expect: 1\nprint Red.green(0.1) // expect: 0\nprint Red.blue(0.1) // expect: 0\n\nvar a = GradientMap()\n\nprint a.red(0.5) // expect: 0.475\n"
  },
  {
    "path": "test/import/import_underscore.morpho",
    "content": "// Import a module with protected symbols \nimport \"underscoreimporttest.m\"\n\nprint _b\n// expect error 'SymblUndf'"
  },
  {
    "path": "test/import/importtest.m",
    "content": "// A simple test library to include \n\nfn cat(a,b) {\n  return a + b\n}\n"
  },
  {
    "path": "test/import/module_not_found.morpho",
    "content": "\nimport unicorn\n// expect error: 'MdlNtFnd'\n"
  },
  {
    "path": "test/import/multiple.morpho",
    "content": "// Import multiple libraries per line\n\nimport color, graphics, plot\n\nprint Red\n// expect: <Color>\n\nprint Graphics\n// expect: @Graphics\n\nprint plotmesh\n// expect: <fn plotmesh>"
  },
  {
    "path": "test/import/multiple_as.morpho",
    "content": "// Can only use as once in an import statement\nimport color as col as foo \n// expect error: 'ImprtMltplAs'\n"
  },
  {
    "path": "test/import/nested_import_in_module.morpho",
    "content": "// Nested imports\nimport color for Blue \n\nvar a = Blue\n\nimport \"nestedimporttest.m\"\n\nprint a\n// expect: <Color>\nprint b\n// expect: <Color>\n"
  },
  {
    "path": "test/import/nestedimporttest.m",
    "content": "// Test module that imports a module \n\nimport \"nestedimporttest2.m\"\n\nimport color \n\nvar b = Red"
  },
  {
    "path": "test/import/nestedimporttest2.m",
    "content": "// Test module that imports a module \n\nimport color for Green\n\nvar c = Green"
  },
  {
    "path": "test/import/one_for_per_line.morpho",
    "content": "// Lists of symbols after for are parsed as one\n// Hence using for requires one import per line\n\nimport color as boo for Red, histogram as hist \n// expect error 'ImprtMltplAs'\n"
  },
  {
    "path": "test/import/optional.morpho",
    "content": "// Test module that defines an optional argument\n\nfn foo(x, beep=\"Boop\") {\n  print x\n  print beep\n}\n"
  },
  {
    "path": "test/import/optional_in_module.morpho",
    "content": "\nimport \"optional.morpho\"\n\nfoo(1)\n// expect: 1\n// expect: Boop\n\nfoo(1, beep=\"BeepBeep\")\n// expect: 1\n// expect: BeepBeep\n"
  },
  {
    "path": "test/import/underscoreimporttest.m",
    "content": "// Test module with underscore\n\nvar _b = 2"
  },
  {
    "path": "test/inheritance/constructor.morpho",
    "content": "class A {\n  init(param) {\n    self.field = param\n  }\n\n  test() {\n    print self.field\n  }\n}\n\nclass B < A {}\n\nvar b = B(\"value\")\nb.test() // expect: value\n"
  },
  {
    "path": "test/inheritance/inherit_from_function.morpho",
    "content": "fn foo() {}\n\nclass Subclass < foo {} // expect error 'SprNtFnd'\n"
  },
  {
    "path": "test/inheritance/inherit_from_nil.morpho",
    "content": "// This test works a bit differently to lox because classes are not dynamic in morpho\n// It still fails, because the symbol Nil doesn't denote a class.\n\nvar Nil = nil\nclass Foo < Nil {} // expect error: 'SprNtFnd'\n"
  },
  {
    "path": "test/inheritance/inherit_from_number.morpho",
    "content": "// As for nil, this fails because Number is not a superclass\n\nvar Number = 123\nclass Foo < Number {} // expect error: 'SprNtFnd'\n"
  },
  {
    "path": "test/inheritance/inherit_methods.morpho",
    "content": "class Foo {\n  methodOnFoo() { print \"foo\" }\n  override() { print \"foo\" }\n}\n\nclass Bar is Foo {\n  methodOnBar() { print \"bar\" }\n  override() { print \"bar\" }\n}\n\nvar bar = Bar()\nbar.methodOnFoo() // expect: foo\nbar.methodOnBar() // expect: bar\nbar.override() // expect: bar\n"
  },
  {
    "path": "test/inheritance/parenthesized_superclass.morpho",
    "content": "class Foo {}\n\nclass Bar < (Foo) {}\n// expect error: 'SprNmMssng'\n"
  },
  {
    "path": "test/inheritance/set_fields_from_base_class.morpho",
    "content": "class Foo {\n  foo(a, b) {\n    self.field1 = a\n    self.field2 = b\n  }\n\n  fooPrint() {\n    print self.field1\n    print self.field2\n  }\n}\n\nclass Bar < Foo {\n  bar(a, b) {\n    self.field1 = a\n    self.field2 = b\n  }\n\n  barPrint() {\n    print self.field1\n    print self.field2\n  }\n}\n\nvar bar = Bar()\nbar.foo(\"foo 1\", \"foo 2\")\nbar.fooPrint()\n// expect: foo 1\n// expect: foo 2\n\nbar.bar(\"bar 1\", \"bar 2\")\nbar.barPrint()\n// expect: bar 1\n// expect: bar 2\n\nbar.fooPrint()\n// expect: bar 1\n// expect: bar 2\n"
  },
  {
    "path": "test/integrate/counter.morpho",
    "content": "// Class to count number of times a function is evaluated\n// Used by integration testing suite \n\nclass EvaluationCounter {\n    init(func) {\n        self.func = func\n        self.pts = []\n    }\n\n    eval(x) {\n        self.pts.append(x.clone())\n        var xx = [] \n        for (u in x) xx.append(u)\n        return apply(self.func, xx)\n    }\n\n    reset() {\n        self.pts = []\n    }\n\n    count() {\n        return self.pts.count() \n    }\n}\n"
  },
  {
    "path": "test/integrate/embedding.morpho",
    "content": "// Ensure position interpolation works correctly\n\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t],     0..1:0.5)\nvar m2 = LineMesh(fn (t) [t,t],   0..1:0.5)\nvar m3 = LineMesh(fn (t) [t,t,t], 0..1:0.5)\n\nprint abs(LineIntegral(fn (x) x[0], method={}).total(m1) - 1/2) < 1e-8\n// expect: true\n\nprint abs(LineIntegral(fn (x) x[0] + x[1], method={}).total(m2) - sqrt(2)) < 1e-8\n// expect: true\n\nprint abs(LineIntegral(fn (x) x[0] + x[1] + x[2], method={}).total(m3) - 3*sqrt(3)/2) < 1e-8\n// expect: true\n"
  },
  {
    "path": "test/integrate/integrals1d.morpho",
    "content": "// Test underlying integration routine\n\nimport meshtools\nimport \"counter.morpho\"\n\nvar mb = MeshBuilder() \nmb.addvertex([0])\nmb.addvertex([1])\nmb.addedge([0,1])\nvar m = mb.build()\n\nfn step(x) { \n    if (x<0.3) return 1\n    return 0\n}\n\n// List of integrals to evaluate and precalculated exact values \nvar tests = [ [ step, 0.3 ],\n              [ fn (x) sqrt(x), 0.66666666666666666667 ],\n              [ fn (x) x*(1-x), 0.16666666666666666667 ],\n              [ fn (x) 1/sqrt(x), 2 ],\n              [ fn (x) exp(-(x-1/2)^2), 0.92256201282558489751 ] ]\n\nvar out = []\n\nfor (t in tests) {\n    var c = EvaluationCounter(t[0]) \n    \n    var r1 = LineIntegral(c.eval, method={ \"rule\" : \"gauss5\" } ).total(m)\n\n    print (abs((r1-t[1])/t[1]))<1e-5\n}\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n\n/*\nfor (t in tests) {\n    var c = EvaluationCounter(t[0]) \n    \n    var r1 = LineIntegral(c.eval, method={ \"rule\" : \"gauss5\" } ).total(m)\n    var c1 = c.count()\n    c.reset()\n    var r2 = LineIntegral(c.eval).total(m)\n    var c2 = c.count()\n\n    out.append([r1, abs((r1-t[1])/t[1]), c1, r2, abs((r2-t[1])/t[1]), c2])\n}\n\nfor (res in out) print Array(res)\n\nvar times = []\nvar ntimes = 1000\n\nprint \"Timing tests\"\nfor (t in tests) {\n    var start = System.clock() \n    for (i in 1..ntimes) {\n        var r1 = LineIntegral(fn (x) apply(t[0], x[0]), method={ } ).total(m)\n    }\n    var time1 = System.clock()-start\n\n    start = System.clock() \n    for (i in 1..ntimes) {\n        var r2 = LineIntegral(fn (x) apply(t[0], x[0])).total(m)\n    }\n    var time2 = System.clock() - start\n    \n    times.append([time1, time2])\n}\n\nfor (t in times) print Array(t)*/"
  },
  {
    "path": "test/integrate/integrals2d.morpho",
    "content": "// Test underlying integration routine\n\nimport meshtools\nimport \"counter.morpho\"\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\nvar m = mb.build()\n\nfn circ(x,y) {\n    var rsq = (x-1/2)^2+(y-1/2)^2\n    if (rsq<=1/4) return 1\n    return 0\n}\n\n// List of integrals to evaluate and precalculated exact values \n// See ACM Transactions on Mathematical Software, Vol 8, No. 2, June 1982,Pages 210-218\nvar tests = [ [ fn (x, y) (x^2+3*y^2)^(-1/2), 0.93313202062943577716 ],\n              // [ circ, 0.39269908169872415481 ], \n              [ fn (x, y) exp(sin(x)*cos(y)), 0.691810450659522 ],\n              [ fn (x, y) x^2/(1+x^2), 0.061175426882524345093 ],\n              [ fn (x, y) sin(3*x+6*y), 0.031203084128814462056 ],\n              [ fn (x, y) sin(x+y)^(-1/2), 0.691684734029392 ] \n              ]\n\nvar out = []\n\nfor (t in tests) {\n    var c = EvaluationCounter(t[0]) \n    var r1 = AreaIntegral(c.eval, method={  } ).total(m)\n    print (abs((r1-t[1])/t[1])<1e-6) \n}\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n\n/*\nfor (t in tests) {\n    var c = EvaluationCounter(t[0]) \n    \n    var r1 = AreaIntegral(c.eval, method={  } ).total(m)\n    var c1 = c.count()\n    c.reset()\n    var r2 = AreaIntegral(c.eval).total(m)\n    var c2 = c.count()\n\n    out.append([r1, abs((r1-t[1])/t[1]), c1, r2, abs((r2-t[1])/t[1]), c2])\n}\n\nfor (res in out) print Array(res)\n\nvar times = []\nvar ntimes = 10\n\nprint \"Timing tests\"\nfor (t in tests) {\n    var start = System.clock() \n    for (i in 1..ntimes) {\n        var r1 = AreaIntegral(fn (x) apply(t[0], x[0], x[1]), method={ } ).total(m)\n    }\n    var time1 = System.clock()-start\n\n    start = System.clock() \n    for (i in 1..ntimes) {\n        var r1 = AreaIntegral(fn (x) apply(t[0], x[0], x[1]) ).total(m)\n    }\n    var time2 = System.clock() - start\n    \n    times.append([time1, time2])\n}\n\nfor (t in times) print Array(t)\n\n*/"
  },
  {
    "path": "test/integrate/integrals2d_quantities.morpho",
    "content": "// Test integration using quantities\n\nimport meshtools\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\nmb.addface([0,1,2])\nvar m = mb.build()\n\nvar f = Field(m, Matrix([0,0]))\nf[0,1]=Matrix([3,0])\nf[0,2]=Matrix([0,6])\n\nprint abs(AreaIntegral(fn (x, q) q[0]+q[1], f, method={  } ).total(m) - 1.5) < 1e-6\n// expect: true\n\nprint abs(AreaIntegral(fn (x, q) sin(q[0]+q[1]), f, method={  } ).total(m) - 0.031203084128814462056) < 1e-8\n// expect: true"
  },
  {
    "path": "test/integrate/integrals3d.morpho",
    "content": "// Test underlying integration routine\n\nimport meshtools\nimport \"counter.morpho\"\n\nvar mb = MeshBuilder() \nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0])\nmb.addvertex([0,1,0])\nmb.addvertex([0,0,1])\nmb.addvolume([0,1,2,3])\nvar m = mb.build()\n\n// List of integrals to evaluate and precalculated values from mathematica\nvar tests = [ [ fn (x, y, z) x*y*z, 1/720 ],\n              [ fn (x, y, z) (x*y*z)^2, 1/45360 ],\n              [ fn (x, y, z) (x*y*z)^3, 1/2217600 ],\n              [ fn (x, y, z) (x*y*z)^4, 1/94594500 ],\n              //[ fn (x, y, z) (x^2+3*y^2+2*z^2)^(-1/2), 0.2638364274 ],\n              [ fn (x, y, z) (1+x^2+3*y^2+2*z^2)^(-1/2), 0.1342458110 ],  \n              [ fn (x, y, z) exp(sin(x)*cos(y)*sin(2*z)), 0.1823550880 ],\n              [ fn (x, y, z) x^2/(1+x^2), 0.01324025695 ],\n              [ fn (x, y, z) sin(3*x+6*y+2*z), 0.04353873237 ]\n              //[ fn (x, y, z) sin(x+y+z)^(-1/2), 0.2097719308 ] \n              ]\n\nvar out = []\n\nfor (t in tests) {\n    var c = EvaluationCounter(t[0]) \n    \n    var r1 = VolumeIntegral(c.eval, method={ \"rule\" : \"grundmann3d1\" } ).total(m)\n\n    print (abs((r1-t[1])/t[1])<1e-6)\n}\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n\n/*\nfor (t in tests) {\n    var c = EvaluationCounter(t[0]) \n    \n    var r1 = VolumeIntegral(c.eval, method={ \"rule\" : \"grundmann1\" } ).total(m)\n    var c1 = c.count()\n    c.reset()\n    var r2 = VolumeIntegral(c.eval).total(m)\n    var c2 = c.count()\n\n    out.append([r1, abs((r1-t[1])/t[1]), c1, r2, abs((r2-t[1])/t[1]), c2])\n}\n\nfor (res in out) print Array(res)\n\n\nvar times = []\nvar ntimes = 100\n\nprint \"Timing tests\"\nfor (t in tests) {\n    var start = System.clock() \n    for (i in 1..ntimes) {\n        var r1 = VolumeIntegral(fn (x) apply(t[0], x[0], x[1], x[2]), method={ \"rule\" : \"grundmann1\" } ).total(m)\n    }\n    var time1 = System.clock()-start\n\n    start = System.clock() \n    for (i in 1..ntimes) {\n        var r1 = VolumeIntegral(fn (x) apply(t[0], x[0], x[1], x[2]) ).total(m)\n    }\n    var time2 = System.clock() - start\n    \n    times.append([time1, time2])\n}\n\nfor (t in times) print Array(t)\n*/"
  },
  {
    "path": "test/integrate/method_entry_type.morpho",
    "content": "// Raise an err if a quadrature rule can't be found\n\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t],     0..1:0.5)\n\nvar a = LineIntegral(fn (x) x[0], method={ \"rule\" : 10000 }).total(m1)\n// expect error 'IntgrtrMthdTyp'\n"
  },
  {
    "path": "test/integrate/method_not_a_dict.morpho",
    "content": "// Raise an err if the quadrature rule isn't found\n\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t],     0..1:0.5)\n\nvar a = LineIntegral(fn (x) x[0], method=\"Foo\") \n// expect error 'IntgrlMthdDct'\n"
  },
  {
    "path": "test/integrate/method_rule_not_found.morpho",
    "content": "// Raise an err if the quadrature rule isn't found\n\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t],     0..1:0.5)\n\nvar a = LineIntegral(fn (x) x[0], method={ \"rule\" : \"Foo\" }).total(m1)\n// expect error 'IntgrtrRlNtFnd'\n"
  },
  {
    "path": "test/integrate/method_rule_unavlb.morpho",
    "content": "// Raise an err if a quadrature rule can't be found\n\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t],     0..1:0.5)\n\nvar a = LineIntegral(fn (x) x[0], method={ \"degree\" : 10000 }).total(m1)\n// expect error 'IntgrtrRlUnavlb'\n"
  },
  {
    "path": "test/integrate/too_many_subdivisions.morpho",
    "content": "// Test underlying integration routine\n\nimport meshtools\nimport \"counter.morpho\"\n\nvar mb = MeshBuilder() \nmb.addvertex([0])\nmb.addvertex([1])\nmb.addedge([0,1])\nvar m = mb.build()\n\nprint LineIntegral(fn (x) 1/x[0], method={ } ).total(m)\n// expect error 'IntgrtrSbdvns' "
  },
  {
    "path": "test/invocation/invocation.morpho",
    "content": "// Invocation veneer class\n\nvar a = Object() \n\nvar b = a.respondsto\n\nprint b\n// expect: <Object>.<fn respondsto>\n\nprint b.clss() \n// expect: @Invocation\n\nprint b.superclass()\n// expect: @Object\n\nprint b.clone()(\"respondsto\") \n// expect: true\n\nvar c = Invocation(a, \"respondsto\")\n\nprint islist(c()) // expect: true\n"
  },
  {
    "path": "test/invocation/invocation_on_class.morpho",
    "content": "// Invocation veneer class\n\nvar a = Object.respondsto\n\nprint a\n// expect: @Object.<fn respondsto>\n\nprint a.clss() \n// expect: @Invocation\n\nprint islist(a()) // expect: true\n"
  },
  {
    "path": "test/json/json.morpho",
    "content": "// Parse various kinds of JSON \n\n//print JSON.parse(\"]\")//{ \\\"min\\\": -1.0e+28, \\\"max\\\": 1.0e+28 }\")\n\n//print JSON.parse(\"\\\"'\\\\u03a9'\\\"\")\n//print JSON.parse(\"\\\"'\\\\uD83E\\\\uDD8B'\\\"\")\n\n/*\nprint \"Hello\"\nfor (i in 1..100000) {\n    try {\n        //print JSON.parse(\"[[ 1, 2, 3 ], $#$%$]\")\n        print JSON.parse(\"{ \\\"Hello\\\": 1, 2: 3 }\")\n    } catch {\n        \"JSONObjctKey\":\n        \"UnrgnzdTkn\": \n    }\n}*/\n\nvar dict = JSON.parse(\"{ \\\"Hello\\\" : \\\"World\\\", \\\"Goodbye\\\" : 2 }\")\n\nprint isdictionary(dict) \n// expect: true\nprint dict[\"Hello\"]\n// expect: World\nprint dict[\"Goodbye\"]\n// expect: 2\n\nvar a = JSON.parse(\"  [ 1213, 1.2, 1.3e-02, 1.4e-03 ] \")\nfor (e in a) print e\n// expect: 1213\n// expect: 1.2\n// expect: 0.013\n// expect: 0.0014\n\nprint JSON.parse(\"   \\\"hel\\\\\\\"lo\\\"\");\n// expect: hel\"lo\n\nprint JSON.parse(\"true\")\n// expect: true\n\nprint JSON.parse(\" \\r \\t   [ true, false, null ]\");\n// expect: [ true, false, nil ]\n"
  },
  {
    "path": "test/json/testjson.morpho",
    "content": "// Automated testing of JSON parser against the test suite:\n// https://github.com/nst/JSONTestSuite\n\n// Loads a file and returns the contents as a string\nfn loadFile(fname) {\n    var f = File(fname, \"read\") \n    var out = f.readall()\n    f.close() \n    return out\n}\n\n// Runs a test, returnineg true if successful\nfn test(src) {\n    var success=false\n    try {\n        JSON.parse(src)\n        success=true\n    } catch {\n        \"DctSprtr\":\n        \"DctTrmntr\":\n        \"ExpExpr\":\n        \"JSONExtrnsTkn\":\n        \"JSONObjctKey\":\n        \"InvldUncd\":\n        \"UnescpdCtrl\":\n        \"JSONBlnkElmnt\":\n        \"JSONNmbrFrmt\":\n        \"UntrmStrng\": \n        \"UnrgnzdTkn\": \n        \"ValRng\": \n        \"MssngComma\":\n        \"MssngSqBrc\":\n        \"StrEsc\":\n        \"RcrsnLmt\":\n    }\n    return success\n}\n\n// Checks if the test passed by looking at the filename, which should begin \n// with 'y' - parse must succeed\n//      'i' - parser can either accept or reject the contents\n//      'n' - parse must raise an error\nfn passq(fname, result) {\n    var expect = fname[0]\n    if (fname[0]==\"y\" && result==true) return \"Passed\" \n    if (fname[0]==\"n\" && result==false) return \"Passed\" \n    if (fname[0]==\"i\") return \"Passed\" \n    return \"Failed\"\n}\n\n// Call this script with a folder of test files to examine\nvar args = System.arguments()\nif (args.count()==0) System.exit() \nvar folder = args[0]\n\nif (Folder.isfolder(folder)) {\n    var files = Folder.contents(folder)\n    print \"Analyzing ${files.count()} files.\"\n    var pass=0\n\n    for (fname, k in files) { // Run tests and report results \n        var src = loadFile(folder+\"/\"+fname)\n        var outcome = test(src)\n        var passed = passq(fname, outcome)\n        if (passed==\"Passed\") pass+=1\n        if (passed==\"Failed\") print \"${k}: ${passed} [${outcome}] [${fname}] ${src}\"\n    }\n\n    print \"Test suite complete: ${pass}/${files.count()} passed.\"\n}"
  },
  {
    "path": "test/json/tostring.morpho",
    "content": "// Output morpho values as JSON\n  \nvar vals = [1, 1.5, true, false, nil, \"hello world\", [1,2,3]]\n\nfor (v in vals) print JSON.tostring(v)\n// expect: 1\n// expect: 1.5\n// expect: true\n// expect: false\n// expect: null\n// expect: \"hello world\"\n// expect: [1,2,3]\n\nvar str = [\"\\b\", \"\\f\", \"\\n\", \"\\t\", \"\\r\", \"\\u0001\", \"👋 world\"]\n\nfor (v in str) print JSON.tostring(v)\n// expect: \"\\b\"\n// expect: \"\\f\"\n// expect: \"\\n\"\n// expect: \"\\t\"\n// expect: \"\\r\"\n// expect: \"\\u0001\"\n// expect: \"👋 world\"\n\nvar dict = { \"Foo\": \"Bar\", \"Boo\": 2, \"Woo\": true, \"false\": true }\n\nvar dstr = JSON.tostring(dict)\nvar dict2 = JSON.parse(dstr)\n\nfor (key in dict.keys()) {\n    print dict2[key] == dict[key]\n}\n// expect: true\n// expect: true\n// expect: true\n// expect: true"
  },
  {
    "path": "test/junk/ctrl.morpho",
    "content": "{\u0004\n// expect error 'UnrgnzdTkn'"
  },
  {
    "path": "test/junk/makectrl.xmorpho",
    "content": "var str = \"{\\x04\"\n\nvar f = File(\"ctrl.morpho\", \"w\")\nf.write(str)\nf.close()"
  },
  {
    "path": "test/junk/oeq0.morpho",
    "content": "o=0\n// expect error 'SymblUndf'"
  },
  {
    "path": "test/list/empty_index.morpho",
    "content": "// Ensure blank indices are caught\n\nvar a = [1,2,3]\n\nprint a[]\n// expect error 'MssngIndx'"
  },
  {
    "path": "test/list/index_out_of_bounds.morpho",
    "content": "// Check indices out of bounds\n\nvar lst = [ 1,2,3 ]\nprint lst[0] // expect: 1\nprint lst[1] // expect: 2\nprint lst[6] // expect error 'IndxBnds'\n"
  },
  {
    "path": "test/list/inherited.morpho",
    "content": "// Test List methods inherited from Object \n\nvar lst = [ 1, 2 ]\n\nprint lst.respondsto(\"contains\") // expect: true\n\nprint islist(lst.respondsto()) // expect: true\n\nprint lst.clss() // expect: @List\n\nprint lst.superclass() // expect: @Object\n\nprint islist(lst.invoke(\"respondsto\")) // expect: true\n\nprint lst.has(\"a\") // expect: false\n\nprint lst.count() // expect: 2"
  },
  {
    "path": "test/list/initializer_with_enumerable.morpho",
    "content": "// Initialize a list with a range\n\nprint List(1..5)\n// expect: [ 1, 2, 3, 4, 5 ]\n\nvar a = [0...1:0.2]\nprint a\n// expect: [ 0, 0.2, 0.4, 0.6, 0.8 ]\n"
  },
  {
    "path": "test/list/initializer_with_vars.morpho",
    "content": "// Initialize a list with vars\nfor (i in 1..3) {\n  var a = [ i-1, i, i+1]\n  print a\n}\n\n// expect: [ 0, 1, 2 ]\n// expect: [ 1, 2, 3 ]\n// expect: [ 2, 3, 4 ]\n"
  },
  {
    "path": "test/list/insert.morpho",
    "content": "// Check insert method\n\nvar a = [ 1, 2 ]\n\na.insert(0, 0)\nprint a // expect: [ 0, 1, 2 ]\n\na.insert(1, 0.5)\nprint a // expect: [ 0, 0.5, 1, 2 ]\n\na.insert(2, 0.6, 0.7, 0.8, 0.9)\nprint a // expect: [ 0, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 2 ]\n\na.insert(-2, 1.5)\nprint a // expect: [ 0, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.5, 2 ]\n\na.insert(-1, 2.5)\nprint a // expect: [ 0, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.5, 2, 2.5 ]\n\nvar b = []\nfor (i in 1..20) b.insert(0, i)\nprint b\n// expect: [ 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]\n"
  },
  {
    "path": "test/list/ismember.morpho",
    "content": "// Check ismember method on a list\n\nprint [1,2,3].ismember(1)\n// expect: true\n\nprint [1,2,3].ismember(5)\n// expect: false\n\nvar a = Object()\nvar b = Object()\nvar c = Object()\n\nprint [a,b].ismember(a)\n// expect: true\n\nprint [a,b].ismember(c)\n// expect: false\n"
  },
  {
    "path": "test/list/join.morpho",
    "content": "// Join lists\n\nprint [ 1, 2 ].join([ 3, 4 ])\n// expect: [ 1, 2, 3, 4 ]\n"
  },
  {
    "path": "test/list/list_from_tuple.morpho",
    "content": "// Convert a Tuple into a List\n\nvar lst = Tuple(1,2,3)\nprint List(lst) // expect: [ 1, 2, 3 ]\n"
  },
  {
    "path": "test/list/nonint_index.mopho",
    "content": "var a = [1,2,3]\nprint a[[1]] // expect: error NonintIndex\n"
  },
  {
    "path": "test/list/order.morpho",
    "content": "// Test List order method\n\nimport constants\n\nvar lst = [ 3,2,1 ]\nprint lst\n// expect: [ 3, 2, 1 ]\nprint lst.order()\n// expect: [ 2, 1, 0 ]\nprint lst.sort() //\n// expect: nil\nprint lst\n// expect: [ 1, 2, 3 ]\n\nvar a = []\nfor (i in 0..2*Pi:Pi/8) a.append(cos(i)*(1+1e-10*i)) // add a bit to avoid equal values for ambiguity in ordering\n\nprint a\n// expect: [ 1, 0.92388, 0.707107, 0.382683, 6.12323e-17, -0.382683, -0.707107, -0.92388, -1, -0.92388, -0.707107, -0.382683, -1.83697e-16, 0.382683, 0.707107, 0.92388, 1 ]\nvar o = a.order()\nprint o\n// expect: [ 8, 9, 7, 10, 6, 11, 5, 12, 4, 3, 13, 2, 14, 1, 15, 0, 16 ]\n\nvar y = []\nfor (i in 0...a.count()) y.append(a[o[i]])\nprint y\n// expect: [ -1, -0.92388, -0.92388, -0.707107, -0.707107, -0.382683, -0.382683, -1.83697e-16, 6.12323e-17, 0.382683, 0.382683, 0.707107, 0.707107, 0.92388, 0.92388, 1, 1 ]\na.sort()\nprint a\n// expect: [ -1, -0.92388, -0.92388, -0.707107, -0.707107, -0.382683, -0.382683, -1.83697e-16, 6.12323e-17, 0.382683, 0.382683, 0.707107, 0.707107, 0.92388, 0.92388, 1, 1 ]\n"
  },
  {
    "path": "test/list/pop_bounds.morpho",
    "content": "// Pop a negative number\n\nvar a = [1,2,3,4,5,6,7,8,9,10]\n\na.pop(50)\n// expect error 'IndxBnds'"
  },
  {
    "path": "test/list/pop_index.morpho",
    "content": "// Check pop method with index on a list\n\nvar a = [ 1, 2, 3, 4, 5, 6 ]\n\nprint a.pop(0) // expect: 1\nprint a // expect: [ 2, 3, 4, 5, 6 ]\n\nprint a.pop(2) // expect: 4\nprint a // expect: [ 2, 3, 5, 6 ]\n\nprint a.pop(3) // expect: 6\nprint a // expect: [ 2, 3, 5 ]\n\nprint a.pop(1) // expect: 3\nprint a // expect: [ 2, 5 ]\n\nprint a.pop() // expect: 5\nprint a // expect: [ 2 ]\n\nprint a.pop() // expect: 2\nprint a // expect: [  ]\n\nprint a.pop() // expect: nil\n"
  },
  {
    "path": "test/list/pop_negative.morpho",
    "content": "// Pop a negative number\n\nvar a = [1,2,3,4,5,6,7,8,9,10]\n\nprint a.pop(-1)\n// expect: 10\n\nfor (i in 1..100) {\n  a.append(i)\n\n  a.pop(-1)\n}\n\nprint \"ok\"\n// expect: ok"
  },
  {
    "path": "test/list/remove.morpho",
    "content": "// Check remove method on a list\n\nvar a = [ 1, 2, 3 ]\n\na.remove(2)\n\nprint a\n// expect: [ 1, 3 ]\n\nvar b = [ \"Ham\", \"Sausage\", \"Eggs\"]\nb.remove(\"Eggs\")\n\nprint b\n// expect: [ Ham, Sausage ]\n\na.remove(4)\n// expect error: 'EntryNtFnd'\n"
  },
  {
    "path": "test/list/reverse.morpho",
    "content": "// Reverse a list\n\n// Odd length\nvar lst = [ 1, 2, 3 ]\nlst.reverse()\nprint lst\n// expect: [ 3, 2, 1 ]\n\nvar lst = [ 1, 2, 3, 4 ]\nlst.reverse()\nprint lst\n// expect: [ 4, 3, 2, 1 ]"
  },
  {
    "path": "test/list/roll.morpho",
    "content": "// Roll a list \n\nvar lst = [ 1,2,3 ]\n\nfor (n in -4..4) {\n    print lst.roll(n)\n}\n\n// expect: [ 2, 3, 1 ]\n// expect: [ 1, 2, 3 ]\n// expect: [ 3, 1, 2 ]\n// expect: [ 2, 3, 1 ]\n// expect: [ 1, 2, 3 ]\n// expect: [ 3, 1, 2 ]\n// expect: [ 2, 3, 1 ]\n// expect: [ 1, 2, 3 ]\n// expect: [ 3, 1, 2 ]\n\n"
  },
  {
    "path": "test/list/sort_with_function.morpho",
    "content": "// Test List sort with function\n\nfn cmp(a, b) {\n  return -(a-b)\n}\n\nvar lst = [ 4, 3, 2, 7, 8, 1, 10, 9, 6, 5 ]\nlst.sort(cmp)\n\nprint lst\n// expect: [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]\n"
  },
  {
    "path": "test/list/sort_with_function_flt.morpho",
    "content": "// Test List sort with function\n\nfn cmp(a, b) {\n  return -(a-b)\n}\n\nvar lst = [ 4.5, 3.5, 2.5, 7.5, 5.5, 8.5, 1.5, 10.5, 9.5, 6.5 , 5.5 ]\nlst.sort(cmp)\n\nprint lst\n// expect: [ 10.5, 9.5, 8.5, 7.5, 6.5, 5.5, 5.5, 4.5, 3.5, 2.5, 1.5 ]\n"
  },
  {
    "path": "test/list/syntax.morpho",
    "content": "// Test List veneer class\n\nvar lst = [ 1,2,3 ]\nprint lst[0] // expect: 1\nprint lst[1] // expect: 2\nprint lst[2] // expect: 3\nprint lst.count() // expect: 3\n\n// Empty list\nvar a = []\nprint a\n// expect: [  ]\n\nfor (i in 1..5) a.append(i)\n\nprint a\n// expect: [ 1, 2, 3, 4, 5 ]\n\nprint a.pop()\n// expect: 5\nprint a.pop()\n// expect: 4\n\nprint a.count()\n// expect: 3\n\nprint a\n// expect: [ 1, 2, 3 ]\n\na.append(\"Hello\")\nprint a\n// expect: [ 1, 2, 3, Hello ]\n"
  },
  {
    "path": "test/list/tuples.morpho",
    "content": "// Generate sets and tuples from a list\n\nvar lst = [ 1,2,3 ]\nfor (x in lst.sets(2)) print x\n// expect: [ 1, 2 ]\n// expect: [ 1, 3 ]\n// expect: [ 2, 3 ]\n\nvar lst = [ 1,2,3,4 ]\nfor (x in lst.sets(3)) print x\n// expect: [ 1, 2, 3 ]\n// expect: [ 1, 2, 4 ]\n// expect: [ 1, 3, 4 ]\n// expect: [ 2, 3, 4 ]\n\nfor (x in lst.sets(5)) print x\n// expect: [ 1, 2, 3, 4 ]\n\nfor (x in [ 1, 2, 3].tuples(2)) print x\n// expect: [ 1, 1 ]\n// expect: [ 1, 2 ]\n// expect: [ 1, 3 ]\n// expect: [ 2, 1 ]\n// expect: [ 2, 2 ]\n// expect: [ 2, 3 ]\n// expect: [ 3, 1 ]\n// expect: [ 3, 2 ]\n// expect: [ 3, 3 ]\n"
  },
  {
    "path": "test/logical/and.morpho",
    "content": "// Note: These tests implicitly depend on ints being truthy.\n\n// Return the first non-true argument.\nprint false and 1 // expect: false\nprint true and 1 // expect: 1\nprint 1 and 2 and false // expect: false\n\n// Return the last argument if all are true.\nprint 1 and true // expect: true\nprint 1 and 2 and 3 // expect: 3\n\n// Short-circuit at the first false argument.\nvar a = \"before\"\nvar b = \"before\"\n(a = true) and\n    (b = false) and\n    (a = \"bad\")\nprint a // expect: true\nprint b // expect: false\n"
  },
  {
    "path": "test/logical/and_truth.morpho",
    "content": "// False and nil are false.\nprint false and \"bad\" // expect: false\nprint nil and \"bad\" // expect: nil\n\n// Everything else is true.\nprint true and \"ok\" // expect: ok\nprint 0 and \"ok\" // expect: ok\nprint \"\" and \"ok\" // expect: ok\n\n// Everything else is true.\nprint true and 0.1 // expect: 0.1\nprint 0 and Sparse(1) // expect: [ 0 ]\nprint \"\" and List() // expect: [  ]\n"
  },
  {
    "path": "test/logical/or.morpho",
    "content": "// Note: These tests implicitly depend on ints being truthy.\n\n// Return the first true argument.\nprint 1 or true // expect: 1\nprint false or 1 // expect: 1\nprint false or false or true // expect: true\n\n// Return the last argument if all are false.\nprint false or false // expect: false\nprint false or false or false // expect: false\n\n// Short-circuit at the first true argument.\nvar a = \"before\"\nvar b = \"before\"\n(a = false) or\n    (b = true) or\n    (a = \"bad\")\nprint a // expect: false\nprint b // expect: true\n"
  },
  {
    "path": "test/logical/or_truth.morpho",
    "content": "// False and nil are false.\nprint false or \"ok\" // expect: ok\nprint nil or \"ok\" // expect: ok\n\n// Everything else is true.\nprint true or \"ok\" // expect: true\nprint 0 or \"ok\" // expect: 0\nprint \"s\" or \"ok\" // expect: s\n"
  },
  {
    "path": "test/math/isinf_isnan_isfinite.morpho",
    "content": "print isnan(1) // expect: false\nprint isnan(0/0) // expect: true\nprint isnan(1.0) // expect: false\nprint isnan(-1.0) // expect: false\nprint isnan(0) // expect: false\nprint isnan(1/0) // expect: false\n\nprint isinf(1/0)\n// expect: true\nprint isinf(-(1/0))\n// expect: true\nprint isinf(0)\n// expect: false\nprint isinf(0.0)\n// expect: false\nprint isinf(2)\n// expect: false\nprint isinf(0/0)\n// expect: false\n\n\nprint isfinite(1/0) // expect: false\nprint isfinite(-(1/0)) // expect: false\nprint isfinite(0) // expect: true\nprint isfinite(0.0) // expect: true\nprint isfinite(2) // expect: true\nprint isfinite(0/0) // expect: false\n"
  },
  {
    "path": "test/math/math.morpho",
    "content": "// Test math functions\n\nprint exp(2)\n// expect: 7.38906\n\nprint log(10)\n// expect: 2.30259\n\nprint log10(10)\n// expect: 1\n\nprint sin(1.2)\n// expect: 0.932039\n\nprint cos(0.2)\n// expect: 0.980067\n\nprint sqrt(2)\n// expect: 1.41421\n\nprint floor(3.5)\n// expect: 3\n\nprint ceil(3.5)\n// expect: 4\n\nprint arctan(-0.2,-0.5)\n// expect: -1.9513\n\nprint abs(-1.2)\n// expect: 1.2\n\nprint sin(\"Hello\")\n// expect error 'ExpctArgNm'\n"
  },
  {
    "path": "test/math/sign.morpho",
    "content": "print sign(-1) // expect: -1\nprint sign(0) // expect: 0\nprint sign(1) // expect: 1\nprint sign(2.0) // expect: 1\nprint sign(-2.0) // expect: -1\nprint sign(0.0) // expect: 0\nprint sign(-(1/0)) // expect: -1\nprint sign(1/0) // expect: 1\nprint sign(0/0) // expect: 0"
  },
  {
    "path": "test/matrix/Lnorm.morpho",
    "content": "// Compute norms other than L2\nimport constants \n\nvar a = Matrix([1,2,3])\n\nprint a.norm(1) // expect: 6\n\nprint abs(a.norm(2) - 3.74166) < 1e-4 // expect: true\n\nprint abs(a.norm(3) - 3.30193) < 1e-4 // expect: true\n\nprint a.norm(Inf) // expect: 3\n\n"
  },
  {
    "path": "test/matrix/arith_scalar.morpho",
    "content": "// Add/subtract a scalar\n\nvar a = Matrix([[1,2], [3,4]])\n\nprint a-1\n// expect: [ 0 1 ]\n// expect: [ 2 3 ]\n\n\nprint 1-a\n// expect: [ 0 -1 ]\n// expect: [ -2 -3 ]\n\n\nprint a+1\n// expect: [ 2 3 ]\n// expect: [ 4 5 ]\n\n\nprint 1+a\n// expect: [ 2 3 ]\n// expect: [ 4 5 ]\n"
  },
  {
    "path": "test/matrix/arithmetic.morpho",
    "content": "// Matrix arithmetic\n\nvar a = Matrix([[1, 2], [3, 4]])\nvar b = Matrix([[0, 1], [1, 0]])\n\nprint \"A+B:\"\nprint a+b\n// expect: A+B:\n// expect: [ 1 3 ]\n// expect: [ 4 4 ]\n\nprint \"A-B:\"\nprint a-b\n// expect: A-B:\n// expect: [ 1 1 ]\n// expect: [ 2 4 ]\n\nprint \"A*B:\"\nprint a*b\n// expect: A*B:\n// expect: [ 2 1 ]\n// expect: [ 4 3 ]\n\nvar c = Matrix([[1,2,3], [4,5,6]])\nvar d = Matrix([[1,2], [3,4], [5,6]])\nprint \"C*D:\"\nprint c*d\n// expect: C*D:\n// expect: [ 22 28 ]\n// expect: [ 49 64 ]\n"
  },
  {
    "path": "test/matrix/assign.morpho",
    "content": "// Assign \n\nvar a = Matrix([[1,2], [3,4]])\nvar b = Matrix(2,2)\n\nprint b \n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\nb.assign(a) \n\nprint b \n// expect: [ 1 2 ]\n// expect: [ 3 4 ]\n\nvar c = Matrix(1,2)\nb.assign(c)\n// expect error 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/blockmatrix_constructor.morpho",
    "content": "// Block matrix constructor with single list \n\nprint Matrix([Matrix(2)])\n// expect error 'MtrxInvldInit'\n"
  },
  {
    "path": "test/matrix/concatenate.morpho",
    "content": "// Concatenate matrices together\n\nvar a = Matrix([[0,1], [1,0]])\nvar b = Matrix([2,3])\n\nprint a\n// expect: [ 0 1 ]\n// expect: [ 1 0 ]\n\nprint b \n// expect: [ 2 ]\n// expect: [ 3 ]\n\nvar c = Matrix([[a, b], [b.transpose(), 0]])\n\nprint c\n// expect: [ 0 1 2 ]\n// expect: [ 1 0 3 ]\n// expect: [ 2 3 0 ]\n\nvar c = Matrix([[a, 0, b], [0, a, b], [b.transpose(), b.transpose(), 0]])\n\nprint c\n// expect: [ 0 1 0 0 2 ]\n// expect: [ 1 0 0 0 3 ]\n// expect: [ 0 0 0 1 2 ]\n// expect: [ 0 0 1 0 3 ]\n// expect: [ 2 3 2 3 0 ]\n\nvar c = Matrix([[a, b], [b, 0]])\n// expect error 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/concatenate_sparse.morpho",
    "content": "// Concatenate matrices together\n\nvar a = Sparse([[0,1,1],[1,0,1]])\nvar b = Matrix([2,3])\n\nprint a\n// expect: [ 0 1 ]\n// expect: [ 1 0 ]\n\nvar c = Matrix([[a, b], [b.transpose(), 0]])\n\nprint c\n// expect: [ 0 1 2 ]\n// expect: [ 1 0 3 ]\n// expect: [ 2 3 0 ]\n\nvar c = Matrix([[a, 0, b], [0, a, b], [b.transpose(), b.transpose(), 0]])\n\nprint c\n// expect: [ 0 1 0 0 2 ]\n// expect: [ 1 0 0 0 3 ]\n// expect: [ 0 0 0 1 2 ]\n// expect: [ 0 0 1 0 3 ]\n// expect: [ 2 3 2 3 0 ]\n\nvar c = Matrix([[a, b], [b, 0]])\n// expect error 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/dimensions.morpho",
    "content": "// Solution of linear systems\n\nvar a = Matrix([[0.8, -0.4], [0.4, 0.8]])\nvar v = Matrix([0.2, 0.3])\n\nprint a.dimensions()\n// expect: [ 2, 2 ]\n\nprint v.dimensions()\n// expect: [ 2, 1 ]\n"
  },
  {
    "path": "test/matrix/eigensystem.morpho",
    "content": "// Check eigenvalues works on a problematic matrix\n\nvar a = Matrix([[ -0.083929, 0.102945, 0.213477 ],\n                [ 0.102945, -0.108697, 0.189335 ],\n                [ 0.213477, 0.189335, 0.192626 ]])\n\nprint a.eigensystem()\n// expect: [ <List>, <Matrix> ]"
  },
  {
    "path": "test/matrix/eigenvalues.morpho",
    "content": "// Transpose\n\nvar a = Matrix([[1,-1,0], [-1,1,0], [0,0,1]])\n\nvar ev = a.eigenvalues()\nev.sort() \nprint ev \n// expect: [ 0, 1, 2 ]\n\nvar b = Matrix([[1,2,0], [-2,1,0], [0,0,1]])\n\nvar ev = b.eigenvalues()\nfor (e in ev) print e \n// expect: 1 + 2im\n// expect: 1 - 2im\n// expect: 1\n\nprint a  // ensure a is not overwritten\n// expect: [ 1 -1 0 ]\n// expect: [ -1 1 0 ]\n// expect: [ 0 0 1 ]\n\nvar es = a.eigensystem()\nprint es[0]\n// expect: [ 2, 0, 1 ]\nprint es[1]\n// expect: [ 0.707107 0.707107 0 ]\n// expect: [ -0.707107 0.707107 0 ]\n// expect: [ 0 0 1 ]\n"
  },
  {
    "path": "test/matrix/format.morpho",
    "content": "// Print matrices with formatted output\n\nvar a = Matrix([[1/2,1/3], [1/3,1/2]])\nprint a.format(\"%5.3g\")\n// expect: [   0.5 0.333 ]\n// expect: [ 0.333   0.5 ]"
  },
  {
    "path": "test/matrix/get_column.morpho",
    "content": "// Transpose\n\nvar a = Matrix([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])\n\n//print a + a\n\nprint a.column(0)\n// expect: [ 1 ]\n// expect: [ 4 ]\n// expect: [ 7 ]\n// expect: [ 10 ]\n\nprint a.column(2)\n// expect: [ 3 ]\n// expect: [ 6 ]\n// expect: [ 9 ]\n// expect: [ 12 ]\n\nprint a.column(10)\n// expect Error 'MtrxBnds'\n"
  },
  {
    "path": "test/matrix/identity.morpho",
    "content": "// Identity matrix\n\nvar a = IdentityMatrix(2)\nprint a\n// expect: [ 1 0 ]\n// expect: [ 0 1 ]\n\na = IdentityMatrix(3)\nprint a\n// expect: [ 1 0 0 ]\n// expect: [ 0 1 0 ]\n// expect: [ 0 0 1 ]\n\na = IdentityMatrix()\n// expect error 'MtrxIdnttyCns'\n"
  },
  {
    "path": "test/matrix/incompatible_add.morpho",
    "content": "// Try to add incompatible matrices\n\nvar a = Matrix([[1, 2, 1], [3, 4, 1]])\nvar b = Matrix([[0, 1], [1, 0]])\n\nprint a+b\n// expect Error: 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/incompatible_mul.morpho",
    "content": "// Try to multiply incompatible matrices\n\nvar a = Matrix([[1, 2, 1], [3, 4, 1]])\nvar b = Matrix([[0, 1], [1, 0]])\n\nprint a*b\n// expect Error: 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/incompatible_sub.morpho",
    "content": "// Try to subtract incompatible matrices\n\nvar a = Matrix([[1, 2, 1], [3, 4, 1]])\nvar b = Matrix([[0, 1], [1, 0]])\n\nprint a*b\n// expect Error: 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/inherited.morpho",
    "content": "// Test Matrix methods inherited from Object \n\nvar err = Matrix([1,2,3])\n\nprint err.respondsto(\"respondsto\") // expect: true\n\nprint islist(err.respondsto()) // expect: true\n\nprint err.clss() // expect: @Matrix\n\nprint err.superclass() // expect: @Object\n\nprint islist(err.invoke(\"respondsto\")) // expect: true\n\nprint err.has(\"a\") // expect: false\n\n"
  },
  {
    "path": "test/matrix/initializer.morpho",
    "content": "// Initialize different matrices\n\nvar a = Matrix([[1]])\nprint a\n// expect: [ 1 ]\n\nvar b = Matrix([ [1, 2], [3, 4]])\nprint b\n// expect: [ 1 2 ]\n// expect: [ 3 4 ]\n\nvar c = Matrix([ [1, 2, 3], [4, 5, 6], [7, 8, 9]])\nprint c\n// expect: [ 1 2 3 ]\n// expect: [ 4 5 6 ]\n// expect: [ 7 8 9 ]\n\na = Matrix(2,2)\na[0,0] = 1.0\na[1,1] = 1.0\nprint a\n// expect: [ 1 0 ]\n// expect: [ 0 1 ]\n\nvar v = Matrix([0.2, 0.3])\nprint v\n// expect: [ 0.2 ]\n// expect: [ 0.3 ]\n\nvar w = Matrix(b)\nprint w\n// expect: [ 1 2 ]\n// expect: [ 3 4 ]\n\nvar w = Matrix([[1,[2,3]]])\n// expect Error 'MtrxInvldInit'\nprint w\n"
  },
  {
    "path": "test/matrix/inverse.morpho",
    "content": "// Testing the inverse method for the Morpho matrix class\n\nvar a = 4, b = 5.7, c = 2, d = 2.4\n\nvar m = Matrix([[a, b], [c, d]])\n\nvar mi = m.inverse()\n\nvar det = a*d - b*c \n\nprint ((mi[0,0] - d/det)<1e-8) // expect: true\nprint ((mi[0,1] + b/det)<1e-8) // expect: true\nprint ((mi[1,0] + c/det)<1e-8) // expect: true\nprint ((mi[1,1] - a/det)<1e-8) // expect: true\n\nvar m = Matrix([[1,0,0],[0,1,0],[0,0,0]])\n\nprint m.inverse() // expect error 'MtrxSnglr'\n"
  },
  {
    "path": "test/matrix/linearsolve.morpho",
    "content": "// Solution of linear systems\n\nvar a = Matrix([[0.8, -0.4], [0.4, 0.8]])\nvar v = Matrix([0.2, 0.3])\n\nprint a\n// expect: [ 0.8 -0.4 ]\n// expect: [ 0.4 0.8 ]\n\nprint v/a\n// expect: [ 0.35 ]\n// expect: [ 0.2 ]\n\nprint a/a\n// expect: [ 1 0 ]\n// expect: [ 0 1 ]\n\nprint a\n// expect: [ 0.8 -0.4 ]\n// expect: [ 0.4 0.8 ]\n\nvar b = Matrix([[3, 4], [6, 8]])\nprint v/b\n// expect error 'MtrxSnglr'\n"
  },
  {
    "path": "test/matrix/linearsolve3x3.morpho",
    "content": "// Solution of linear systems\n\nvar a = Matrix([[1,2,2], [4,5,6], [7,8,9]])\nvar v = Matrix([1,2,3])\n\nvar w=v/a\nprint a*w\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/matrix/negate.morpho",
    "content": "// Transpose\n\nvar a = Matrix([[1,2], [3,4]])\n\nprint -a\n// expect: [ -1 -2 ]\n// expect: [ -3 -4 ]\n"
  },
  {
    "path": "test/matrix/nonnum_indices.morpho",
    "content": "// Access with non numerical indices\n\nvar a = Matrix([[1,2], [3,4], [5,6]])\n\nprint a[\"Hello\", \"Squirrel\"]\n// expect error: 'MtrxInvldIndx'\n"
  },
  {
    "path": "test/matrix/nonnum_initializer.morpho",
    "content": "// Non numerical initializer\n\nvar m = Matrix([[1,2], [3,\"oops\"]])\n// expect error: 'MtrxInvldInit'\n"
  },
  {
    "path": "test/matrix/norm.morpho",
    "content": "// Transpose\n\nvar a = Matrix([1,1,1])\n\nprint a.norm()\n// expect: 1.73205\n"
  },
  {
    "path": "test/matrix/outer.morpho",
    "content": "// Outer product\n\nvar a = Matrix([1,2,3])\n\nprint a.outer(a)\n// expect: [ 1 2 3 ]\n// expect: [ 2 4 6 ]\n// expect: [ 3 6 9 ]\n\nvar b = Matrix([1,2])\nprint a.outer(b)\n// expect: [ 1 2 ]\n// expect: [ 2 4 ]\n// expect: [ 3 6 ]\n"
  },
  {
    "path": "test/matrix/reshape.morpho",
    "content": "// Reshape matrices \n\nvar a = Matrix([[1,4], [2,5], [3,6]])\nprint a \n// expect: [ 1 4 ]\n// expect: [ 2 5 ]\n// expect: [ 3 6 ]\n\na.reshape(1,6)\nprint a \n// expect: [ 1 2 3 4 5 6 ]\n\na.reshape(2,3)\nprint a \n// expect: [ 1 3 5 ]\n// expect: [ 2 4 6 ]\n\na.reshape(6,1)\nprint a \n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n// expect: [ 4 ]\n// expect: [ 5 ]\n// expect: [ 6 ]\n\na.reshape(3,2)\nprint a \n// expect: [ 1 4 ]\n// expect: [ 2 5 ]\n// expect: [ 3 6 ]\n\na.reshape(3,3)\n// expect error 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/matrix/roll.morpho",
    "content": "// Roll a list \n\nvar m = Matrix([ [1,2,3,4],\n                 [5,6,7,8],\n                 [9,10,11,12],\n                 [13,14,15,16]])\nprint m\n// expect: [ 1 2 3 4 ]\n// expect: [ 5 6 7 8 ]\n// expect: [ 9 10 11 12 ]\n// expect: [ 13 14 15 16 ]\n\nprint m.roll(1,0)\n// expect: [ 13 14 15 16 ]\n// expect: [ 1 2 3 4 ]\n// expect: [ 5 6 7 8 ]\n// expect: [ 9 10 11 12 ]\n\nprint m.roll(-1,0)\n// expect: [ 5 6 7 8 ]\n// expect: [ 9 10 11 12 ]\n// expect: [ 13 14 15 16 ]\n// expect: [ 1 2 3 4 ]\n\nprint m.roll(-2,0)\n// expect: [ 9 10 11 12 ]\n// expect: [ 13 14 15 16 ]\n// expect: [ 1 2 3 4 ]\n// expect: [ 5 6 7 8 ]\n\nprint m.roll(1,1)\n// expect: [ 4 1 2 3 ]\n// expect: [ 8 5 6 7 ]\n// expect: [ 12 9 10 11 ]\n// expect: [ 16 13 14 15 ]\n\nprint m.roll(-1,1)\n// expect: [ 2 3 4 1 ]\n// expect: [ 6 7 8 5 ]\n// expect: [ 10 11 12 9 ]\n// expect: [ 14 15 16 13 ]\n\nvar a = Matrix([ [  -1,  -1,   -1,   -1,   -1 ],\n                 [ -0.5, -0.5, -0.5, -0.5, -0.5 ],\n                 [  0,    0,    0,    0,    0 ],\n                 [  0.5,  0.5,  0.5,  0.5,  0.5 ],\n                 [  1,    1,    1,    1,    1 ] ])\n\nprint a.roll(-1, 0)\n// expect: [ -0.5 -0.5 -0.5 -0.5 -0.5 ]\n// expect: [ 0 0 0 0 0 ]\n// expect: [ 0.5 0.5 0.5 0.5 0.5 ]\n// expect: [ 1 1 1 1 1 ]\n// expect: [ -1 -1 -1 -1 -1 ]"
  },
  {
    "path": "test/matrix/scalar_mul.morpho",
    "content": "// Transpose\n\nvar a = Matrix([[1,2], [3,4]])\n\nprint 2*a\n// expect: [ 2 4 ]\n// expect: [ 6 8 ]\n\nprint a*2\n// expect: [ 2 4 ]\n// expect: [ 6 8 ]\n\nprint a/2\n// expect: [ 0.5 1 ]\n// expect: [ 1.5 2 ]\n"
  },
  {
    "path": "test/matrix/set_column.morpho",
    "content": "// Transpose\n\nvar a = Matrix(3,3)\n\na.setcolumn(1, Matrix([1,2,3]))\n\nprint a\n// expect: [ 0 1 0 ]\n// expect: [ 0 2 0 ]\n// expect: [ 0 3 0 ]\n"
  },
  {
    "path": "test/matrix/trace.morpho",
    "content": "// Transpose\n\nvar a = Matrix([[1,2], [3,4]])\n\nprint a.trace()\n// expect: 5\n\nvar b = Matrix([[1, 2]])\nprint b.trace()\n// expect error 'MtrxNtSq'\n"
  },
  {
    "path": "test/matrix/transpose.morpho",
    "content": "// Transpose\n\nvar a = Matrix([[1,2], [3,4], [5,6]])\n\nprint a.transpose()\n// expect: [ 1 3 5 ]\n// expect: [ 2 4 6 ]\n"
  },
  {
    "path": "test/mesh/addgradeerror.morpho",
    "content": "\nvar a = Mesh(\"tetrahedron.mesh\")\n\nimport meshtools\nvar m = LineMesh(fn (t) [cos(t), sin(t), 0], 0..2*Pi:0.2, closed=true)\nm.addgrade(2)\n\n// expect error 'MshAddGrdOutOfBnds'\n"
  },
  {
    "path": "test/mesh/addgradetwo.morpho",
    "content": "// Test to check that addgrade from a volume element works\nimport meshtools\n\nvar m = Mesh(\"tetrahedron2.mesh\")\nm.addgrade(2)\nm.addgrade(1)\n\nprint m.connectivitymatrix(0,3)\n// expect: [ 1 ]\n// expect: [ 1 ]\n// expect: [ 1 ]\n// expect: [ 1 ]\n\nprint m.connectivitymatrix(0,1)\n// expect: [ 1 1 0 1 0 0 ]\n// expect: [ 1 0 1 0 1 0 ]\n// expect: [ 0 1 1 0 0 1 ]\n// expect: [ 0 0 0 1 1 1 ]\n\nprint m.connectivitymatrix(0,2)\n// expect: [ 1 1 1 0 ]\n// expect: [ 1 1 0 1 ]\n// expect: [ 1 0 1 1 ]\n// expect: [ 0 1 1 1 ]\n"
  },
  {
    "path": "test/mesh/addgradezero.morpho",
    "content": "// Test to confirm that adding grade 0 to a mesh doesn't change anything.\nimport meshtools\nimport symmetry\nvar m = LineMesh(fn (t) [t,0,0], -1..1:1)\nm.addgrade(0)\nprint m.connectivitymatrix(0,0) // expect: nil\nvar s = Selection(m, fn (x,y,z) x<-0.5 || x>0.5)\nvar t = Translate(Matrix([2,0,0]))\nm.addsymmetry(t, s)\nm.addgrade(0)\nprint m.connectivitymatrix(0,0)\n// expect: [ 0 0 <Translate> ]\n// expect: [ 0 0 0 ]\n// expect: [ 0 0 0 ]\n"
  },
  {
    "path": "test/mesh/clone.morpho",
    "content": "\nvar a = Mesh(\"square.mesh\")\nvar b = a.clone()\n\nprint b\n// expect: <Mesh: 4 vertices>\n\na.addgrade(1)\n\nprint b.connectivitymatrix(0,2)\n// expect: [ 1 0 ]\n// expect: [ 1 1 ]\n// expect: [ 1 1 ]\n// expect: [ 0 1 ]\n\n\nprint b.connectivitymatrix(0,1)\n// expect: nil\n"
  },
  {
    "path": "test/mesh/connectivity.morpho",
    "content": "\nvar a = Mesh(\"square.mesh\")\n\na.addgrade(1)\n\nprint a.connectivitymatrix(0,1)\n// expect: [ 1 1 0 0 0 ]\n// expect: [ 1 0 1 1 0 ]\n// expect: [ 0 1 1 0 1 ]\n// expect: [ 0 0 0 1 1 ]\n\nprint a.connectivitymatrix(0,2)\n// expect: [ 1 0 ]\n// expect: [ 1 1 ]\n// expect: [ 1 1 ]\n// expect: [ 0 1 ]\n\nprint a.connectivitymatrix(2,0)\n// expect: [ 1 1 1 0 ]\n// expect: [ 0 1 1 1 ]\n\nprint a.connectivitymatrix(1,2)\n// expect: [ 1 0 ]\n// expect: [ 1 0 ]\n// expect: [ 1 1 ]\n// expect: [ 0 1 ]\n// expect: [ 0 1 ]\n"
  },
  {
    "path": "test/mesh/connectivity_tetrahedron.morpho",
    "content": "\nvar a = Mesh(\"tetrahedron.mesh\")\n\na.addgrade(1)\n\nprint a.connectivitymatrix(0,2)\n// expect: [ 1 0 1 1 ]\n// expect: [ 1 1 0 1 ]\n// expect: [ 1 1 1 0 ]\n// expect: [ 0 1 1 1 ]\nprint \"\"\n// expect:\n\nprint a.connectivitymatrix(0,1)\n// expect: [ 1 1 0 0 0 1 ]\n// expect: [ 1 0 1 1 0 0 ]\n// expect: [ 0 1 1 0 1 0 ]\n// expect: [ 0 0 0 1 1 1 ]\nprint \"\"\n// expect:\n\nprint a.connectivitymatrix(1,0)\n// expect: [ 1 1 0 0 ]\n// expect: [ 1 0 1 0 ]\n// expect: [ 0 1 1 0 ]\n// expect: [ 0 1 0 1 ]\n// expect: [ 0 0 1 1 ]\n// expect: [ 1 0 0 1 ]\nprint \"\"\n// expect:\n\nprint a.connectivitymatrix(2,0)\n// expect: [ 1 1 1 0 ]\n// expect: [ 0 1 1 1 ]\n// expect: [ 1 0 1 1 ]\n// expect: [ 1 1 0 1 ]\nprint \"\"\n// expect:\n\nprint a.connectivitymatrix(1,2)\n// expect: [ 1 0 0 1 ]\n// expect: [ 1 0 1 0 ]\n// expect: [ 1 1 0 0 ]\n// expect: [ 0 1 0 1 ]\n// expect: [ 0 1 1 0 ]\n// expect: [ 0 0 1 1 ]\n"
  },
  {
    "path": "test/mesh/err_empty_mesh_set_element.morpho",
    "content": "var m = Mesh()\nprint m // shows empty mesh\n// expect: <Mesh: 0 vertices>\nm.setvertexposition(0, Matrix([-5,0,0]))\n// expect error 'MshInvldId'"
  },
  {
    "path": "test/mesh/err_mesh_con_args.morpho",
    "content": "import meshtools\nvar m = LineMesh(fn (t) [t,0,0], -1..1:0.2)\nprint m \n// expect: <Mesh: 11 vertices>\nvar m2 = Mesh(m)\n// expect error 'MshArgs'\n"
  },
  {
    "path": "test/mesh/inherited.morpho",
    "content": "// Test Mesh methods inherited from Object \n\nvar a = Mesh() \n\nprint a.respondsto(\"respondsto\") // expect: true\n\nprint islist(a.respondsto()) // expect: true\n\nprint a.clss() // expect: @Mesh\n\nprint a.superclass() // expect: @Object\n\nprint islist(a.invoke(\"respondsto\")) // expect: true\n\nprint a.has(\"a\") // expect: false\n\n"
  },
  {
    "path": "test/mesh/load.morpho",
    "content": "\nvar a = Mesh(\"sphere.mesh\")\n\nprint a\n// expect: <Mesh: 770 vertices>\n"
  },
  {
    "path": "test/mesh/load_missing_coord.morpho",
    "content": "\nvar a = Mesh(\"square_missingcoord.mesh\")\n\nprint a\n// expect error 'MshLdVrtDim'\n"
  },
  {
    "path": "test/mesh/load_spurious_vertex_ref.morpho",
    "content": "\nvar a = Mesh(\"square_spurious_vertex_ref.mesh\")\n\nprint a\n// expect error 'MshLdVrtNtFnd'\n"
  },
  {
    "path": "test/mesh/maxgrade.morpho",
    "content": "import meshtools\nvar a = Mesh(\"tetrahedron.mesh\")\n\n\n\nprint a.maxgrade()\n// expect: 2\n\n\nvar L = 0.5\nvar dx = 0.1\n\nvar m = AreaMesh(fn (u, v) [u, v], -L..L:dx, -L..L:dx)\nprint m.count(0) // expect: 121\nprint m.count(1) // expect: 0\nprint m.count(2) // expect: 200\nm.addgrade(1)\nprint m.count(0) // expect: 121\nprint m.count(1) // expect: 320\nprint m.count(2) // expect: 200\n"
  },
  {
    "path": "test/mesh/merge.morpho",
    "content": "import meshtools\n\nvar m1 = LineMesh(fn (t) [cos(t), sin(t), 0], 0..Pi:Pi/5)\nvar m2 = LineMesh(fn (t) [cos(t), sin(t), 0], Pi..2*Pi:Pi/5)\n\nvar merge = MeshMerge([m1])\nmerge.addmesh([m2])\nvar m = merge.merge()\n\nvar lines = m.connectivitymatrix(0,1)\nprint lines\n// expect: [ 1 0 0 0 0 0 0 0 0 1 ]\n// expect: [ 1 1 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 1 1 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 1 1 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 1 1 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 1 1 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 1 1 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 1 1 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 1 1 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 1 1 ]\n\nfor (elid in 0...m.count(1)) print lines.rowindices(elid)\n// expect: [ 0, 1 ]\n// expect: [ 1, 2 ]\n// expect: [ 2, 3 ]\n// expect: [ 3, 4 ]\n// expect: [ 4, 5 ]\n// expect: [ 5, 6 ]\n// expect: [ 6, 7 ]\n// expect: [ 7, 8 ]\n// expect: [ 8, 9 ]\n// expect: [ 0, 9 ]\n\nvar merge2 = MeshMerge(m1)\nmerge2.addmesh(m2)\nprint merge2.meshes\n// expect: [ <Mesh>, <Mesh> ]\n\nvar merger2 = merge2.merge()\nvar v1 = m.vertexmatrix()\nvar v2 = merger2.vertexmatrix()\nvar Equal= true\nvar d = v1.dimensions()\nfor ( d1 in 0...d[0]){\n\t\tfor (d2 in 0...d[1]){\n\t\t\tEqual = Equal && v1[d1,d2]==v2[d1,d2]\n\t\t}\n}\nprint Equal\n// expect: true"
  },
  {
    "path": "test/mesh/out.mesh",
    "content": "vertices\n\n1 0 0 0 \n2 1 0 0 \n3 0 1 0 \n4 1 1 0 \n\nfaces\n\n1 1 2 3 \n2 2 3 4 \n"
  },
  {
    "path": "test/mesh/removegrade.morpho",
    "content": "// Test to check that addgrade from a volume element works\nimport meshtools\n\nvar m = Mesh(\"tetrahedron2.mesh\")\nm.addgrade(2)\nm.addgrade(1)\nm.removegrade(1)\nm.removegrade(2)\n\nprint m.connectivitymatrix(0,3)\n// expect: [ 1 ]\n// expect: [ 1 ]\n// expect: [ 1 ]\n// expect: [ 1 ]\n\nprint m.connectivitymatrix(0,1)\n// expect: nil\n\nprint m.connectivitymatrix(0,2)\n// expect: nil\n"
  },
  {
    "path": "test/mesh/resetconnectivity.morpho",
    "content": "\nvar a = Mesh(\"square.mesh\")\na.addgrade(1)\n\nprint a.connectivitymatrix(0,1)\n// expect: [ 1 1 0 0 0 ]\n// expect: [ 1 0 1 1 0 ]\n// expect: [ 0 1 1 0 1 ]\n// expect: [ 0 0 0 1 1 ]\n\nprint a.connectivitymatrix(0,2)\n// expect: [ 1 0 ]\n// expect: [ 1 1 ]\n// expect: [ 1 1 ]\n// expect: [ 0 1 ]\n\nprint a.connectivitymatrix(2,0)\n// expect: [ 1 1 1 0 ]\n// expect: [ 0 1 1 1 ]\n\na.resetconnectivity()\n\nprint a.connectivitymatrix(0,1)\n// expect: [ 1 1 0 0 0 ]\n// expect: [ 1 0 1 1 0 ]\n// expect: [ 0 1 1 0 1 ]\n// expect: [ 0 0 0 1 1 ]\n\nprint a.connectivitymatrix(0,2)\n// expect: [ 1 0 ]\n// expect: [ 1 1 ]\n// expect: [ 1 1 ]\n// expect: [ 0 1 ]\n\nprint a.connectivitymatrix(2,0)\n// expect: [ 1 1 1 0 ]\n// expect: [ 0 1 1 1 ]\n"
  },
  {
    "path": "test/mesh/save.morpho",
    "content": "\nvar a = Mesh(\"square.mesh\")\n\nprint a\n// expect: <Mesh: 4 vertices>\n\na.save(\"out.mesh\")\n\nvar b = Mesh(\"out.mesh\")\nprint b\n// expect: <Mesh: 4 vertices>\n"
  },
  {
    "path": "test/mesh/sphere.mesh",
    "content": "vertices\n\n1 0.0864185 0.596014 -0.0864185 \n2 -0.351971 -0.351971 -0.351971 \n3 0.512094 -0.0935136 -0.31663 \n4 0.351971 -0.351971 -0.351971 \n5 0.0935136 0.512094 -0.31663 \n6 -0.351971 0.351971 -0.351971 \n7 1.56135e-16 -0.193544 0.577235 \n8 0.351971 0.351971 -0.351971 \n9 -0.31663 0.0935136 0.512094 \n10 -0.351971 -0.351971 0.351971 \n11 0.273295 0.471245 0.273295 \n12 0.351971 -0.351971 0.351971 \n13 -0.273295 0.273295 0.471245 \n14 -0.351971 0.351971 0.351971 \n15 0.273295 -0.471245 -0.273295 \n16 0.351971 0.351971 0.351971 \n17 -0.273295 -0.471245 0.273295 \n18 7.59791e-17 -1.19499e-16 -0.606904 \n19 0.273295 0.471245 -0.273295 \n20 -1.79147e-18 3.71799e-17 0.606904 \n21 0.512094 0.0935136 -0.31663 \n22 1.49826e-17 -0.606904 8.02851e-17 \n23 1.77124e-16 0.577235 -0.193544 \n24 4.9909e-17 0.606904 3.88868e-17 \n25 0.0935136 0.512094 0.31663 \n26 -0.606904 5.5069e-17 5.20646e-17 \n27 -0.31663 -0.0935136 0.512094 \n28 0.606904 4.84814e-17 1.12006e-17 \n29 0.596014 -0.0864185 0.0864185 \n30 -0.430905 -0.430905 -7.82355e-17 \n31 -0.596014 -0.0864185 -0.0864185 \n32 0.177071 -0.177071 0.555185 \n33 -0.596014 0.0864185 0.0864185 \n34 0.177071 0.555185 0.177071 \n35 0.596014 -0.0864185 -0.0864185 \n36 0.555185 -0.177071 0.177071 \n37 0.596014 0.0864185 0.0864185 \n38 3.78864e-17 -0.430905 -0.430905 \n39 0.577235 -8.14644e-17 -0.193544 \n40 0.430905 -0.430905 -4.12764e-17 \n41 0.512094 0.0935136 0.31663 \n42 0.177071 0.177071 0.555185 \n43 -0.0935136 0.512094 0.31663 \n44 -0.177071 0.555185 0.177071 \n45 -0.193544 4.71291e-17 0.577235 \n46 7.28164e-17 0.430905 -0.430905 \n47 0.471245 -0.273295 0.273295 \n48 -0.430905 0.430905 2.48688e-17 \n49 -0.471245 -0.273295 -0.273295 \n50 -0.177071 0.177071 0.555185 \n51 -0.471245 0.273295 0.273295 \n52 -0.555185 -0.177071 -0.177071 \n53 0.471245 -0.273295 -0.273295 \n54 -0.430905 -1.09518e-19 -0.430905 \n55 0.471245 0.273295 0.273295 \n56 0.430905 0.430905 -8.51463e-17 \n57 0.31663 -0.0935136 -0.512094 \n58 -0.177071 -0.555185 -0.177071 \n59 0.512094 -0.0935136 0.31663 \n60 -0.555185 0.177071 -0.177071 \n61 5.9116e-17 0.577235 0.193544 \n62 0.430905 -1.66575e-16 -0.430905 \n63 -0.512094 0.31663 -0.0935136 \n64 -0.177071 -0.177071 -0.555185 \n65 -0.185357 -0.41054 -0.41054 \n66 0.177071 -0.555185 -0.177071 \n67 -0.41054 -0.185357 -0.41054 \n68 -0.555185 0.177071 0.177071 \n69 -0.185357 -0.41054 0.41054 \n70 6.81268e-17 -0.430905 0.430905 \n71 -0.41054 -0.185357 0.41054 \n72 0.177071 -0.177071 -0.555185 \n73 0.31663 -0.512094 -0.0935136 \n74 0.177071 -0.555185 0.177071 \n75 0.31663 0.0935136 -0.512094 \n76 -0.555185 -0.177071 0.177071 \n77 0.577235 4.3661e-17 0.193544 \n78 -3.00145e-17 0.430905 0.430905 \n79 0.31663 -0.0935136 0.512094 \n80 0.177071 0.177071 -0.555185 \n81 -0.512094 0.31663 0.0935136 \n82 -0.177071 -0.555185 0.177071 \n83 0.185357 -0.41054 -0.41054 \n84 0.555185 -0.177071 -0.177071 \n85 -0.41054 0.185357 -0.41054 \n86 -0.430905 -1.17887e-16 0.430905 \n87 0.185357 -0.41054 0.41054 \n88 -0.177071 0.177071 -0.555185 \n89 -0.41054 0.185357 0.41054 \n90 -0.177071 0.555185 -0.177071 \n91 0.31663 -0.512094 0.0935136 \n92 0.555185 0.177071 -0.177071 \n93 0.193544 -2.28647e-16 -0.577235 \n94 0.430905 -1.05715e-16 0.430905 \n95 -0.31663 0.0935136 -0.512094 \n96 -0.177071 -0.177071 0.555185 \n97 0.31663 0.0935136 0.512094 \n98 0.177071 0.555185 -0.177071 \n99 -0.577235 0.193544 6.86928e-17 \n100 0.555185 0.177071 0.177071 \n101 0.41054 -0.41054 -0.185357 \n102 -0.273295 0.471245 0.273295 \n103 0.41054 0.41054 -0.185357 \n104 -0.471245 0.273295 -0.273295 \n105 0.0864185 -0.0864185 -0.596014 \n106 -0.471245 -0.273295 0.273295 \n107 -0.0864185 0.0864185 -0.596014 \n108 0.471245 0.273295 -0.273295 \n109 0.193544 -0.577235 -5.55382e-17 \n110 0.0935136 -0.31663 -0.512094 \n111 -0.31663 -0.512094 0.0935136 \n112 0.577235 0.193544 -5.78119e-17 \n113 -0.31663 -0.0935136 -0.512094 \n114 0.512094 -0.31663 0.0935136 \n115 0.193544 -4.49097e-17 0.577235 \n116 -0.0935136 0.31663 0.512094 \n117 -0.0935136 -0.512094 -0.31663 \n118 -0.577235 -9.95665e-17 0.193544 \n119 0.41054 -0.41054 0.185357 \n120 -0.41054 -0.41054 -0.185357 \n121 0.41054 0.41054 0.185357 \n122 -0.185357 0.41054 -0.41054 \n123 0.273295 -0.273295 -0.471245 \n124 0.41054 -0.185357 -0.41054 \n125 -0.273295 0.273295 -0.471245 \n126 -0.185357 0.41054 0.41054 \n127 -0.512094 -0.31663 0.0935136 \n128 0.41054 -0.185357 0.41054 \n129 -0.31663 -0.512094 -0.0935136 \n130 -7.72329e-17 -0.193544 -0.577235 \n131 -0.193544 -3.58121e-17 -0.577235 \n132 0.0935136 0.31663 -0.512094 \n133 -0.31663 0.512094 0.0935136 \n134 0.512094 -0.31663 -0.0935136 \n135 0.0935136 -0.512094 -0.31663 \n136 9.00716e-17 0.193544 0.577235 \n137 0.0864185 0.0864185 0.596014 \n138 -0.41054 -0.41054 0.185357 \n139 -0.0864185 -0.596014 -0.0864185 \n140 0.185357 0.41054 -0.41054 \n141 0.0864185 -0.596014 0.0864185 \n142 0.41054 0.185357 -0.41054 \n143 -0.0864185 0.596014 -0.0864185 \n144 0.185357 0.41054 0.41054 \n145 -0.512094 -0.31663 -0.0935136 \n146 0.41054 0.185357 0.41054 \n147 -0.193544 -0.577235 -8.53496e-17 \n148 0.0935136 -0.512094 0.31663 \n149 0.31663 0.512094 -0.0935136 \n150 -0.0935136 0.31663 -0.512094 \n151 -0.31663 0.512094 -0.0935136 \n152 0.577235 -0.193544 -6.84494e-17 \n153 3.18471e-17 -0.577235 -0.193544 \n154 -0.512094 -0.0935136 -0.31663 \n155 0.273295 0.273295 0.471245 \n156 0.0864185 -0.0864185 0.596014 \n157 -0.273295 -0.471245 -0.273295 \n158 -0.41054 0.41054 -0.185357 \n159 0.273295 -0.471245 0.273295 \n160 -0.0864185 -0.0864185 -0.596014 \n161 -0.273295 0.471245 -0.273295 \n162 0.0864185 0.0864185 -0.596014 \n163 -0.577235 -0.193544 2.32018e-17 \n164 -0.0864185 -0.0864185 0.596014 \n165 0.512094 0.31663 -0.0935136 \n166 -0.0935136 -0.512094 0.31663 \n167 0.31663 0.512094 0.0935136 \n168 -5.60366e-18 0.193544 -0.577235 \n169 -0.193544 0.577235 1.09776e-17 \n170 -0.0935136 -0.31663 0.512094 \n171 -0.512094 0.0935136 0.31663 \n172 -0.512094 0.0935136 -0.31663 \n173 -0.0864185 0.596014 0.0864185 \n174 0.273295 -0.273295 0.471245 \n175 -0.596014 0.0864185 -0.0864185 \n176 -0.41054 0.41054 0.185357 \n177 -0.596014 -0.0864185 0.0864185 \n178 -0.273295 -0.273295 -0.471245 \n179 0.596014 0.0864185 -0.0864185 \n180 0.273295 0.273295 -0.471245 \n181 -0.0935136 -0.31663 -0.512094 \n182 -0.273295 -0.273295 0.471245 \n183 0.512094 0.31663 0.0935136 \n184 -1.0845e-16 -0.577235 0.193544 \n185 0.193544 0.577235 -1.8251e-16 \n186 -0.0935136 0.512094 -0.31663 \n187 0.0935136 0.31663 0.512094 \n188 0.0935136 -0.31663 0.512094 \n189 -0.512094 -0.0935136 0.31663 \n190 -0.577235 2.04248e-17 -0.193544 \n191 0.0864185 0.596014 0.0864185 \n192 -0.0864185 0.0864185 0.596014 \n193 0.0864185 -0.596014 -0.0864185 \n194 -0.0864185 -0.596014 0.0864185 \n195 0.572222 -0.187783 0.0896566 \n196 0.136787 0.538749 -0.249253 \n197 0.377416 -0.0471868 -0.476089 \n198 0.187783 -0.572222 -0.0896566 \n199 -0.315438 0.415407 -0.315438 \n200 0.257409 -0.550008 -0.0471104 \n201 0.231715 -0.445393 0.345554 \n202 -0.257409 0.550008 -0.0471104 \n203 0.465691 0.367021 0.140757 \n204 0.043793 0.59041 -0.140941 \n205 0.367021 0.465691 -0.140757 \n206 -0.227102 0.517774 0.227102 \n207 -0.538749 -0.136787 -0.249253 \n208 0.0896566 -0.187783 0.572222 \n209 0.476089 -0.0471868 0.377416 \n210 -0.249253 -0.538749 0.136787 \n211 -0.572222 -0.187783 -0.0896566 \n212 0.59041 -0.140941 -0.043793 \n213 0.140757 -0.465691 0.367021 \n214 -0.257409 0.550008 0.0471104 \n215 -0.345554 0.231715 0.445393 \n216 -0.043793 0.59041 -0.140941 \n217 0.298784 0.497541 -0.185739 \n218 -0.315438 0.415407 0.315438 \n219 -0.476089 -0.0471868 -0.377416 \n220 -0.0896566 -0.187783 0.572222 \n221 0.538749 -0.136787 0.249253 \n222 -0.377416 -0.476089 0.0471868 \n223 -0.572222 -0.187783 0.0896566 \n224 0.59041 -0.140941 0.043793 \n225 0.185739 -0.497541 0.298784 \n226 0.345554 0.231715 -0.445393 \n227 -0.367021 0.140757 0.465691 \n228 8.36375e-17 0.601832 -0.0880064 \n229 0.345554 0.445393 0.231715 \n230 -0.517774 0.227102 -0.227102 \n231 0.043129 -0.043129 0.604789 \n232 -0.249253 0.136787 0.538749 \n233 -0.0896566 0.572222 0.187783 \n234 -0.377416 -0.0471868 -0.476089 \n235 0.538749 0.249253 -0.136787 \n236 0.601832 -0.0880064 2.66407e-17 \n237 2.13308e-17 0.518174 0.320444 \n238 0.298784 0.185739 -0.497541 \n239 -0.298784 0.185739 0.497541 \n240 0.231715 -0.445393 -0.345554 \n241 0.298784 0.497541 0.185739 \n242 -0.415407 0.315438 -0.315438 \n243 0.132421 -0.132421 0.579255 \n244 -0.377416 0.0471868 0.476089 \n245 0.0896566 0.572222 0.187783 \n246 -0.249253 -0.136787 -0.538749 \n247 0.476089 0.377416 -0.0471868 \n248 -0.445393 -0.345554 0.231715 \n249 -0.0471104 0.550008 0.257409 \n250 0.367021 0.140757 -0.465691 \n251 0.59041 0.140941 0.043793 \n252 0.185739 -0.497541 -0.298784 \n253 0.367021 0.465691 0.140757 \n254 -0.517774 -0.227102 0.227102 \n255 -0.385744 0.385744 -0.271934 \n256 0.227102 0.517774 0.227102 \n257 -0.538749 0.249253 -0.136787 \n258 0.187783 0.0896566 0.572222 \n259 0.377416 0.476089 0.0471868 \n260 -0.465691 -0.367021 0.140757 \n261 0.0471104 0.550008 0.257409 \n262 0.231715 0.345554 0.445393 \n263 0.59041 0.140941 -0.043793 \n264 0.140757 -0.465691 -0.367021 \n265 -0.415407 -0.315438 0.315438 \n266 -0.425784 0.425784 -0.0937993 \n267 0.315438 0.415407 0.315438 \n268 -0.476089 0.377416 -0.0471868 \n269 0.187783 -0.0896566 0.572222 \n270 0.249253 0.538749 0.136787 \n271 -0.497541 -0.298784 0.185739 \n272 -0.140757 -0.465691 0.367021 \n273 0.140757 0.367021 0.465691 \n274 0.601832 0.0880064 8.84876e-17 \n275 1.46149e-16 0.518174 -0.320444 \n276 0.517774 0.227102 -0.227102 \n277 -0.043129 -0.043129 -0.604789 \n278 -0.227102 0.227102 0.517774 \n279 -0.271934 -0.385744 -0.385744 \n280 -0.136787 -0.538749 -0.249253 \n281 -0.187783 0.572222 -0.0896566 \n282 0.518174 -0.320444 -1.15474e-16 \n283 -0.185739 -0.497541 0.298784 \n284 0.185739 0.298784 0.497541 \n285 -0.367021 -0.140757 0.465691 \n286 0.0471104 0.550008 -0.257409 \n287 0.415407 0.315438 -0.315438 \n288 -0.132421 -0.132421 -0.579255 \n289 -0.315438 0.315438 0.415407 \n290 -0.0937993 -0.425784 -0.425784 \n291 -0.0471868 -0.476089 -0.377416 \n292 -0.187783 0.572222 0.0896566 \n293 0.550008 -0.257409 -0.0471104 \n294 -0.231715 -0.445393 0.345554 \n295 0.140941 0.043793 -0.59041 \n296 -0.298784 -0.185739 0.497541 \n297 -0.0471104 0.550008 -0.257409 \n298 0.0471868 -0.377416 -0.476089 \n299 0.043129 0.043129 -0.604789 \n300 0.227102 -0.517774 -0.227102 \n301 -0.385744 -0.271934 -0.385744 \n302 0.425784 -0.425784 0.0937993 \n303 -0.538749 0.136787 0.249253 \n304 0.550008 -0.257409 0.0471104 \n305 0.345554 -0.231715 0.445393 \n306 0.140941 -0.043793 -0.59041 \n307 -0.345554 -0.231715 0.445393 \n308 0.043793 -0.59041 -0.140941 \n309 0.136787 -0.249253 -0.538749 \n310 0.132421 0.132421 -0.579255 \n311 0.315438 -0.415407 -0.315438 \n312 -0.425784 -0.0937993 -0.425784 \n313 0.385744 -0.385744 0.271934 \n314 -0.476089 0.0471868 0.377416 \n315 -0.465691 -0.367021 -0.140757 \n316 0.367021 -0.140757 0.465691 \n317 0.0880064 -1.81288e-16 -0.601832 \n318 0.518174 0.320444 -6.6765e-17 \n319 -0.043793 -0.59041 -0.140941 \n320 0.572222 0.187783 0.0896566 \n321 -0.043129 -0.043129 0.604789 \n322 -0.227102 -0.517774 0.227102 \n323 -0.271934 -0.385744 0.385744 \n324 0.425784 0.425784 0.0937993 \n325 -0.043129 0.604789 0.043129 \n326 -0.497541 -0.298784 -0.185739 \n327 0.298784 -0.185739 0.497541 \n328 -0.140757 0.367021 0.465691 \n329 0.550008 0.257409 0.0471104 \n330 -4.67222e-17 -0.601832 -0.0880064 \n331 0.572222 0.187783 -0.0896566 \n332 -0.132421 -0.132421 0.579255 \n333 -0.315438 -0.415407 0.315438 \n334 -0.0937993 -0.425784 0.425784 \n335 0.385744 0.385744 0.271934 \n336 -0.132421 0.579255 0.132421 \n337 -0.445393 -0.345554 -0.231715 \n338 -0.043793 -0.59041 0.140941 \n339 -0.185739 0.298784 0.497541 \n340 0.550008 0.257409 -0.0471104 \n341 0.445393 0.231715 0.345554 \n342 0.538749 -0.249253 0.136787 \n343 -0.0471868 -0.476089 0.377416 \n344 0.227102 0.517774 -0.227102 \n345 -0.385744 -0.271934 0.385744 \n346 0.227102 -0.227102 -0.517774 \n347 -0.604789 0.043129 -0.043129 \n348 -0.231715 -0.345554 0.445393 \n349 0.043793 -0.59041 0.140941 \n350 -0.231715 0.345554 0.445393 \n351 -0.140941 -0.043793 0.59041 \n352 0.465691 0.140757 0.367021 \n353 0.476089 -0.377416 0.0471868 \n354 -0.136787 -0.538749 0.249253 \n355 0.315438 0.415407 -0.315438 \n356 -0.425784 -0.0937993 0.425784 \n357 0.315438 -0.315438 -0.415407 \n358 -0.579255 0.132421 -0.132421 \n359 -0.140757 -0.367021 0.465691 \n360 -6.7966e-17 -0.601832 0.0880064 \n361 0.320444 -1.51359e-16 -0.518174 \n362 -0.140941 0.043793 0.59041 \n363 0.497541 0.185739 0.298784 \n364 -0.0471868 0.377416 0.476089 \n365 -0.0896566 0.187783 -0.572222 \n366 0.476089 0.0471868 -0.377416 \n367 0.249253 -0.538749 -0.136787 \n368 -0.227102 0.227102 -0.517774 \n369 -0.604789 -0.043129 0.043129 \n370 -0.185739 -0.298784 0.497541 \n371 0.345554 0.231715 0.445393 \n372 0.257409 0.0471104 -0.550008 \n373 -0.0880064 8.19325e-17 0.601832 \n374 -2.68478e-17 -0.518174 -0.320444 \n375 -0.136787 0.249253 0.538749 \n376 0.0896566 0.187783 -0.572222 \n377 0.538749 0.136787 -0.249253 \n378 0.377416 -0.476089 -0.0471868 \n379 -0.315438 0.315438 -0.415407 \n380 -0.579255 -0.132421 0.132421 \n381 -0.59041 -0.140941 -0.043793 \n382 0.298784 0.185739 0.497541 \n383 0.257409 -0.0471104 -0.550008 \n384 0.231715 0.345554 -0.445393 \n385 0.0471104 -0.550008 -0.257409 \n386 -0.572222 -0.0896566 0.187783 \n387 -0.136787 -0.249253 0.538749 \n388 0.0896566 0.572222 -0.187783 \n389 0.377416 0.0471868 -0.476089 \n390 -0.538749 -0.249253 0.136787 \n391 0.604789 0.043129 -0.043129 \n392 -0.59041 -0.140941 0.043793 \n393 0.367021 0.140757 0.465691 \n394 -0.043793 0.140941 0.59041 \n395 0.140757 0.367021 -0.465691 \n396 -0.0471104 -0.550008 -0.257409 \n397 -0.572222 0.0896566 0.187783 \n398 -0.0471868 -0.377416 0.476089 \n399 -0.0896566 0.572222 -0.187783 \n400 0.249253 0.136787 -0.538749 \n401 -0.476089 -0.377416 0.0471868 \n402 0.579255 0.132421 -0.132421 \n403 -0.601832 -0.0880064 8.65333e-17 \n404 -6.34285e-17 -0.518174 0.320444 \n405 0.043793 0.140941 0.59041 \n406 0.185739 0.298784 -0.497541 \n407 0.465691 -0.140757 0.367021 \n408 -0.385744 -0.385744 -0.271934 \n409 -0.476089 0.0471868 -0.377416 \n410 0.136787 0.538749 0.249253 \n411 0.572222 -0.0896566 0.187783 \n412 -0.377416 -0.476089 -0.0471868 \n413 -0.136787 -0.249253 -0.538749 \n414 0.231715 -0.345554 0.445393 \n415 -0.0471104 -0.550008 0.257409 \n416 1.45663e-17 0.0880064 0.601832 \n417 -0.320444 -2.20514e-17 0.518174 \n418 0.497541 -0.185739 0.298784 \n419 -0.425784 -0.425784 -0.0937993 \n420 -0.538749 0.136787 -0.249253 \n421 0.0471868 0.476089 0.377416 \n422 0.572222 0.0896566 0.187783 \n423 -0.249253 -0.538749 -0.136787 \n424 -0.0471868 -0.377416 -0.476089 \n425 0.185739 -0.298784 0.497541 \n426 0.0471104 -0.550008 0.257409 \n427 -0.345554 -0.445393 0.231715 \n428 -0.257409 -0.0471104 0.550008 \n429 0.445393 -0.231715 0.345554 \n430 -0.271934 0.385744 -0.385744 \n431 0.227102 -0.227102 0.517774 \n432 -0.377416 -0.0471868 0.476089 \n433 0.249253 -0.136787 0.538749 \n434 -0.187783 -0.0896566 -0.572222 \n435 0.476089 0.377416 0.0471868 \n436 0.140757 -0.367021 0.465691 \n437 0.140941 0.043793 0.59041 \n438 -0.367021 -0.465691 0.140757 \n439 -0.257409 0.0471104 0.550008 \n440 -0.445393 0.231715 0.345554 \n441 -0.0937993 0.425784 -0.425784 \n442 0.315438 -0.315438 0.415407 \n443 -0.249253 -0.136787 0.538749 \n444 0.377416 -0.0471868 0.476089 \n445 -0.187783 0.0896566 -0.572222 \n446 0.538749 0.249253 0.136787 \n447 -0.518174 -0.320444 -1.95339e-17 \n448 0.140941 -0.043793 0.59041 \n449 -0.298784 -0.497541 0.185739 \n450 -0.140757 0.367021 -0.465691 \n451 -0.465691 0.140757 0.367021 \n452 0.385744 -0.271934 -0.385744 \n453 -0.425784 0.425784 0.0937993 \n454 0.604789 -0.043129 0.043129 \n455 -0.476089 0.377416 0.0471868 \n456 -0.249253 0.538749 0.136787 \n457 0.187783 0.572222 0.0896566 \n458 -0.550008 -0.257409 -0.0471104 \n459 0.0880064 -1.69762e-17 0.601832 \n460 5.07865e-17 0.320444 0.518174 \n461 -0.185739 0.298784 -0.497541 \n462 -0.497541 0.185739 0.298784 \n463 0.425784 -0.0937993 -0.425784 \n464 -0.385744 0.385744 0.271934 \n465 0.579255 -0.132421 0.132421 \n466 -0.538749 0.249253 0.136787 \n467 -0.377416 0.476089 0.0471868 \n468 0.187783 0.572222 -0.0896566 \n469 -0.550008 -0.257409 0.0471104 \n470 0.445393 -0.231715 -0.345554 \n471 -0.0471104 0.257409 0.550008 \n472 -0.231715 0.345554 -0.445393 \n473 0.59041 -0.043793 0.140941 \n474 -0.271934 0.385744 0.385744 \n475 -0.227102 -0.227102 -0.517774 \n476 -0.604789 -0.043129 -0.043129 \n477 0.0937993 -0.425784 -0.425784 \n478 0.0471868 -0.476089 -0.377416 \n479 0.136787 0.249253 0.538749 \n480 0.043793 -0.140941 0.59041 \n481 0.465691 -0.140757 -0.367021 \n482 0.0471104 0.257409 0.550008 \n483 -0.445393 0.345554 -0.231715 \n484 0.59041 0.043793 0.140941 \n485 -0.0937993 0.425784 0.425784 \n486 -0.315438 -0.315438 -0.415407 \n487 -0.579255 -0.132421 -0.132421 \n488 0.271934 -0.385744 -0.385744 \n489 0.136787 -0.538749 -0.249253 \n490 0.0471868 0.377416 0.476089 \n491 -0.043793 -0.140941 0.59041 \n492 0.497541 -0.185739 -0.298784 \n493 -0.367021 -0.465691 -0.140757 \n494 -0.465691 0.367021 -0.140757 \n495 0.601832 -9.49355e-18 0.0880064 \n496 0.385744 -0.271934 0.385744 \n497 0.227102 0.227102 -0.517774 \n498 -0.604789 0.043129 0.043129 \n499 -0.425784 0.0937993 -0.425784 \n500 0.043129 0.043129 0.604789 \n501 -0.476089 -0.0471868 0.377416 \n502 1.08987e-16 -0.0880064 0.601832 \n503 0.320444 -3.28597e-17 0.518174 \n504 -0.298784 -0.497541 -0.185739 \n505 -0.497541 0.298784 -0.185739 \n506 -0.465691 -0.140757 0.367021 \n507 0.425784 -0.0937993 0.425784 \n508 0.315438 0.315438 -0.415407 \n509 -0.579255 0.132421 0.132421 \n510 -0.385744 0.271934 -0.385744 \n511 0.132421 0.132421 0.579255 \n512 -0.538749 -0.136787 0.249253 \n513 -0.231715 -0.345554 -0.445393 \n514 0.257409 0.0471104 0.550008 \n515 -0.345554 -0.445393 -0.231715 \n516 -0.043793 0.140941 -0.59041 \n517 -0.497541 -0.185739 0.298784 \n518 0.0896566 -0.187783 -0.572222 \n519 -0.227102 -0.227102 0.517774 \n520 0.604789 -0.043129 -0.043129 \n521 0.0937993 -0.425784 0.425784 \n522 -0.043129 -0.604789 -0.043129 \n523 0.140941 0.59041 0.043793 \n524 -0.140757 -0.367021 -0.465691 \n525 0.257409 -0.0471104 0.550008 \n526 -0.445393 -0.231715 -0.345554 \n527 0.043793 0.140941 -0.59041 \n528 -0.445393 -0.231715 0.345554 \n529 -0.0896566 -0.187783 -0.572222 \n530 -0.315438 -0.315438 0.415407 \n531 0.579255 -0.132421 -0.132421 \n532 0.271934 -0.385744 0.385744 \n533 -0.132421 -0.579255 -0.132421 \n534 0.140941 0.59041 -0.043793 \n535 -0.185739 -0.298784 -0.497541 \n536 0.445393 0.231715 -0.345554 \n537 -0.465691 -0.140757 -0.367021 \n538 2.77187e-17 0.0880064 -0.601832 \n539 0.518174 -1.20677e-17 0.320444 \n540 0.136787 0.249253 -0.538749 \n541 -0.0896566 -0.572222 0.187783 \n542 0.604789 0.043129 0.043129 \n543 -0.425784 0.0937993 0.425784 \n544 0.043129 -0.604789 0.043129 \n545 0.0880064 0.601832 1.76832e-17 \n546 9.63855e-17 -0.320444 0.518174 \n547 0.497541 0.185739 -0.298784 \n548 -0.497541 -0.185739 -0.298784 \n549 -0.445393 0.345554 0.231715 \n550 0.550008 -0.0471104 0.257409 \n551 0.0471868 0.377416 -0.476089 \n552 0.0896566 -0.572222 0.187783 \n553 0.579255 0.132421 0.132421 \n554 -0.385744 0.271934 0.385744 \n555 0.132421 -0.579255 0.132421 \n556 0.345554 -0.445393 -0.231715 \n557 0.0471104 -0.257409 0.550008 \n558 0.465691 0.140757 -0.367021 \n559 -0.140941 -0.59041 -0.043793 \n560 -0.497541 0.298784 0.185739 \n561 0.550008 0.0471104 0.257409 \n562 0.476089 -0.377416 -0.0471868 \n563 -0.136787 0.538749 -0.249253 \n564 0.572222 0.0896566 -0.187783 \n565 0.377416 -0.476089 0.0471868 \n566 -0.043129 0.604789 -0.043129 \n567 0.367021 -0.465691 -0.140757 \n568 -0.0471104 -0.257409 0.550008 \n569 -0.345554 0.445393 0.231715 \n570 -0.140941 -0.59041 0.043793 \n571 -0.465691 0.367021 0.140757 \n572 -0.59041 -0.043793 0.140941 \n573 0.538749 -0.249253 -0.136787 \n574 -0.0471868 0.476089 -0.377416 \n575 0.572222 -0.0896566 -0.187783 \n576 0.249253 -0.538749 0.136787 \n577 -0.132421 0.579255 -0.132421 \n578 0.298784 -0.497541 -0.185739 \n579 0.231715 -0.345554 -0.445393 \n580 -0.367021 0.465691 0.140757 \n581 -0.0880064 -0.601832 8.03375e-17 \n582 -6.52521e-17 0.320444 -0.518174 \n583 -0.59041 0.043793 0.140941 \n584 -0.0896566 0.187783 0.572222 \n585 0.0471868 -0.377416 0.476089 \n586 0.538749 0.136787 0.249253 \n587 0.187783 0.0896566 -0.572222 \n588 -0.476089 -0.377416 -0.0471868 \n589 0.320444 0.518174 -1.33925e-16 \n590 0.185739 -0.298784 -0.497541 \n591 -0.298784 0.497541 0.185739 \n592 -0.445393 0.231715 -0.345554 \n593 -0.0471104 0.257409 -0.550008 \n594 -0.601832 2.54962e-17 0.0880064 \n595 0.0896566 0.187783 0.572222 \n596 0.136787 -0.249253 0.538749 \n597 0.476089 0.0471868 0.377416 \n598 0.187783 -0.0896566 -0.572222 \n599 -0.538749 -0.249253 -0.136787 \n600 0.257409 0.550008 0.0471104 \n601 0.140757 -0.367021 -0.465691 \n602 0.59041 0.043793 -0.140941 \n603 -0.497541 0.185739 -0.298784 \n604 0.0471104 0.257409 -0.550008 \n605 -0.345554 0.231715 -0.445393 \n606 -0.425784 -0.425784 0.0937993 \n607 -0.572222 0.0896566 -0.187783 \n608 -0.0471868 0.476089 0.377416 \n609 -0.249253 0.136787 -0.538749 \n610 -0.187783 -0.572222 -0.0896566 \n611 0.257409 0.550008 -0.0471104 \n612 0.231715 0.445393 0.345554 \n613 0.59041 -0.043793 -0.140941 \n614 -0.465691 0.140757 -0.367021 \n615 -0.59041 0.140941 0.043793 \n616 -0.367021 0.140757 -0.465691 \n617 -0.385744 -0.385744 0.271934 \n618 -0.572222 -0.0896566 -0.187783 \n619 -0.136787 0.538749 0.249253 \n620 -0.377416 0.0471868 -0.476089 \n621 -0.187783 -0.572222 0.0896566 \n622 0.345554 -0.445393 0.231715 \n623 0.140757 0.465691 0.367021 \n624 0.601832 1.79672e-17 -0.0880064 \n625 -0.320444 -0.518174 -1.08154e-16 \n626 -0.59041 0.140941 -0.043793 \n627 -0.298784 0.185739 -0.497541 \n628 0.0937993 0.425784 -0.425784 \n629 0.043129 0.604789 0.043129 \n630 -0.187783 -0.0896566 0.572222 \n631 0.377416 0.0471868 0.476089 \n632 0.249253 0.538749 -0.136787 \n633 0.298784 -0.497541 0.185739 \n634 0.185739 0.497541 0.298784 \n635 -0.367021 0.465691 -0.140757 \n636 -0.257409 -0.550008 -0.0471104 \n637 -0.601832 0.0880064 -2.08196e-17 \n638 -0.518174 -1.17754e-17 0.320444 \n639 0.271934 0.385744 -0.385744 \n640 0.132421 0.579255 0.132421 \n641 -0.187783 0.0896566 0.572222 \n642 0.249253 0.136787 0.538749 \n643 0.377416 0.476089 -0.0471868 \n644 0.367021 -0.465691 0.140757 \n645 0.043793 -0.140941 -0.59041 \n646 -0.298784 0.497541 -0.185739 \n647 -0.257409 -0.550008 0.0471104 \n648 -0.231715 0.445393 -0.345554 \n649 -0.550008 -0.0471104 0.257409 \n650 0.425784 0.0937993 -0.425784 \n651 -0.043129 0.043129 0.604789 \n652 0.517774 -0.227102 0.227102 \n653 -0.572222 0.187783 0.0896566 \n654 -0.377416 0.476089 -0.0471868 \n655 0.445393 -0.345554 0.231715 \n656 -0.043793 -0.140941 -0.59041 \n657 -0.345554 0.445393 -0.231715 \n658 -0.59041 0.043793 -0.140941 \n659 -0.140757 0.465691 -0.367021 \n660 -0.550008 0.0471104 0.257409 \n661 0.385744 0.271934 -0.385744 \n662 -0.132421 0.132421 0.579255 \n663 0.415407 -0.315438 0.315438 \n664 -0.572222 0.187783 -0.0896566 \n665 -0.249253 0.538749 -0.136787 \n666 0.465691 -0.367021 0.140757 \n667 4.34868e-17 -0.0880064 -0.601832 \n668 0.518174 -1.94879e-16 -0.320444 \n669 -0.59041 -0.043793 -0.140941 \n670 -0.185739 0.497541 -0.298784 \n671 -0.367021 -0.140757 -0.465691 \n672 0.0937993 0.425784 0.425784 \n673 0.043129 -0.604789 -0.043129 \n674 -0.517774 -0.227102 -0.227102 \n675 0.385744 -0.385744 -0.271934 \n676 0.0896566 -0.572222 -0.187783 \n677 0.497541 -0.298784 0.185739 \n678 -0.140757 0.465691 0.367021 \n679 0.550008 0.0471104 -0.257409 \n680 -0.601832 1.06019e-16 -0.0880064 \n681 -0.518174 0.320444 -1.1541e-16 \n682 -0.298784 -0.185739 -0.497541 \n683 0.271934 0.385744 0.385744 \n684 0.132421 -0.579255 -0.132421 \n685 -0.415407 -0.315438 -0.315438 \n686 0.425784 -0.425784 -0.0937993 \n687 -0.0896566 -0.572222 -0.187783 \n688 0.140941 -0.59041 0.043793 \n689 -0.185739 0.497541 0.298784 \n690 0.550008 -0.0471104 -0.257409 \n691 0.445393 0.345554 -0.231715 \n692 -0.550008 0.257409 0.0471104 \n693 -0.345554 -0.231715 -0.445393 \n694 0.425784 0.0937993 0.425784 \n695 -0.043129 -0.604789 0.043129 \n696 -0.517774 0.227102 0.227102 \n697 0.385744 0.385744 -0.271934 \n698 0.227102 0.227102 0.517774 \n699 0.140941 -0.59041 -0.043793 \n700 -0.231715 0.445393 0.345554 \n701 -0.140941 0.59041 -0.043793 \n702 0.465691 0.367021 -0.140757 \n703 -0.550008 0.257409 -0.0471104 \n704 -0.140941 -0.043793 -0.59041 \n705 0.385744 0.271934 0.385744 \n706 -0.132421 -0.579255 0.132421 \n707 -0.415407 0.315438 0.315438 \n708 0.425784 0.425784 -0.0937993 \n709 0.315438 0.315438 0.415407 \n710 0.0880064 -0.601832 7.34352e-17 \n711 -5.7854e-17 -0.320444 -0.518174 \n712 -0.140941 0.59041 0.043793 \n713 0.497541 0.298784 -0.185739 \n714 0.231715 0.445393 -0.345554 \n715 -0.140941 0.043793 -0.59041 \n716 0.136787 -0.538749 0.249253 \n717 0.043129 0.604789 -0.043129 \n718 0.517774 -0.227102 -0.227102 \n719 0.043129 -0.043129 -0.604789 \n720 -0.227102 -0.517774 -0.227102 \n721 0.465691 -0.367021 -0.140757 \n722 0.0471104 -0.257409 -0.550008 \n723 -0.0880064 0.601832 2.99876e-17 \n724 -0.518174 -4.24312e-17 -0.320444 \n725 0.185739 0.497541 -0.298784 \n726 -0.0880064 -1.95812e-17 -0.601832 \n727 0.0471868 -0.476089 0.377416 \n728 0.132421 0.579255 -0.132421 \n729 0.415407 -0.315438 -0.315438 \n730 0.132421 -0.132421 -0.579255 \n731 -0.315438 -0.415407 -0.315438 \n732 0.497541 -0.298784 -0.185739 \n733 -0.0471104 -0.257409 -0.550008 \n734 0.345554 -0.231715 -0.445393 \n735 -0.550008 0.0471104 -0.257409 \n736 0.140757 0.465691 -0.367021 \n737 -0.320444 -2.8719e-17 -0.518174 \n738 -0.0471868 0.377416 -0.476089 \n739 0.538749 -0.136787 -0.249253 \n740 0.517774 0.227102 0.227102 \n741 -0.043129 0.043129 -0.604789 \n742 0.227102 -0.517774 0.227102 \n743 0.445393 -0.345554 -0.231715 \n744 -0.043793 0.59041 0.140941 \n745 0.367021 -0.140757 -0.465691 \n746 -0.550008 -0.0471104 -0.257409 \n747 -0.231715 -0.445393 -0.345554 \n748 -0.257409 -0.0471104 -0.550008 \n749 -0.136787 0.249253 -0.538749 \n750 0.476089 -0.0471868 -0.377416 \n751 0.415407 0.315438 0.315438 \n752 -0.132421 0.132421 -0.579255 \n753 0.315438 -0.415407 0.315438 \n754 0.320444 -0.518174 3.51807e-17 \n755 0.043793 0.59041 0.140941 \n756 0.298784 -0.185739 -0.497541 \n757 0.445393 0.345554 0.231715 \n758 -0.140757 -0.465691 -0.367021 \n759 -0.257409 0.0471104 -0.550008 \n760 0.572222 -0.187783 -0.0896566 \n761 0.0471868 0.476089 -0.377416 \n762 0.249253 -0.136787 -0.538749 \n763 0.187783 -0.572222 0.0896566 \n764 -0.227102 0.517774 -0.227102 \n765 0.257409 -0.550008 0.0471104 \n766 3.47909e-17 0.601832 0.0880064 \n767 -0.320444 0.518174 8.65973e-17 \n768 0.497541 0.298784 0.185739 \n769 -0.185739 -0.497541 -0.298784 \n770 0.345554 0.445393 -0.231715 \n\nedges\n\n1 152 195 \n2 195 36 \n3 5 196 \n4 196 98 \n5 57 197 \n6 197 62 \n7 109 198 \n8 198 66 \n9 161 199 \n10 199 6 \n11 109 200 \n12 200 73 \n13 159 201 \n14 201 87 \n15 151 202 \n16 202 169 \n17 183 203 \n18 203 121 \n19 1 204 \n20 204 23 \n21 103 205 \n22 205 149 \n23 44 206 \n24 206 102 \n25 52 207 \n26 207 154 \n27 32 208 \n28 208 7 \n29 94 209 \n30 209 59 \n31 82 210 \n32 210 111 \n33 52 211 \n34 211 163 \n35 35 212 \n36 212 152 \n37 87 213 \n38 213 148 \n39 169 214 \n40 214 133 \n41 13 215 \n42 215 89 \n43 23 216 \n44 216 143 \n45 149 217 \n46 217 19 \n47 102 218 \n48 218 14 \n49 154 219 \n50 219 54 \n51 7 220 \n52 220 96 \n53 59 221 \n54 221 36 \n55 111 222 \n56 222 30 \n57 163 223 \n58 223 76 \n59 152 224 \n60 224 29 \n61 148 225 \n62 225 159 \n63 142 226 \n64 226 180 \n65 89 227 \n66 227 9 \n67 143 228 \n68 228 1 \n69 121 229 \n70 229 11 \n71 60 230 \n72 230 104 \n73 20 231 \n74 231 156 \n75 50 232 \n76 232 9 \n77 44 233 \n78 233 61 \n79 54 234 \n80 234 113 \n81 92 235 \n82 235 165 \n83 29 236 \n84 236 35 \n85 25 237 \n86 237 43 \n87 180 238 \n88 238 75 \n89 9 239 \n90 239 13 \n91 83 240 \n92 240 15 \n93 11 241 \n94 241 167 \n95 104 242 \n96 242 6 \n97 156 243 \n98 243 32 \n99 9 244 \n100 244 86 \n101 61 245 \n102 245 34 \n103 113 246 \n104 246 64 \n105 165 247 \n106 247 56 \n107 106 248 \n108 248 138 \n109 43 249 \n110 249 61 \n111 75 250 \n112 250 142 \n113 37 251 \n114 251 112 \n115 15 252 \n116 252 135 \n117 167 253 \n118 253 121 \n119 76 254 \n120 254 106 \n121 6 255 \n122 255 158 \n123 34 256 \n124 256 11 \n125 60 257 \n126 257 63 \n127 42 258 \n128 258 115 \n129 56 259 \n130 259 167 \n131 138 260 \n132 260 127 \n133 61 261 \n134 261 25 \n135 155 262 \n136 262 144 \n137 112 263 \n138 263 179 \n139 135 264 \n140 264 83 \n141 106 265 \n142 265 10 \n143 158 266 \n144 266 48 \n145 11 267 \n146 267 16 \n147 63 268 \n148 268 48 \n149 115 269 \n150 269 32 \n151 167 270 \n152 270 34 \n153 127 271 \n154 271 106 \n155 69 272 \n156 272 166 \n157 144 273 \n158 273 187 \n159 179 274 \n160 274 37 \n161 186 275 \n162 275 5 \n163 92 276 \n164 276 108 \n165 18 277 \n166 277 160 \n167 50 278 \n168 278 13 \n169 2 279 \n170 279 65 \n171 58 280 \n172 280 117 \n173 90 281 \n174 281 169 \n175 114 282 \n176 282 134 \n177 166 283 \n178 283 17 \n179 187 284 \n180 284 155 \n181 71 285 \n182 285 27 \n183 5 286 \n184 286 23 \n185 108 287 \n186 287 8 \n187 160 288 \n188 288 64 \n189 13 289 \n190 289 14 \n191 65 290 \n192 290 38 \n193 117 291 \n194 291 38 \n195 169 292 \n196 292 44 \n197 134 293 \n198 293 152 \n199 17 294 \n200 294 69 \n201 162 295 \n202 295 93 \n203 27 296 \n204 296 182 \n205 23 297 \n206 297 186 \n207 38 298 \n208 298 110 \n209 18 299 \n210 299 162 \n211 66 300 \n212 300 15 \n213 2 301 \n214 301 67 \n215 40 302 \n216 302 119 \n217 68 303 \n218 303 171 \n219 152 304 \n220 304 114 \n221 174 305 \n222 305 128 \n223 93 306 \n224 306 105 \n225 182 307 \n226 307 71 \n227 193 308 \n228 308 153 \n229 110 309 \n230 309 72 \n231 162 310 \n232 310 80 \n233 15 311 \n234 311 4 \n235 67 312 \n236 312 54 \n237 119 313 \n238 313 12 \n239 171 314 \n240 314 86 \n241 120 315 \n242 315 145 \n243 128 316 \n244 316 79 \n245 105 317 \n246 317 162 \n247 165 318 \n248 318 183 \n249 153 319 \n250 319 139 \n251 100 320 \n252 320 112 \n253 20 321 \n254 321 164 \n255 82 322 \n256 322 17 \n257 10 323 \n258 323 69 \n259 56 324 \n260 324 121 \n261 24 325 \n262 325 173 \n263 145 326 \n264 326 49 \n265 79 327 \n266 327 174 \n267 126 328 \n268 328 116 \n269 183 329 \n270 329 112 \n271 139 330 \n272 330 193 \n273 112 331 \n274 331 92 \n275 164 332 \n276 332 96 \n277 17 333 \n278 333 10 \n279 69 334 \n280 334 70 \n281 121 335 \n282 335 16 \n283 173 336 \n284 336 44 \n285 49 337 \n286 337 120 \n287 194 338 \n288 338 184 \n289 116 339 \n290 339 13 \n291 112 340 \n292 340 165 \n293 55 341 \n294 341 146 \n295 36 342 \n296 342 114 \n297 70 343 \n298 343 166 \n299 98 344 \n300 344 19 \n301 10 345 \n302 345 71 \n303 72 346 \n304 346 123 \n305 26 347 \n306 347 175 \n307 182 348 \n308 348 69 \n309 184 349 \n310 349 141 \n311 13 350 \n312 350 126 \n313 164 351 \n314 351 45 \n315 146 352 \n316 352 41 \n317 114 353 \n318 353 40 \n319 166 354 \n320 354 82 \n321 19 355 \n322 355 8 \n323 71 356 \n324 356 86 \n325 123 357 \n326 357 4 \n327 175 358 \n328 358 60 \n329 69 359 \n330 359 170 \n331 141 360 \n332 360 194 \n333 57 361 \n334 361 75 \n335 45 362 \n336 362 192 \n337 41 363 \n338 363 55 \n339 78 364 \n340 364 116 \n341 88 365 \n342 365 168 \n343 62 366 \n344 366 21 \n345 66 367 \n346 367 73 \n347 88 368 \n348 368 125 \n349 26 369 \n350 369 177 \n351 170 370 \n352 370 182 \n353 146 371 \n354 371 155 \n355 75 372 \n356 372 93 \n357 192 373 \n358 373 164 \n359 117 374 \n360 374 135 \n361 116 375 \n362 375 50 \n363 168 376 \n364 376 80 \n365 21 377 \n366 377 92 \n367 73 378 \n368 378 40 \n369 125 379 \n370 379 6 \n371 177 380 \n372 380 76 \n373 31 381 \n374 381 163 \n375 155 382 \n376 382 97 \n377 93 383 \n378 383 57 \n379 180 384 \n380 384 140 \n381 135 385 \n382 385 153 \n383 76 386 \n384 386 118 \n385 96 387 \n386 387 170 \n387 98 388 \n388 388 23 \n389 62 389 \n390 389 75 \n391 76 390 \n392 390 127 \n393 28 391 \n394 391 179 \n395 163 392 \n396 392 177 \n397 97 393 \n398 393 146 \n399 192 394 \n400 394 136 \n401 140 395 \n402 395 132 \n403 153 396 \n404 396 117 \n405 118 397 \n406 397 68 \n407 170 398 \n408 398 70 \n409 23 399 \n410 399 90 \n411 75 400 \n412 400 80 \n413 127 401 \n414 401 30 \n415 179 402 \n416 402 92 \n417 177 403 \n418 403 31 \n419 148 404 \n420 404 166 \n421 136 405 \n422 405 137 \n423 132 406 \n424 406 180 \n425 128 407 \n426 407 59 \n427 2 408 \n428 408 120 \n429 54 409 \n430 409 172 \n431 34 410 \n432 410 25 \n433 36 411 \n434 411 77 \n435 30 412 \n436 412 129 \n437 64 413 \n438 413 181 \n439 87 414 \n440 414 174 \n441 166 415 \n442 415 184 \n443 137 416 \n444 416 192 \n445 9 417 \n446 417 27 \n447 59 418 \n448 418 47 \n449 120 419 \n450 419 30 \n451 172 420 \n452 420 60 \n453 25 421 \n454 421 78 \n455 77 422 \n456 422 100 \n457 129 423 \n458 423 58 \n459 181 424 \n460 424 38 \n461 174 425 \n462 425 188 \n463 184 426 \n464 426 148 \n465 17 427 \n466 427 138 \n467 27 428 \n468 428 45 \n469 47 429 \n470 429 128 \n471 6 430 \n472 430 122 \n473 32 431 \n474 431 174 \n475 86 432 \n476 432 27 \n477 32 433 \n478 433 79 \n479 64 434 \n480 434 131 \n481 56 435 \n482 435 183 \n483 188 436 \n484 436 87 \n485 137 437 \n486 437 115 \n487 138 438 \n488 438 111 \n489 45 439 \n490 439 9 \n491 51 440 \n492 440 89 \n493 122 441 \n494 441 46 \n495 174 442 \n496 442 12 \n497 27 443 \n498 443 96 \n499 79 444 \n500 444 94 \n501 131 445 \n502 445 88 \n503 183 446 \n504 446 100 \n505 127 447 \n506 447 145 \n507 115 448 \n508 448 156 \n509 111 449 \n510 449 17 \n511 122 450 \n512 450 150 \n513 89 451 \n514 451 171 \n515 4 452 \n516 452 124 \n517 48 453 \n518 453 176 \n519 28 454 \n520 454 29 \n521 48 455 \n522 455 81 \n523 44 456 \n524 456 133 \n525 34 457 \n526 457 185 \n527 145 458 \n528 458 163 \n529 156 459 \n530 459 137 \n531 187 460 \n532 460 116 \n533 150 461 \n534 461 125 \n535 171 462 \n536 462 51 \n537 124 463 \n538 463 62 \n539 176 464 \n540 464 14 \n541 29 465 \n542 465 36 \n543 81 466 \n544 466 68 \n545 133 467 \n546 467 48 \n547 185 468 \n548 468 98 \n549 163 469 \n550 469 127 \n551 53 470 \n552 470 124 \n553 116 471 \n554 471 136 \n555 125 472 \n556 472 122 \n557 29 473 \n558 473 77 \n559 14 474 \n560 474 126 \n561 64 475 \n562 475 178 \n563 26 476 \n564 476 31 \n565 38 477 \n566 477 83 \n567 38 478 \n568 478 135 \n569 42 479 \n570 479 187 \n571 156 480 \n572 480 7 \n573 124 481 \n574 481 3 \n575 136 482 \n576 482 187 \n577 104 483 \n578 483 158 \n579 77 484 \n580 484 37 \n581 126 485 \n582 485 78 \n583 178 486 \n584 486 2 \n585 31 487 \n586 487 52 \n587 83 488 \n588 488 4 \n589 135 489 \n590 489 66 \n591 187 490 \n592 490 78 \n593 7 491 \n594 491 164 \n595 3 492 \n596 492 53 \n597 120 493 \n598 493 129 \n599 158 494 \n600 494 63 \n601 37 495 \n602 495 29 \n603 12 496 \n604 496 128 \n605 80 497 \n606 497 180 \n607 26 498 \n608 498 33 \n609 54 499 \n610 499 85 \n611 20 500 \n612 500 137 \n613 86 501 \n614 501 189 \n615 164 502 \n616 502 156 \n617 79 503 \n618 503 97 \n619 129 504 \n620 504 157 \n621 63 505 \n622 505 104 \n623 71 506 \n624 506 189 \n625 128 507 \n626 507 94 \n627 180 508 \n628 508 8 \n629 33 509 \n630 509 68 \n631 85 510 \n632 510 6 \n633 137 511 \n634 511 42 \n635 189 512 \n636 512 76 \n637 178 513 \n638 513 65 \n639 97 514 \n640 514 115 \n641 157 515 \n642 515 120 \n643 107 516 \n644 516 168 \n645 189 517 \n646 517 106 \n647 72 518 \n648 518 130 \n649 96 519 \n650 519 182 \n651 28 520 \n652 520 35 \n653 70 521 \n654 521 87 \n655 22 522 \n656 522 139 \n657 191 523 \n658 523 185 \n659 65 524 \n660 524 181 \n661 115 525 \n662 525 79 \n663 49 526 \n664 526 67 \n665 168 527 \n666 527 162 \n667 106 528 \n668 528 71 \n669 130 529 \n670 529 64 \n671 182 530 \n672 530 10 \n673 35 531 \n674 531 84 \n675 87 532 \n676 532 12 \n677 139 533 \n678 533 58 \n679 185 534 \n680 534 1 \n681 181 535 \n682 535 178 \n683 142 536 \n684 536 108 \n685 67 537 \n686 537 154 \n687 162 538 \n688 538 107 \n689 41 539 \n690 539 59 \n691 80 540 \n692 540 132 \n693 82 541 \n694 541 184 \n695 28 542 \n696 542 37 \n697 86 543 \n698 543 89 \n699 22 544 \n700 544 141 \n701 1 545 \n702 545 191 \n703 170 546 \n704 546 188 \n705 108 547 \n706 547 21 \n707 154 548 \n708 548 49 \n709 176 549 \n710 549 51 \n711 59 550 \n712 550 77 \n713 132 551 \n714 551 46 \n715 184 552 \n716 552 74 \n717 37 553 \n718 553 100 \n719 89 554 \n720 554 14 \n721 141 555 \n722 555 74 \n723 15 556 \n724 556 101 \n725 188 557 \n726 557 7 \n727 21 558 \n728 558 142 \n729 139 559 \n730 559 147 \n731 51 560 \n732 560 81 \n733 77 561 \n734 561 41 \n735 40 562 \n736 562 134 \n737 90 563 \n738 563 186 \n739 92 564 \n740 564 39 \n741 40 565 \n742 565 91 \n743 24 566 \n744 566 143 \n745 101 567 \n746 567 73 \n747 7 568 \n748 568 170 \n749 102 569 \n750 569 176 \n751 147 570 \n752 570 194 \n753 81 571 \n754 571 176 \n755 177 572 \n756 572 118 \n757 134 573 \n758 573 84 \n759 186 574 \n760 574 46 \n761 39 575 \n762 575 84 \n763 91 576 \n764 576 74 \n765 143 577 \n766 577 90 \n767 73 578 \n768 578 15 \n769 83 579 \n770 579 123 \n771 176 580 \n772 580 133 \n773 194 581 \n774 581 139 \n775 132 582 \n776 582 150 \n777 118 583 \n778 583 33 \n779 50 584 \n780 584 136 \n781 70 585 \n782 585 188 \n783 100 586 \n784 586 41 \n785 80 587 \n786 587 93 \n787 30 588 \n788 588 145 \n789 149 589 \n790 589 167 \n791 123 590 \n792 590 110 \n793 133 591 \n794 591 102 \n795 85 592 \n796 592 104 \n797 150 593 \n798 593 168 \n799 33 594 \n800 594 177 \n801 136 595 \n802 595 42 \n803 188 596 \n804 596 32 \n805 41 597 \n806 597 94 \n807 93 598 \n808 598 72 \n809 145 599 \n810 599 52 \n811 167 600 \n812 600 185 \n813 110 601 \n814 601 83 \n815 179 602 \n816 602 39 \n817 104 603 \n818 603 172 \n819 168 604 \n820 604 132 \n821 125 605 \n822 605 85 \n823 30 606 \n824 606 138 \n825 60 607 \n826 607 190 \n827 78 608 \n828 608 43 \n829 88 609 \n830 609 95 \n831 58 610 \n832 610 147 \n833 185 611 \n834 611 149 \n835 11 612 \n836 612 144 \n837 39 613 \n838 613 35 \n839 172 614 \n840 614 85 \n841 33 615 \n842 615 99 \n843 85 616 \n844 616 95 \n845 138 617 \n846 617 10 \n847 190 618 \n848 618 52 \n849 43 619 \n850 619 44 \n851 95 620 \n852 620 54 \n853 147 621 \n854 621 82 \n855 119 622 \n856 622 159 \n857 144 623 \n858 623 25 \n859 35 624 \n860 624 179 \n861 111 625 \n862 625 129 \n863 99 626 \n864 626 175 \n865 95 627 \n866 627 125 \n867 46 628 \n868 628 140 \n869 24 629 \n870 629 191 \n871 96 630 \n872 630 45 \n873 94 631 \n874 631 97 \n875 98 632 \n876 632 149 \n877 159 633 \n878 633 91 \n879 25 634 \n880 634 11 \n881 158 635 \n882 635 151 \n883 129 636 \n884 636 147 \n885 175 637 \n886 637 33 \n887 171 638 \n888 638 189 \n889 140 639 \n890 639 8 \n891 191 640 \n892 640 34 \n893 45 641 \n894 641 50 \n895 97 642 \n896 642 42 \n897 149 643 \n898 643 56 \n899 91 644 \n900 644 119 \n901 105 645 \n902 645 130 \n903 151 646 \n904 646 161 \n905 147 647 \n906 647 111 \n907 161 648 \n908 648 122 \n909 189 649 \n910 649 118 \n911 62 650 \n912 650 142 \n913 20 651 \n914 651 192 \n915 36 652 \n916 652 47 \n917 68 653 \n918 653 99 \n919 48 654 \n920 654 151 \n921 47 655 \n922 655 119 \n923 130 656 \n924 656 160 \n925 161 657 \n926 657 158 \n927 175 658 \n928 658 190 \n929 122 659 \n930 659 186 \n931 118 660 \n932 660 171 \n933 142 661 \n934 661 8 \n935 192 662 \n936 662 50 \n937 47 663 \n938 663 12 \n939 99 664 \n940 664 60 \n941 151 665 \n942 665 90 \n943 119 666 \n944 666 114 \n945 160 667 \n946 667 105 \n947 3 668 \n948 668 21 \n949 190 669 \n950 669 31 \n951 186 670 \n952 670 161 \n953 67 671 \n954 671 113 \n955 78 672 \n956 672 144 \n957 22 673 \n958 673 193 \n959 52 674 \n960 674 49 \n961 4 675 \n962 675 101 \n963 66 676 \n964 676 153 \n965 114 677 \n966 677 47 \n967 126 678 \n968 678 43 \n969 21 679 \n970 679 39 \n971 31 680 \n972 680 175 \n973 63 681 \n974 681 81 \n975 113 682 \n976 682 178 \n977 144 683 \n978 683 16 \n979 193 684 \n980 684 66 \n981 49 685 \n982 685 2 \n983 101 686 \n984 686 40 \n985 153 687 \n986 687 58 \n987 141 688 \n988 688 109 \n989 43 689 \n990 689 102 \n991 39 690 \n992 690 3 \n993 108 691 \n994 691 103 \n995 81 692 \n996 692 99 \n997 178 693 \n998 693 67 \n999 94 694 \n1000 694 146 \n1001 22 695 \n1002 695 194 \n1003 68 696 \n1004 696 51 \n1005 8 697 \n1006 697 103 \n1007 42 698 \n1008 698 155 \n1009 109 699 \n1010 699 193 \n1011 102 700 \n1012 700 126 \n1013 143 701 \n1014 701 169 \n1015 103 702 \n1016 702 165 \n1017 99 703 \n1018 703 63 \n1019 160 704 \n1020 704 131 \n1021 146 705 \n1022 705 16 \n1023 194 706 \n1024 706 82 \n1025 51 707 \n1026 707 14 \n1027 103 708 \n1028 708 56 \n1029 155 709 \n1030 709 16 \n1031 193 710 \n1032 710 141 \n1033 181 711 \n1034 711 110 \n1035 169 712 \n1036 712 173 \n1037 165 713 \n1038 713 108 \n1039 140 714 \n1040 714 19 \n1041 131 715 \n1042 715 107 \n1043 74 716 \n1044 716 148 \n1045 24 717 \n1046 717 1 \n1047 84 718 \n1048 718 53 \n1049 18 719 \n1050 719 105 \n1051 58 720 \n1052 720 157 \n1053 101 721 \n1054 721 134 \n1055 110 722 \n1056 722 130 \n1057 173 723 \n1058 723 143 \n1059 154 724 \n1060 724 172 \n1061 19 725 \n1062 725 5 \n1063 107 726 \n1064 726 160 \n1065 148 727 \n1066 727 70 \n1067 1 728 \n1068 728 98 \n1069 53 729 \n1070 729 4 \n1071 105 730 \n1072 730 72 \n1073 157 731 \n1074 731 2 \n1075 134 732 \n1076 732 53 \n1077 130 733 \n1078 733 181 \n1079 123 734 \n1080 734 124 \n1081 172 735 \n1082 735 190 \n1083 5 736 \n1084 736 140 \n1085 95 737 \n1086 737 113 \n1087 46 738 \n1088 738 150 \n1089 84 739 \n1090 739 3 \n1091 100 740 \n1092 740 55 \n1093 18 741 \n1094 741 107 \n1095 74 742 \n1096 742 159 \n1097 53 743 \n1098 743 101 \n1099 173 744 \n1100 744 61 \n1101 124 745 \n1102 745 57 \n1103 190 746 \n1104 746 154 \n1105 157 747 \n1106 747 65 \n1107 113 748 \n1108 748 131 \n1109 150 749 \n1110 749 88 \n1111 3 750 \n1112 750 62 \n1113 55 751 \n1114 751 16 \n1115 107 752 \n1116 752 88 \n1117 159 753 \n1118 753 12 \n1119 73 754 \n1120 754 91 \n1121 61 755 \n1122 755 191 \n1123 57 756 \n1124 756 123 \n1125 121 757 \n1126 757 55 \n1127 65 758 \n1128 758 117 \n1129 131 759 \n1130 759 95 \n1131 84 760 \n1132 760 152 \n1133 46 761 \n1134 761 5 \n1135 72 762 \n1136 762 57 \n1137 74 763 \n1138 763 109 \n1139 90 764 \n1140 764 161 \n1141 91 765 \n1142 765 109 \n1143 191 766 \n1144 766 173 \n1145 133 767 \n1146 767 151 \n1147 55 768 \n1148 768 183 \n1149 117 769 \n1150 769 157 \n1151 19 770 \n1152 770 103 \n1153 500 416 \n1154 416 651 \n1155 651 500 \n1156 640 457 \n1157 457 523 \n1158 523 640 \n1159 394 405 \n1160 405 416 \n1161 416 394 \n1162 468 728 \n1163 728 534 \n1164 534 468 \n1165 333 617 \n1166 617 427 \n1167 427 333 \n1168 717 545 \n1169 545 629 \n1170 629 717 \n1171 606 438 \n1172 438 222 \n1173 222 606 \n1174 523 534 \n1175 534 545 \n1176 545 523 \n1177 210 449 \n1178 449 322 \n1179 322 210 \n1180 311 675 \n1181 675 556 \n1182 556 311 \n1183 427 438 \n1184 438 449 \n1185 449 427 \n1186 686 378 \n1187 378 567 \n1188 567 686 \n1189 490 364 \n1190 364 460 \n1191 460 490 \n1192 367 578 \n1193 578 300 \n1194 300 367 \n1195 375 584 \n1196 584 471 \n1197 471 375 \n1198 556 567 \n1199 567 578 \n1200 578 556 \n1201 595 479 \n1202 479 482 \n1203 482 595 \n1204 643 259 \n1205 259 589 \n1206 589 643 \n1207 460 471 \n1208 471 482 \n1209 482 460 \n1210 270 457 \n1211 457 600 \n1212 600 270 \n1213 419 412 \n1214 412 493 \n1215 493 419 \n1216 468 632 \n1217 632 611 \n1218 611 468 \n1219 423 720 \n1220 720 504 \n1221 504 423 \n1222 589 600 \n1223 600 611 \n1224 611 589 \n1225 731 408 \n1226 408 515 \n1227 515 731 \n1228 313 753 \n1229 753 622 \n1230 622 313 \n1231 493 504 \n1232 504 515 \n1233 515 493 \n1234 742 633 \n1235 633 576 \n1236 576 742 \n1237 685 301 \n1238 301 526 \n1239 526 685 \n1240 565 644 \n1241 644 302 \n1242 302 565 \n1243 312 219 \n1244 219 537 \n1245 537 312 \n1246 622 633 \n1247 633 644 \n1248 644 622 \n1249 207 548 \n1250 548 674 \n1251 674 207 \n1252 663 313 \n1253 313 655 \n1254 655 663 \n1255 526 537 \n1256 537 548 \n1257 548 526 \n1258 302 666 \n1259 666 353 \n1260 353 302 \n1261 533 610 \n1262 610 559 \n1263 559 533 \n1264 342 677 \n1265 677 652 \n1266 652 342 \n1267 621 706 \n1268 706 570 \n1269 570 621 \n1270 655 666 \n1271 666 677 \n1272 677 655 \n1273 695 581 \n1274 581 522 \n1275 522 695 \n1276 555 763 \n1277 763 688 \n1278 688 555 \n1279 559 570 \n1280 570 581 \n1281 581 559 \n1282 198 684 \n1283 684 699 \n1284 699 198 \n1285 510 242 \n1286 242 592 \n1287 592 510 \n1288 673 710 \n1289 710 544 \n1290 544 673 \n1291 230 603 \n1292 603 420 \n1293 420 230 \n1294 688 699 \n1295 699 710 \n1296 710 688 \n1297 409 614 \n1298 614 499 \n1299 499 409 \n1300 686 562 \n1301 562 721 \n1302 721 686 \n1303 592 603 \n1304 603 614 \n1305 614 592 \n1306 573 718 \n1307 718 732 \n1308 732 573 \n1309 222 412 \n1310 412 625 \n1311 625 222 \n1312 729 675 \n1313 675 743 \n1314 743 729 \n1315 423 610 \n1316 610 636 \n1317 636 423 \n1318 721 732 \n1319 732 743 \n1320 743 721 \n1321 621 210 \n1322 210 647 \n1323 647 621 \n1324 378 565 \n1325 565 754 \n1326 754 378 \n1327 625 636 \n1328 636 647 \n1329 647 625 \n1330 576 763 \n1331 763 765 \n1332 765 576 \n1333 358 607 \n1334 607 658 \n1335 658 358 \n1336 198 367 \n1337 367 200 \n1338 200 198 \n1339 618 487 \n1340 487 669 \n1341 669 618 \n1342 754 765 \n1343 765 200 \n1344 200 754 \n1345 476 680 \n1346 680 347 \n1347 347 476 \n1348 531 760 \n1349 760 212 \n1350 212 531 \n1351 658 669 \n1352 669 680 \n1353 680 658 \n1354 195 465 \n1355 465 224 \n1356 224 195 \n1357 287 697 \n1358 697 691 \n1359 691 287 \n1360 454 236 \n1361 236 520 \n1362 520 454 \n1363 708 247 \n1364 247 702 \n1365 702 708 \n1366 212 224 \n1367 224 236 \n1368 236 212 \n1369 235 713 \n1370 713 276 \n1371 276 235 \n1372 265 617 \n1373 617 248 \n1374 248 265 \n1375 691 702 \n1376 702 713 \n1377 713 691 \n1378 606 260 \n1379 260 401 \n1380 401 606 \n1381 219 409 \n1382 409 724 \n1383 724 219 \n1384 390 271 \n1385 271 254 \n1386 254 390 \n1387 420 607 \n1388 607 735 \n1389 735 420 \n1390 248 260 \n1391 260 271 \n1392 271 248 \n1393 618 207 \n1394 207 746 \n1395 746 618 \n1396 353 562 \n1397 562 282 \n1398 282 353 \n1399 724 735 \n1400 735 746 \n1401 746 724 \n1402 573 760 \n1403 760 293 \n1404 293 573 \n1405 335 751 \n1406 751 757 \n1407 757 335 \n1408 195 342 \n1409 342 304 \n1410 304 195 \n1411 740 768 \n1412 768 446 \n1413 446 740 \n1414 282 293 \n1415 293 304 \n1416 304 282 \n1417 435 203 \n1418 203 324 \n1419 324 435 \n1420 419 588 \n1421 588 315 \n1422 315 419 \n1423 757 768 \n1424 768 203 \n1425 203 757 \n1426 599 674 \n1427 674 326 \n1428 326 599 \n1429 289 554 \n1430 554 215 \n1431 215 289 \n1432 685 408 \n1433 408 337 \n1434 337 685 \n1435 543 227 \n1436 227 244 \n1437 244 543 \n1438 315 326 \n1439 326 337 \n1440 337 315 \n1441 232 239 \n1442 239 278 \n1443 278 232 \n1444 530 323 \n1445 323 348 \n1446 348 530 \n1447 215 227 \n1448 227 239 \n1449 239 215 \n1450 334 398 \n1451 398 359 \n1452 359 334 \n1453 553 320 \n1454 320 251 \n1455 251 553 \n1456 387 370 \n1457 370 519 \n1458 519 387 \n1459 331 402 \n1460 402 263 \n1461 263 331 \n1462 348 359 \n1463 359 370 \n1464 370 348 \n1465 391 274 \n1466 274 542 \n1467 542 391 \n1468 487 211 \n1469 211 381 \n1470 381 487 \n1471 251 263 \n1472 263 274 \n1473 274 251 \n1474 223 380 \n1475 380 392 \n1476 392 223 \n1477 356 432 \n1478 432 285 \n1479 285 356 \n1480 369 403 \n1481 403 476 \n1482 476 369 \n1483 443 519 \n1484 519 296 \n1485 296 443 \n1486 381 392 \n1487 392 403 \n1488 403 381 \n1489 530 345 \n1490 345 307 \n1491 307 530 \n1492 532 442 \n1493 442 414 \n1494 414 532 \n1495 285 296 \n1496 296 307 \n1497 307 285 \n1498 431 425 \n1499 425 596 \n1500 596 431 \n1501 247 435 \n1502 435 318 \n1503 318 247 \n1504 585 436 \n1505 436 521 \n1506 521 585 \n1507 446 320 \n1508 320 329 \n1509 329 446 \n1510 414 425 \n1511 425 436 \n1512 436 414 \n1513 331 235 \n1514 235 340 \n1515 340 331 \n1516 401 588 \n1517 588 447 \n1518 447 401 \n1519 318 329 \n1520 329 340 \n1521 340 318 \n1522 599 211 \n1523 211 458 \n1524 458 599 \n1525 332 630 \n1526 630 351 \n1527 351 332 \n1528 223 390 \n1529 390 469 \n1530 469 223 \n1531 641 662 \n1532 662 362 \n1533 362 641 \n1534 447 458 \n1535 458 469 \n1536 469 447 \n1537 651 373 \n1538 373 321 \n1539 321 651 \n1540 243 208 \n1541 208 480 \n1542 480 243 \n1543 351 362 \n1544 362 373 \n1545 373 351 \n1546 220 332 \n1547 332 491 \n1548 491 220 \n1549 508 639 \n1550 639 384 \n1551 384 508 \n1552 321 502 \n1553 502 231 \n1554 231 321 \n1555 628 395 \n1556 395 551 \n1557 551 628 \n1558 480 491 \n1559 491 502 \n1560 502 480 \n1561 540 406 \n1562 406 497 \n1563 497 540 \n1564 486 279 \n1565 279 513 \n1566 513 486 \n1567 384 395 \n1568 395 406 \n1569 406 384 \n1570 290 424 \n1571 424 524 \n1572 524 290 \n1573 244 432 \n1574 432 417 \n1575 417 244 \n1576 413 535 \n1577 535 475 \n1578 475 413 \n1579 443 630 \n1580 630 428 \n1581 428 443 \n1582 513 524 \n1583 524 535 \n1584 535 513 \n1585 641 232 \n1586 232 439 \n1587 439 641 \n1588 398 585 \n1589 585 546 \n1590 546 398 \n1591 417 428 \n1592 428 439 \n1593 439 417 \n1594 596 208 \n1595 208 557 \n1596 557 596 \n1597 441 738 \n1598 738 450 \n1599 450 441 \n1600 220 387 \n1601 387 568 \n1602 568 220 \n1603 749 368 \n1604 368 461 \n1605 461 749 \n1606 546 557 \n1607 557 568 \n1608 568 546 \n1609 379 430 \n1610 430 472 \n1611 472 379 \n1612 488 357 \n1613 357 579 \n1614 579 488 \n1615 450 461 \n1616 461 472 \n1617 472 450 \n1618 346 590 \n1619 590 309 \n1620 309 346 \n1621 242 255 \n1622 255 483 \n1623 483 242 \n1624 298 601 \n1625 601 477 \n1626 477 298 \n1627 266 268 \n1628 268 494 \n1629 494 266 \n1630 579 590 \n1631 590 601 \n1632 601 579 \n1633 257 505 \n1634 505 230 \n1635 230 257 \n1636 267 683 \n1637 683 612 \n1638 612 267 \n1639 483 494 \n1640 494 505 \n1641 505 483 \n1642 672 623 \n1643 623 421 \n1644 421 672 \n1645 752 365 \n1646 365 516 \n1647 516 752 \n1648 410 634 \n1649 634 256 \n1650 256 410 \n1651 376 310 \n1652 310 527 \n1653 527 376 \n1654 612 623 \n1655 623 634 \n1656 634 612 \n1657 299 538 \n1658 538 741 \n1659 741 299 \n1660 730 518 \n1661 518 645 \n1662 645 730 \n1663 516 527 \n1664 527 538 \n1665 538 516 \n1666 529 288 \n1667 288 656 \n1668 656 529 \n1669 464 707 \n1670 707 549 \n1671 549 464 \n1672 277 667 \n1673 667 719 \n1674 719 277 \n1675 696 560 \n1676 560 466 \n1677 466 696 \n1678 645 656 \n1679 656 667 \n1680 667 645 \n1681 455 571 \n1682 571 453 \n1683 453 455 \n1684 485 608 \n1685 608 678 \n1686 678 485 \n1687 549 560 \n1688 560 571 \n1689 571 549 \n1690 619 206 \n1691 206 689 \n1692 689 619 \n1693 551 738 \n1694 738 582 \n1695 582 551 \n1696 218 474 \n1697 474 700 \n1698 700 218 \n1699 749 365 \n1700 365 593 \n1701 593 749 \n1702 678 689 \n1703 689 700 \n1704 700 678 \n1705 376 540 \n1706 540 604 \n1707 604 376 \n1708 424 298 \n1709 298 711 \n1710 711 424 \n1711 582 593 \n1712 593 604 \n1713 604 582 \n1714 309 518 \n1715 518 722 \n1716 722 309 \n1717 509 653 \n1718 653 615 \n1719 615 509 \n1720 529 413 \n1721 413 733 \n1722 733 529 \n1723 664 358 \n1724 358 626 \n1725 626 664 \n1726 711 722 \n1727 722 733 \n1728 733 711 \n1729 347 637 \n1730 637 498 \n1731 498 347 \n1732 336 233 \n1733 233 744 \n1734 744 336 \n1735 615 626 \n1736 626 637 \n1737 637 615 \n1738 245 640 \n1739 640 755 \n1740 755 245 \n1741 199 430 \n1742 430 648 \n1743 648 199 \n1744 629 766 \n1745 766 325 \n1746 325 629 \n1747 441 574 \n1748 574 659 \n1749 659 441 \n1750 744 755 \n1751 755 766 \n1752 766 744 \n1753 563 670 \n1754 670 764 \n1755 764 563 \n1756 753 532 \n1757 532 201 \n1758 201 753 \n1759 648 659 \n1760 659 670 \n1761 670 648 \n1762 521 213 \n1763 213 727 \n1764 727 521 \n1765 268 455 \n1766 455 681 \n1767 681 268 \n1768 716 225 \n1769 225 742 \n1770 742 716 \n1771 466 653 \n1772 653 692 \n1773 692 466 \n1774 201 213 \n1775 213 225 \n1776 225 201 \n1777 664 257 \n1778 257 703 \n1779 703 664 \n1780 421 608 \n1781 608 237 \n1782 237 421 \n1783 681 692 \n1784 692 703 \n1785 703 681 \n1786 619 233 \n1787 233 249 \n1788 249 619 \n1789 639 355 \n1790 355 714 \n1791 714 639 \n1792 245 410 \n1793 410 261 \n1794 261 245 \n1795 344 725 \n1796 725 196 \n1797 196 344 \n1798 237 249 \n1799 249 261 \n1800 261 237 \n1801 761 736 \n1802 736 628 \n1803 628 761 \n1804 334 343 \n1805 343 272 \n1806 272 334 \n1807 714 725 \n1808 725 736 \n1809 736 714 \n1810 354 322 \n1811 322 283 \n1812 283 354 \n1813 731 279 \n1814 279 747 \n1815 747 731 \n1816 333 323 \n1817 323 294 \n1818 294 333 \n1819 290 291 \n1820 291 758 \n1821 758 290 \n1822 272 283 \n1823 283 294 \n1824 294 272 \n1825 280 769 \n1826 769 720 \n1827 720 280 \n1828 442 496 \n1829 496 305 \n1830 305 442 \n1831 747 758 \n1832 758 769 \n1833 769 747 \n1834 507 444 \n1835 444 316 \n1836 316 507 \n1837 728 388 \n1838 388 204 \n1839 204 728 \n1840 433 327 \n1841 327 431 \n1842 431 433 \n1843 399 577 \n1844 577 216 \n1845 216 399 \n1846 305 316 \n1847 316 327 \n1848 327 305 \n1849 566 228 \n1850 228 717 \n1851 717 566 \n1852 706 541 \n1853 541 338 \n1854 338 706 \n1855 204 216 \n1856 216 228 \n1857 228 204 \n1858 552 555 \n1859 555 349 \n1860 349 552 \n1861 488 311 \n1862 311 240 \n1863 240 488 \n1864 544 360 \n1865 360 695 \n1866 695 544 \n1867 300 252 \n1868 252 489 \n1869 489 300 \n1870 338 349 \n1871 349 360 \n1872 360 338 \n1873 478 264 \n1874 264 477 \n1875 477 478 \n1876 705 709 \n1877 709 371 \n1878 371 705 \n1879 240 252 \n1880 252 264 \n1881 264 240 \n1882 698 382 \n1883 382 642 \n1884 642 698 \n1885 574 761 \n1886 761 275 \n1887 275 574 \n1888 631 393 \n1889 393 694 \n1890 694 631 \n1891 196 388 \n1892 388 286 \n1893 286 196 \n1894 371 382 \n1895 382 393 \n1896 393 371 \n1897 399 563 \n1898 563 297 \n1899 297 399 \n1900 727 343 \n1901 343 404 \n1902 404 727 \n1903 275 286 \n1904 286 297 \n1905 297 275 \n1906 354 541 \n1907 541 415 \n1908 415 354 \n1909 684 676 \n1910 676 308 \n1911 308 684 \n1912 552 716 \n1913 716 426 \n1914 426 552 \n1915 687 533 \n1916 533 319 \n1917 319 687 \n1918 404 415 \n1919 415 426 \n1920 426 404 \n1921 522 330 \n1922 330 673 \n1923 673 522 \n1924 511 258 \n1925 258 437 \n1926 437 511 \n1927 308 319 \n1928 319 330 \n1929 330 308 \n1930 269 243 \n1931 243 448 \n1932 448 269 \n1933 751 705 \n1934 705 341 \n1935 341 751 \n1936 231 459 \n1937 459 500 \n1938 500 231 \n1939 694 352 \n1940 352 597 \n1941 597 694 \n1942 437 448 \n1943 448 459 \n1944 459 437 \n1945 586 363 \n1946 363 740 \n1947 740 586 \n1948 729 452 \n1949 452 470 \n1950 470 729 \n1951 341 352 \n1952 352 363 \n1953 363 341 \n1954 463 750 \n1955 750 481 \n1956 481 463 \n1957 291 478 \n1958 478 374 \n1959 374 291 \n1960 739 492 \n1961 492 718 \n1962 718 739 \n1963 489 676 \n1964 676 385 \n1965 385 489 \n1966 470 481 \n1967 481 492 \n1968 492 470 \n1969 687 280 \n1970 280 396 \n1971 396 687 \n1972 444 631 \n1973 631 503 \n1974 503 444 \n1975 374 385 \n1976 385 396 \n1977 396 374 \n1978 642 258 \n1979 258 514 \n1980 514 642 \n1981 507 209 \n1982 209 407 \n1983 407 507 \n1984 269 433 \n1985 433 525 \n1986 525 269 \n1987 221 652 \n1988 652 418 \n1989 418 221 \n1990 503 514 \n1991 514 525 \n1992 525 503 \n1993 663 496 \n1994 496 429 \n1995 429 663 \n1996 661 287 \n1997 287 536 \n1998 536 661 \n1999 407 418 \n2000 418 429 \n2001 429 407 \n2002 276 547 \n2003 547 377 \n2004 377 276 \n2005 707 554 \n2006 554 440 \n2007 440 707 \n2008 366 558 \n2009 558 650 \n2010 650 366 \n2011 543 451 \n2012 451 314 \n2013 314 543 \n2014 536 547 \n2015 547 558 \n2016 558 536 \n2017 303 462 \n2018 462 696 \n2019 696 303 \n2020 218 464 \n2021 464 569 \n2022 569 218 \n2023 440 451 \n2024 451 462 \n2025 462 440 \n2026 453 580 \n2027 580 467 \n2028 467 453 \n2029 465 411 \n2030 411 473 \n2031 473 465 \n2032 456 591 \n2033 591 206 \n2034 206 456 \n2035 422 553 \n2036 553 484 \n2037 484 422 \n2038 569 580 \n2039 580 591 \n2040 591 569 \n2041 542 495 \n2042 495 454 \n2043 454 542 \n2044 402 564 \n2045 564 602 \n2046 602 402 \n2047 473 484 \n2048 484 495 \n2049 495 473 \n2050 575 531 \n2051 531 613 \n2052 613 575 \n2053 356 501 \n2054 501 506 \n2055 506 356 \n2056 520 624 \n2057 624 391 \n2058 391 520 \n2059 512 254 \n2060 254 517 \n2061 517 512 \n2062 602 613 \n2063 613 624 \n2064 624 602 \n2065 265 345 \n2066 345 528 \n2067 528 265 \n2068 266 654 \n2069 654 635 \n2070 635 266 \n2071 506 517 \n2072 517 528 \n2073 528 506 \n2074 665 764 \n2075 764 646 \n2076 646 665 \n2077 597 209 \n2078 209 539 \n2079 539 597 \n2080 199 255 \n2081 255 657 \n2082 657 199 \n2083 221 411 \n2084 411 550 \n2085 550 221 \n2086 635 646 \n2087 646 657 \n2088 657 635 \n2089 422 586 \n2090 586 561 \n2091 561 422 \n2092 750 366 \n2093 366 668 \n2094 668 750 \n2095 539 550 \n2096 550 561 \n2097 561 539 \n2098 377 564 \n2099 564 679 \n2100 679 377 \n2101 380 386 \n2102 386 572 \n2103 572 380 \n2104 575 739 \n2105 739 690 \n2106 690 575 \n2107 397 509 \n2108 509 583 \n2109 583 397 \n2110 668 679 \n2111 679 690 \n2112 690 668 \n2113 498 594 \n2114 594 369 \n2115 369 498 \n2116 577 281 \n2117 281 701 \n2118 701 577 \n2119 572 583 \n2120 583 594 \n2121 594 572 \n2122 292 336 \n2123 336 712 \n2124 712 292 \n2125 379 510 \n2126 510 605 \n2127 605 379 \n2128 325 723 \n2129 723 566 \n2130 566 325 \n2131 499 616 \n2132 616 620 \n2133 620 499 \n2134 701 712 \n2135 712 723 \n2136 723 701 \n2137 609 627 \n2138 627 368 \n2139 368 609 \n2140 357 452 \n2141 452 734 \n2142 734 357 \n2143 605 616 \n2144 616 627 \n2145 627 605 \n2146 463 197 \n2147 197 745 \n2148 745 463 \n2149 314 501 \n2150 501 638 \n2151 638 314 \n2152 762 756 \n2153 756 346 \n2154 346 762 \n2155 512 386 \n2156 386 649 \n2157 649 512 \n2158 734 745 \n2159 745 756 \n2160 756 734 \n2161 397 303 \n2162 303 660 \n2163 660 397 \n2164 467 654 \n2165 654 767 \n2166 767 467 \n2167 638 649 \n2168 649 660 \n2169 660 638 \n2170 665 281 \n2171 281 202 \n2172 202 665 \n2173 312 234 \n2174 234 671 \n2175 671 312 \n2176 292 456 \n2177 456 214 \n2178 214 292 \n2179 246 475 \n2180 475 682 \n2181 682 246 \n2182 767 202 \n2183 202 214 \n2184 214 767 \n2185 486 301 \n2186 301 693 \n2187 693 486 \n2188 661 508 \n2189 508 226 \n2190 226 661 \n2191 671 682 \n2192 682 693 \n2193 693 671 \n2194 497 238 \n2195 238 400 \n2196 400 497 \n2197 288 434 \n2198 434 704 \n2199 704 288 \n2200 389 250 \n2201 250 650 \n2202 650 389 \n2203 445 752 \n2204 752 715 \n2205 715 445 \n2206 226 238 \n2207 238 250 \n2208 250 226 \n2209 741 726 \n2210 726 277 \n2211 277 741 \n2212 709 683 \n2213 683 262 \n2214 262 709 \n2215 704 715 \n2216 715 726 \n2217 726 704 \n2218 672 273 \n2219 273 490 \n2220 490 672 \n2221 620 234 \n2222 234 737 \n2223 737 620 \n2224 479 284 \n2225 284 698 \n2226 698 479 \n2227 246 434 \n2228 434 748 \n2229 748 246 \n2230 262 273 \n2231 273 284 \n2232 284 262 \n2233 445 609 \n2234 609 759 \n2235 759 445 \n2236 310 587 \n2237 587 295 \n2238 295 310 \n2239 737 748 \n2240 748 759 \n2241 759 737 \n2242 598 730 \n2243 730 306 \n2244 306 598 \n2245 355 697 \n2246 697 770 \n2247 770 355 \n2248 719 317 \n2249 317 299 \n2250 299 719 \n2251 708 643 \n2252 643 205 \n2253 205 708 \n2254 295 306 \n2255 306 317 \n2256 317 295 \n2257 632 217 \n2258 217 344 \n2259 344 632 \n2260 485 364 \n2261 364 328 \n2262 328 485 \n2263 770 205 \n2264 205 217 \n2265 217 770 \n2266 375 278 \n2267 278 339 \n2268 339 375 \n2269 335 267 \n2270 267 229 \n2271 229 335 \n2272 289 474 \n2273 474 350 \n2274 350 289 \n2275 256 241 \n2276 241 270 \n2277 270 256 \n2278 328 339 \n2279 339 350 \n2280 350 328 \n2281 259 253 \n2282 253 324 \n2283 324 259 \n2284 197 389 \n2285 389 361 \n2286 361 197 \n2287 229 241 \n2288 241 253 \n2289 253 229 \n2290 400 587 \n2291 587 372 \n2292 372 400 \n2293 598 762 \n2294 762 383 \n2295 383 598 \n2296 361 372 \n2297 372 383 \n2298 383 361 \n2299 662 584 \n2300 584 394 \n2301 394 662 \n2302 595 511 \n2303 511 405 \n2304 405 595 \n\nfaces\n\n1 500 137 416 \n2 416 192 651 \n3 20 651 500 \n4 500 416 651 \n5 640 34 457 \n6 457 185 523 \n7 191 523 640 \n8 640 457 523 \n9 394 136 405 \n10 405 137 416 \n11 416 192 394 \n12 394 405 416 \n13 468 98 728 \n14 1 728 534 \n15 185 534 468 \n16 468 728 534 \n17 333 10 617 \n18 138 617 427 \n19 17 427 333 \n20 333 617 427 \n21 717 1 545 \n22 545 191 629 \n23 24 629 717 \n24 717 545 629 \n25 606 138 438 \n26 438 111 222 \n27 222 30 606 \n28 606 438 222 \n29 523 185 534 \n30 534 1 545 \n31 545 191 523 \n32 523 534 545 \n33 210 111 449 \n34 449 17 322 \n35 82 322 210 \n36 210 449 322 \n37 311 4 675 \n38 675 101 556 \n39 15 556 311 \n40 311 675 556 \n41 427 138 438 \n42 438 111 449 \n43 449 17 427 \n44 427 438 449 \n45 686 40 378 \n46 73 378 567 \n47 101 567 686 \n48 686 378 567 \n49 490 78 364 \n50 364 116 460 \n51 187 460 490 \n52 490 364 460 \n53 367 73 578 \n54 578 15 300 \n55 66 300 367 \n56 367 578 300 \n57 375 50 584 \n58 584 136 471 \n59 116 471 375 \n60 375 584 471 \n61 556 101 567 \n62 567 73 578 \n63 578 15 556 \n64 556 567 578 \n65 595 42 479 \n66 479 187 482 \n67 136 482 595 \n68 595 479 482 \n69 643 56 259 \n70 259 167 589 \n71 149 589 643 \n72 643 259 589 \n73 460 116 471 \n74 471 136 482 \n75 482 187 460 \n76 460 471 482 \n77 270 34 457 \n78 457 185 600 \n79 167 600 270 \n80 270 457 600 \n81 419 30 412 \n82 412 129 493 \n83 120 493 419 \n84 419 412 493 \n85 468 98 632 \n86 632 149 611 \n87 185 611 468 \n88 468 632 611 \n89 423 58 720 \n90 720 157 504 \n91 129 504 423 \n92 423 720 504 \n93 589 167 600 \n94 600 185 611 \n95 611 149 589 \n96 589 600 611 \n97 731 2 408 \n98 408 120 515 \n99 157 515 731 \n100 731 408 515 \n101 313 12 753 \n102 159 753 622 \n103 119 622 313 \n104 313 753 622 \n105 493 129 504 \n106 504 157 515 \n107 515 120 493 \n108 493 504 515 \n109 742 159 633 \n110 633 91 576 \n111 576 74 742 \n112 742 633 576 \n113 685 2 301 \n114 301 67 526 \n115 49 526 685 \n116 685 301 526 \n117 565 91 644 \n118 644 119 302 \n119 40 302 565 \n120 565 644 302 \n121 312 54 219 \n122 154 219 537 \n123 67 537 312 \n124 312 219 537 \n125 622 159 633 \n126 633 91 644 \n127 644 119 622 \n128 622 633 644 \n129 207 154 548 \n130 548 49 674 \n131 52 674 207 \n132 207 548 674 \n133 663 12 313 \n134 119 313 655 \n135 47 655 663 \n136 663 313 655 \n137 526 67 537 \n138 537 154 548 \n139 548 49 526 \n140 526 537 548 \n141 302 119 666 \n142 666 114 353 \n143 353 40 302 \n144 302 666 353 \n145 533 58 610 \n146 610 147 559 \n147 139 559 533 \n148 533 610 559 \n149 342 114 677 \n150 677 47 652 \n151 36 652 342 \n152 342 677 652 \n153 621 82 706 \n154 194 706 570 \n155 147 570 621 \n156 621 706 570 \n157 655 119 666 \n158 666 114 677 \n159 677 47 655 \n160 655 666 677 \n161 695 194 581 \n162 581 139 522 \n163 22 522 695 \n164 695 581 522 \n165 555 74 763 \n166 763 109 688 \n167 141 688 555 \n168 555 763 688 \n169 559 147 570 \n170 570 194 581 \n171 581 139 559 \n172 559 570 581 \n173 198 66 684 \n174 193 684 699 \n175 109 699 198 \n176 198 684 699 \n177 510 6 242 \n178 104 242 592 \n179 85 592 510 \n180 510 242 592 \n181 673 193 710 \n182 710 141 544 \n183 22 544 673 \n184 673 710 544 \n185 230 104 603 \n186 603 172 420 \n187 420 60 230 \n188 230 603 420 \n189 688 109 699 \n190 699 193 710 \n191 710 141 688 \n192 688 699 710 \n193 409 172 614 \n194 614 85 499 \n195 54 499 409 \n196 409 614 499 \n197 686 40 562 \n198 562 134 721 \n199 101 721 686 \n200 686 562 721 \n201 592 104 603 \n202 603 172 614 \n203 614 85 592 \n204 592 603 614 \n205 573 84 718 \n206 718 53 732 \n207 134 732 573 \n208 573 718 732 \n209 222 30 412 \n210 412 129 625 \n211 111 625 222 \n212 222 412 625 \n213 729 4 675 \n214 675 101 743 \n215 53 743 729 \n216 729 675 743 \n217 423 58 610 \n218 610 147 636 \n219 129 636 423 \n220 423 610 636 \n221 721 134 732 \n222 732 53 743 \n223 743 101 721 \n224 721 732 743 \n225 621 82 210 \n226 210 111 647 \n227 147 647 621 \n228 621 210 647 \n229 378 40 565 \n230 565 91 754 \n231 73 754 378 \n232 378 565 754 \n233 625 129 636 \n234 636 147 647 \n235 647 111 625 \n236 625 636 647 \n237 576 74 763 \n238 763 109 765 \n239 91 765 576 \n240 576 763 765 \n241 358 60 607 \n242 607 190 658 \n243 175 658 358 \n244 358 607 658 \n245 198 66 367 \n246 367 73 200 \n247 109 200 198 \n248 198 367 200 \n249 618 52 487 \n250 31 487 669 \n251 190 669 618 \n252 618 487 669 \n253 754 91 765 \n254 765 109 200 \n255 200 73 754 \n256 754 765 200 \n257 476 31 680 \n258 680 175 347 \n259 26 347 476 \n260 476 680 347 \n261 531 84 760 \n262 760 152 212 \n263 35 212 531 \n264 531 760 212 \n265 658 190 669 \n266 669 31 680 \n267 680 175 658 \n268 658 669 680 \n269 195 36 465 \n270 29 465 224 \n271 152 224 195 \n272 195 465 224 \n273 287 8 697 \n274 697 103 691 \n275 108 691 287 \n276 287 697 691 \n277 454 29 236 \n278 236 35 520 \n279 28 520 454 \n280 454 236 520 \n281 708 56 247 \n282 165 247 702 \n283 103 702 708 \n284 708 247 702 \n285 212 152 224 \n286 224 29 236 \n287 236 35 212 \n288 212 224 236 \n289 235 165 713 \n290 713 108 276 \n291 92 276 235 \n292 235 713 276 \n293 265 10 617 \n294 138 617 248 \n295 106 248 265 \n296 265 617 248 \n297 691 103 702 \n298 702 165 713 \n299 713 108 691 \n300 691 702 713 \n301 606 138 260 \n302 260 127 401 \n303 401 30 606 \n304 606 260 401 \n305 219 54 409 \n306 409 172 724 \n307 154 724 219 \n308 219 409 724 \n309 390 127 271 \n310 271 106 254 \n311 76 254 390 \n312 390 271 254 \n313 420 60 607 \n314 607 190 735 \n315 172 735 420 \n316 420 607 735 \n317 248 138 260 \n318 260 127 271 \n319 271 106 248 \n320 248 260 271 \n321 618 52 207 \n322 207 154 746 \n323 190 746 618 \n324 618 207 746 \n325 353 40 562 \n326 562 134 282 \n327 114 282 353 \n328 353 562 282 \n329 724 172 735 \n330 735 190 746 \n331 746 154 724 \n332 724 735 746 \n333 573 84 760 \n334 760 152 293 \n335 134 293 573 \n336 573 760 293 \n337 335 16 751 \n338 55 751 757 \n339 121 757 335 \n340 335 751 757 \n341 195 36 342 \n342 342 114 304 \n343 152 304 195 \n344 195 342 304 \n345 740 55 768 \n346 768 183 446 \n347 446 100 740 \n348 740 768 446 \n349 282 134 293 \n350 293 152 304 \n351 304 114 282 \n352 282 293 304 \n353 435 183 203 \n354 203 121 324 \n355 56 324 435 \n356 435 203 324 \n357 419 30 588 \n358 588 145 315 \n359 120 315 419 \n360 419 588 315 \n361 757 55 768 \n362 768 183 203 \n363 203 121 757 \n364 757 768 203 \n365 599 52 674 \n366 674 49 326 \n367 145 326 599 \n368 599 674 326 \n369 289 14 554 \n370 89 554 215 \n371 13 215 289 \n372 289 554 215 \n373 685 2 408 \n374 408 120 337 \n375 49 337 685 \n376 685 408 337 \n377 543 89 227 \n378 227 9 244 \n379 244 86 543 \n380 543 227 244 \n381 315 145 326 \n382 326 49 337 \n383 337 120 315 \n384 315 326 337 \n385 232 9 239 \n386 239 13 278 \n387 50 278 232 \n388 232 239 278 \n389 530 10 323 \n390 323 69 348 \n391 182 348 530 \n392 530 323 348 \n393 215 89 227 \n394 227 9 239 \n395 239 13 215 \n396 215 227 239 \n397 334 70 398 \n398 170 398 359 \n399 69 359 334 \n400 334 398 359 \n401 553 100 320 \n402 320 112 251 \n403 37 251 553 \n404 553 320 251 \n405 387 170 370 \n406 370 182 519 \n407 96 519 387 \n408 387 370 519 \n409 331 92 402 \n410 179 402 263 \n411 112 263 331 \n412 331 402 263 \n413 348 69 359 \n414 359 170 370 \n415 370 182 348 \n416 348 359 370 \n417 391 179 274 \n418 274 37 542 \n419 28 542 391 \n420 391 274 542 \n421 487 52 211 \n422 211 163 381 \n423 31 381 487 \n424 487 211 381 \n425 251 112 263 \n426 263 179 274 \n427 274 37 251 \n428 251 263 274 \n429 223 76 380 \n430 177 380 392 \n431 163 392 223 \n432 223 380 392 \n433 356 86 432 \n434 432 27 285 \n435 71 285 356 \n436 356 432 285 \n437 369 177 403 \n438 403 31 476 \n439 26 476 369 \n440 369 403 476 \n441 443 96 519 \n442 519 182 296 \n443 27 296 443 \n444 443 519 296 \n445 381 163 392 \n446 392 177 403 \n447 403 31 381 \n448 381 392 403 \n449 530 10 345 \n450 345 71 307 \n451 182 307 530 \n452 530 345 307 \n453 532 12 442 \n454 174 442 414 \n455 87 414 532 \n456 532 442 414 \n457 285 27 296 \n458 296 182 307 \n459 307 71 285 \n460 285 296 307 \n461 431 174 425 \n462 425 188 596 \n463 596 32 431 \n464 431 425 596 \n465 247 56 435 \n466 435 183 318 \n467 165 318 247 \n468 247 435 318 \n469 585 188 436 \n470 436 87 521 \n471 70 521 585 \n472 585 436 521 \n473 446 100 320 \n474 320 112 329 \n475 183 329 446 \n476 446 320 329 \n477 414 174 425 \n478 425 188 436 \n479 436 87 414 \n480 414 425 436 \n481 331 92 235 \n482 235 165 340 \n483 112 340 331 \n484 331 235 340 \n485 401 30 588 \n486 588 145 447 \n487 127 447 401 \n488 401 588 447 \n489 318 183 329 \n490 329 112 340 \n491 340 165 318 \n492 318 329 340 \n493 599 52 211 \n494 211 163 458 \n495 145 458 599 \n496 599 211 458 \n497 332 96 630 \n498 630 45 351 \n499 164 351 332 \n500 332 630 351 \n501 223 76 390 \n502 390 127 469 \n503 163 469 223 \n504 223 390 469 \n505 641 50 662 \n506 192 662 362 \n507 45 362 641 \n508 641 662 362 \n509 447 145 458 \n510 458 163 469 \n511 469 127 447 \n512 447 458 469 \n513 651 192 373 \n514 373 164 321 \n515 20 321 651 \n516 651 373 321 \n517 243 32 208 \n518 208 7 480 \n519 156 480 243 \n520 243 208 480 \n521 351 45 362 \n522 362 192 373 \n523 373 164 351 \n524 351 362 373 \n525 220 96 332 \n526 164 332 491 \n527 7 491 220 \n528 220 332 491 \n529 508 8 639 \n530 140 639 384 \n531 180 384 508 \n532 508 639 384 \n533 321 164 502 \n534 502 156 231 \n535 20 231 321 \n536 321 502 231 \n537 628 140 395 \n538 395 132 551 \n539 551 46 628 \n540 628 395 551 \n541 480 7 491 \n542 491 164 502 \n543 502 156 480 \n544 480 491 502 \n545 540 132 406 \n546 406 180 497 \n547 80 497 540 \n548 540 406 497 \n549 486 2 279 \n550 279 65 513 \n551 178 513 486 \n552 486 279 513 \n553 384 140 395 \n554 395 132 406 \n555 406 180 384 \n556 384 395 406 \n557 290 38 424 \n558 181 424 524 \n559 65 524 290 \n560 290 424 524 \n561 244 86 432 \n562 432 27 417 \n563 9 417 244 \n564 244 432 417 \n565 413 181 535 \n566 535 178 475 \n567 64 475 413 \n568 413 535 475 \n569 443 96 630 \n570 630 45 428 \n571 27 428 443 \n572 443 630 428 \n573 513 65 524 \n574 524 181 535 \n575 535 178 513 \n576 513 524 535 \n577 641 50 232 \n578 232 9 439 \n579 45 439 641 \n580 641 232 439 \n581 398 70 585 \n582 585 188 546 \n583 170 546 398 \n584 398 585 546 \n585 417 27 428 \n586 428 45 439 \n587 439 9 417 \n588 417 428 439 \n589 596 32 208 \n590 208 7 557 \n591 188 557 596 \n592 596 208 557 \n593 441 46 738 \n594 738 150 450 \n595 122 450 441 \n596 441 738 450 \n597 220 96 387 \n598 387 170 568 \n599 7 568 220 \n600 220 387 568 \n601 749 88 368 \n602 368 125 461 \n603 150 461 749 \n604 749 368 461 \n605 546 188 557 \n606 557 7 568 \n607 568 170 546 \n608 546 557 568 \n609 379 6 430 \n610 430 122 472 \n611 125 472 379 \n612 379 430 472 \n613 488 4 357 \n614 123 357 579 \n615 83 579 488 \n616 488 357 579 \n617 450 150 461 \n618 461 125 472 \n619 472 122 450 \n620 450 461 472 \n621 346 123 590 \n622 590 110 309 \n623 309 72 346 \n624 346 590 309 \n625 242 6 255 \n626 255 158 483 \n627 104 483 242 \n628 242 255 483 \n629 298 110 601 \n630 601 83 477 \n631 38 477 298 \n632 298 601 477 \n633 266 48 268 \n634 63 268 494 \n635 158 494 266 \n636 266 268 494 \n637 579 123 590 \n638 590 110 601 \n639 601 83 579 \n640 579 590 601 \n641 257 63 505 \n642 505 104 230 \n643 60 230 257 \n644 257 505 230 \n645 267 16 683 \n646 144 683 612 \n647 11 612 267 \n648 267 683 612 \n649 483 158 494 \n650 494 63 505 \n651 505 104 483 \n652 483 494 505 \n653 672 144 623 \n654 623 25 421 \n655 421 78 672 \n656 672 623 421 \n657 752 88 365 \n658 365 168 516 \n659 107 516 752 \n660 752 365 516 \n661 410 25 634 \n662 634 11 256 \n663 34 256 410 \n664 410 634 256 \n665 376 80 310 \n666 162 310 527 \n667 168 527 376 \n668 376 310 527 \n669 612 144 623 \n670 623 25 634 \n671 634 11 612 \n672 612 623 634 \n673 299 162 538 \n674 538 107 741 \n675 18 741 299 \n676 299 538 741 \n677 730 72 518 \n678 518 130 645 \n679 105 645 730 \n680 730 518 645 \n681 516 168 527 \n682 527 162 538 \n683 538 107 516 \n684 516 527 538 \n685 529 64 288 \n686 160 288 656 \n687 130 656 529 \n688 529 288 656 \n689 464 14 707 \n690 51 707 549 \n691 176 549 464 \n692 464 707 549 \n693 277 160 667 \n694 667 105 719 \n695 18 719 277 \n696 277 667 719 \n697 696 51 560 \n698 560 81 466 \n699 466 68 696 \n700 696 560 466 \n701 645 130 656 \n702 656 160 667 \n703 667 105 645 \n704 645 656 667 \n705 455 81 571 \n706 571 176 453 \n707 48 453 455 \n708 455 571 453 \n709 485 78 608 \n710 608 43 678 \n711 126 678 485 \n712 485 608 678 \n713 549 51 560 \n714 560 81 571 \n715 571 176 549 \n716 549 560 571 \n717 619 44 206 \n718 206 102 689 \n719 43 689 619 \n720 619 206 689 \n721 551 46 738 \n722 738 150 582 \n723 132 582 551 \n724 551 738 582 \n725 218 14 474 \n726 474 126 700 \n727 102 700 218 \n728 218 474 700 \n729 749 88 365 \n730 365 168 593 \n731 150 593 749 \n732 749 365 593 \n733 678 43 689 \n734 689 102 700 \n735 700 126 678 \n736 678 689 700 \n737 376 80 540 \n738 540 132 604 \n739 168 604 376 \n740 376 540 604 \n741 424 38 298 \n742 298 110 711 \n743 181 711 424 \n744 424 298 711 \n745 582 150 593 \n746 593 168 604 \n747 604 132 582 \n748 582 593 604 \n749 309 72 518 \n750 518 130 722 \n751 110 722 309 \n752 309 518 722 \n753 509 68 653 \n754 653 99 615 \n755 33 615 509 \n756 509 653 615 \n757 529 64 413 \n758 413 181 733 \n759 130 733 529 \n760 529 413 733 \n761 664 60 358 \n762 175 358 626 \n763 99 626 664 \n764 664 358 626 \n765 711 110 722 \n766 722 130 733 \n767 733 181 711 \n768 711 722 733 \n769 347 175 637 \n770 637 33 498 \n771 26 498 347 \n772 347 637 498 \n773 336 44 233 \n774 233 61 744 \n775 173 744 336 \n776 336 233 744 \n777 615 99 626 \n778 626 175 637 \n779 637 33 615 \n780 615 626 637 \n781 245 34 640 \n782 191 640 755 \n783 61 755 245 \n784 245 640 755 \n785 199 6 430 \n786 430 122 648 \n787 161 648 199 \n788 199 430 648 \n789 629 191 766 \n790 766 173 325 \n791 24 325 629 \n792 629 766 325 \n793 441 46 574 \n794 186 574 659 \n795 122 659 441 \n796 441 574 659 \n797 744 61 755 \n798 755 191 766 \n799 766 173 744 \n800 744 755 766 \n801 563 186 670 \n802 670 161 764 \n803 90 764 563 \n804 563 670 764 \n805 753 12 532 \n806 87 532 201 \n807 159 201 753 \n808 753 532 201 \n809 648 122 659 \n810 659 186 670 \n811 670 161 648 \n812 648 659 670 \n813 521 87 213 \n814 213 148 727 \n815 727 70 521 \n816 521 213 727 \n817 268 48 455 \n818 455 81 681 \n819 63 681 268 \n820 268 455 681 \n821 716 148 225 \n822 225 159 742 \n823 74 742 716 \n824 716 225 742 \n825 466 68 653 \n826 653 99 692 \n827 81 692 466 \n828 466 653 692 \n829 201 87 213 \n830 213 148 225 \n831 225 159 201 \n832 201 213 225 \n833 664 60 257 \n834 257 63 703 \n835 99 703 664 \n836 664 257 703 \n837 421 78 608 \n838 608 43 237 \n839 25 237 421 \n840 421 608 237 \n841 681 81 692 \n842 692 99 703 \n843 703 63 681 \n844 681 692 703 \n845 619 44 233 \n846 233 61 249 \n847 43 249 619 \n848 619 233 249 \n849 639 8 355 \n850 19 355 714 \n851 140 714 639 \n852 639 355 714 \n853 245 34 410 \n854 410 25 261 \n855 61 261 245 \n856 245 410 261 \n857 344 19 725 \n858 725 5 196 \n859 196 98 344 \n860 344 725 196 \n861 237 43 249 \n862 249 61 261 \n863 261 25 237 \n864 237 249 261 \n865 761 5 736 \n866 736 140 628 \n867 46 628 761 \n868 761 736 628 \n869 334 70 343 \n870 343 166 272 \n871 69 272 334 \n872 334 343 272 \n873 714 19 725 \n874 725 5 736 \n875 736 140 714 \n876 714 725 736 \n877 354 82 322 \n878 322 17 283 \n879 166 283 354 \n880 354 322 283 \n881 731 2 279 \n882 279 65 747 \n883 157 747 731 \n884 731 279 747 \n885 333 10 323 \n886 323 69 294 \n887 17 294 333 \n888 333 323 294 \n889 290 38 291 \n890 117 291 758 \n891 65 758 290 \n892 290 291 758 \n893 272 166 283 \n894 283 17 294 \n895 294 69 272 \n896 272 283 294 \n897 280 117 769 \n898 769 157 720 \n899 58 720 280 \n900 280 769 720 \n901 442 12 496 \n902 496 128 305 \n903 174 305 442 \n904 442 496 305 \n905 747 65 758 \n906 758 117 769 \n907 769 157 747 \n908 747 758 769 \n909 507 94 444 \n910 79 444 316 \n911 128 316 507 \n912 507 444 316 \n913 728 98 388 \n914 388 23 204 \n915 1 204 728 \n916 728 388 204 \n917 433 79 327 \n918 327 174 431 \n919 32 431 433 \n920 433 327 431 \n921 399 90 577 \n922 143 577 216 \n923 23 216 399 \n924 399 577 216 \n925 305 128 316 \n926 316 79 327 \n927 327 174 305 \n928 305 316 327 \n929 566 143 228 \n930 228 1 717 \n931 24 717 566 \n932 566 228 717 \n933 706 82 541 \n934 541 184 338 \n935 194 338 706 \n936 706 541 338 \n937 204 23 216 \n938 216 143 228 \n939 228 1 204 \n940 204 216 228 \n941 552 74 555 \n942 141 555 349 \n943 184 349 552 \n944 552 555 349 \n945 488 4 311 \n946 15 311 240 \n947 83 240 488 \n948 488 311 240 \n949 544 141 360 \n950 360 194 695 \n951 22 695 544 \n952 544 360 695 \n953 300 15 252 \n954 252 135 489 \n955 489 66 300 \n956 300 252 489 \n957 338 184 349 \n958 349 141 360 \n959 360 194 338 \n960 338 349 360 \n961 478 135 264 \n962 264 83 477 \n963 38 477 478 \n964 478 264 477 \n965 705 16 709 \n966 155 709 371 \n967 146 371 705 \n968 705 709 371 \n969 240 15 252 \n970 252 135 264 \n971 264 83 240 \n972 240 252 264 \n973 698 155 382 \n974 382 97 642 \n975 642 42 698 \n976 698 382 642 \n977 574 46 761 \n978 761 5 275 \n979 186 275 574 \n980 574 761 275 \n981 631 97 393 \n982 393 146 694 \n983 94 694 631 \n984 631 393 694 \n985 196 98 388 \n986 388 23 286 \n987 5 286 196 \n988 196 388 286 \n989 371 155 382 \n990 382 97 393 \n991 393 146 371 \n992 371 382 393 \n993 399 90 563 \n994 563 186 297 \n995 23 297 399 \n996 399 563 297 \n997 727 70 343 \n998 343 166 404 \n999 148 404 727 \n1000 727 343 404 \n1001 275 5 286 \n1002 286 23 297 \n1003 297 186 275 \n1004 275 286 297 \n1005 354 82 541 \n1006 541 184 415 \n1007 166 415 354 \n1008 354 541 415 \n1009 684 66 676 \n1010 676 153 308 \n1011 193 308 684 \n1012 684 676 308 \n1013 552 74 716 \n1014 716 148 426 \n1015 184 426 552 \n1016 552 716 426 \n1017 687 58 533 \n1018 139 533 319 \n1019 153 319 687 \n1020 687 533 319 \n1021 404 166 415 \n1022 415 184 426 \n1023 426 148 404 \n1024 404 415 426 \n1025 522 139 330 \n1026 330 193 673 \n1027 22 673 522 \n1028 522 330 673 \n1029 511 42 258 \n1030 258 115 437 \n1031 137 437 511 \n1032 511 258 437 \n1033 308 153 319 \n1034 319 139 330 \n1035 330 193 308 \n1036 308 319 330 \n1037 269 32 243 \n1038 156 243 448 \n1039 115 448 269 \n1040 269 243 448 \n1041 751 16 705 \n1042 146 705 341 \n1043 55 341 751 \n1044 751 705 341 \n1045 231 156 459 \n1046 459 137 500 \n1047 20 500 231 \n1048 231 459 500 \n1049 694 146 352 \n1050 352 41 597 \n1051 597 94 694 \n1052 694 352 597 \n1053 437 115 448 \n1054 448 156 459 \n1055 459 137 437 \n1056 437 448 459 \n1057 586 41 363 \n1058 363 55 740 \n1059 100 740 586 \n1060 586 363 740 \n1061 729 4 452 \n1062 452 124 470 \n1063 53 470 729 \n1064 729 452 470 \n1065 341 146 352 \n1066 352 41 363 \n1067 363 55 341 \n1068 341 352 363 \n1069 463 62 750 \n1070 3 750 481 \n1071 124 481 463 \n1072 463 750 481 \n1073 291 38 478 \n1074 478 135 374 \n1075 117 374 291 \n1076 291 478 374 \n1077 739 3 492 \n1078 492 53 718 \n1079 84 718 739 \n1080 739 492 718 \n1081 489 66 676 \n1082 676 153 385 \n1083 135 385 489 \n1084 489 676 385 \n1085 470 124 481 \n1086 481 3 492 \n1087 492 53 470 \n1088 470 481 492 \n1089 687 58 280 \n1090 280 117 396 \n1091 153 396 687 \n1092 687 280 396 \n1093 444 94 631 \n1094 631 97 503 \n1095 79 503 444 \n1096 444 631 503 \n1097 374 135 385 \n1098 385 153 396 \n1099 396 117 374 \n1100 374 385 396 \n1101 642 42 258 \n1102 258 115 514 \n1103 97 514 642 \n1104 642 258 514 \n1105 507 94 209 \n1106 209 59 407 \n1107 128 407 507 \n1108 507 209 407 \n1109 269 32 433 \n1110 433 79 525 \n1111 115 525 269 \n1112 269 433 525 \n1113 221 36 652 \n1114 652 47 418 \n1115 59 418 221 \n1116 221 652 418 \n1117 503 97 514 \n1118 514 115 525 \n1119 525 79 503 \n1120 503 514 525 \n1121 663 12 496 \n1122 496 128 429 \n1123 47 429 663 \n1124 663 496 429 \n1125 661 8 287 \n1126 108 287 536 \n1127 142 536 661 \n1128 661 287 536 \n1129 407 59 418 \n1130 418 47 429 \n1131 429 128 407 \n1132 407 418 429 \n1133 276 108 547 \n1134 547 21 377 \n1135 377 92 276 \n1136 276 547 377 \n1137 707 14 554 \n1138 89 554 440 \n1139 51 440 707 \n1140 707 554 440 \n1141 366 21 558 \n1142 558 142 650 \n1143 62 650 366 \n1144 366 558 650 \n1145 543 89 451 \n1146 451 171 314 \n1147 314 86 543 \n1148 543 451 314 \n1149 536 108 547 \n1150 547 21 558 \n1151 558 142 536 \n1152 536 547 558 \n1153 303 171 462 \n1154 462 51 696 \n1155 68 696 303 \n1156 303 462 696 \n1157 218 14 464 \n1158 176 464 569 \n1159 102 569 218 \n1160 218 464 569 \n1161 440 89 451 \n1162 451 171 462 \n1163 462 51 440 \n1164 440 451 462 \n1165 453 176 580 \n1166 580 133 467 \n1167 467 48 453 \n1168 453 580 467 \n1169 465 36 411 \n1170 411 77 473 \n1171 29 473 465 \n1172 465 411 473 \n1173 456 133 591 \n1174 591 102 206 \n1175 44 206 456 \n1176 456 591 206 \n1177 422 100 553 \n1178 37 553 484 \n1179 77 484 422 \n1180 422 553 484 \n1181 569 176 580 \n1182 580 133 591 \n1183 591 102 569 \n1184 569 580 591 \n1185 542 37 495 \n1186 495 29 454 \n1187 28 454 542 \n1188 542 495 454 \n1189 402 92 564 \n1190 564 39 602 \n1191 179 602 402 \n1192 402 564 602 \n1193 473 77 484 \n1194 484 37 495 \n1195 495 29 473 \n1196 473 484 495 \n1197 575 84 531 \n1198 35 531 613 \n1199 39 613 575 \n1200 575 531 613 \n1201 356 86 501 \n1202 501 189 506 \n1203 71 506 356 \n1204 356 501 506 \n1205 520 35 624 \n1206 624 179 391 \n1207 28 391 520 \n1208 520 624 391 \n1209 512 76 254 \n1210 254 106 517 \n1211 189 517 512 \n1212 512 254 517 \n1213 602 39 613 \n1214 613 35 624 \n1215 624 179 602 \n1216 602 613 624 \n1217 265 10 345 \n1218 345 71 528 \n1219 106 528 265 \n1220 265 345 528 \n1221 266 48 654 \n1222 654 151 635 \n1223 158 635 266 \n1224 266 654 635 \n1225 506 189 517 \n1226 517 106 528 \n1227 528 71 506 \n1228 506 517 528 \n1229 665 90 764 \n1230 764 161 646 \n1231 151 646 665 \n1232 665 764 646 \n1233 597 94 209 \n1234 209 59 539 \n1235 41 539 597 \n1236 597 209 539 \n1237 199 6 255 \n1238 255 158 657 \n1239 161 657 199 \n1240 199 255 657 \n1241 221 36 411 \n1242 411 77 550 \n1243 59 550 221 \n1244 221 411 550 \n1245 635 151 646 \n1246 646 161 657 \n1247 657 158 635 \n1248 635 646 657 \n1249 422 100 586 \n1250 586 41 561 \n1251 77 561 422 \n1252 422 586 561 \n1253 750 62 366 \n1254 366 21 668 \n1255 3 668 750 \n1256 750 366 668 \n1257 539 59 550 \n1258 550 77 561 \n1259 561 41 539 \n1260 539 550 561 \n1261 377 92 564 \n1262 564 39 679 \n1263 21 679 377 \n1264 377 564 679 \n1265 380 76 386 \n1266 386 118 572 \n1267 177 572 380 \n1268 380 386 572 \n1269 575 84 739 \n1270 739 3 690 \n1271 39 690 575 \n1272 575 739 690 \n1273 397 68 509 \n1274 33 509 583 \n1275 118 583 397 \n1276 397 509 583 \n1277 668 21 679 \n1278 679 39 690 \n1279 690 3 668 \n1280 668 679 690 \n1281 498 33 594 \n1282 594 177 369 \n1283 26 369 498 \n1284 498 594 369 \n1285 577 90 281 \n1286 281 169 701 \n1287 143 701 577 \n1288 577 281 701 \n1289 572 118 583 \n1290 583 33 594 \n1291 594 177 572 \n1292 572 583 594 \n1293 292 44 336 \n1294 173 336 712 \n1295 169 712 292 \n1296 292 336 712 \n1297 379 6 510 \n1298 85 510 605 \n1299 125 605 379 \n1300 379 510 605 \n1301 325 173 723 \n1302 723 143 566 \n1303 24 566 325 \n1304 325 723 566 \n1305 499 85 616 \n1306 616 95 620 \n1307 620 54 499 \n1308 499 616 620 \n1309 701 169 712 \n1310 712 173 723 \n1311 723 143 701 \n1312 701 712 723 \n1313 609 95 627 \n1314 627 125 368 \n1315 88 368 609 \n1316 609 627 368 \n1317 357 4 452 \n1318 452 124 734 \n1319 123 734 357 \n1320 357 452 734 \n1321 605 85 616 \n1322 616 95 627 \n1323 627 125 605 \n1324 605 616 627 \n1325 463 62 197 \n1326 57 197 745 \n1327 124 745 463 \n1328 463 197 745 \n1329 314 86 501 \n1330 501 189 638 \n1331 171 638 314 \n1332 314 501 638 \n1333 762 57 756 \n1334 756 123 346 \n1335 72 346 762 \n1336 762 756 346 \n1337 512 76 386 \n1338 386 118 649 \n1339 189 649 512 \n1340 512 386 649 \n1341 734 124 745 \n1342 745 57 756 \n1343 756 123 734 \n1344 734 745 756 \n1345 397 68 303 \n1346 303 171 660 \n1347 118 660 397 \n1348 397 303 660 \n1349 467 48 654 \n1350 654 151 767 \n1351 133 767 467 \n1352 467 654 767 \n1353 638 189 649 \n1354 649 118 660 \n1355 660 171 638 \n1356 638 649 660 \n1357 665 90 281 \n1358 281 169 202 \n1359 151 202 665 \n1360 665 281 202 \n1361 312 54 234 \n1362 234 113 671 \n1363 67 671 312 \n1364 312 234 671 \n1365 292 44 456 \n1366 456 133 214 \n1367 169 214 292 \n1368 292 456 214 \n1369 246 64 475 \n1370 475 178 682 \n1371 113 682 246 \n1372 246 475 682 \n1373 767 151 202 \n1374 202 169 214 \n1375 214 133 767 \n1376 767 202 214 \n1377 486 2 301 \n1378 301 67 693 \n1379 178 693 486 \n1380 486 301 693 \n1381 661 8 508 \n1382 180 508 226 \n1383 142 226 661 \n1384 661 508 226 \n1385 671 113 682 \n1386 682 178 693 \n1387 693 67 671 \n1388 671 682 693 \n1389 497 180 238 \n1390 238 75 400 \n1391 400 80 497 \n1392 497 238 400 \n1393 288 64 434 \n1394 434 131 704 \n1395 160 704 288 \n1396 288 434 704 \n1397 389 75 250 \n1398 250 142 650 \n1399 62 650 389 \n1400 389 250 650 \n1401 445 88 752 \n1402 107 752 715 \n1403 131 715 445 \n1404 445 752 715 \n1405 226 180 238 \n1406 238 75 250 \n1407 250 142 226 \n1408 226 238 250 \n1409 741 107 726 \n1410 726 160 277 \n1411 18 277 741 \n1412 741 726 277 \n1413 709 16 683 \n1414 144 683 262 \n1415 155 262 709 \n1416 709 683 262 \n1417 704 131 715 \n1418 715 107 726 \n1419 726 160 704 \n1420 704 715 726 \n1421 672 144 273 \n1422 273 187 490 \n1423 490 78 672 \n1424 672 273 490 \n1425 620 54 234 \n1426 234 113 737 \n1427 95 737 620 \n1428 620 234 737 \n1429 479 187 284 \n1430 284 155 698 \n1431 42 698 479 \n1432 479 284 698 \n1433 246 64 434 \n1434 434 131 748 \n1435 113 748 246 \n1436 246 434 748 \n1437 262 144 273 \n1438 273 187 284 \n1439 284 155 262 \n1440 262 273 284 \n1441 445 88 609 \n1442 609 95 759 \n1443 131 759 445 \n1444 445 609 759 \n1445 310 80 587 \n1446 587 93 295 \n1447 162 295 310 \n1448 310 587 295 \n1449 737 113 748 \n1450 748 131 759 \n1451 759 95 737 \n1452 737 748 759 \n1453 598 72 730 \n1454 105 730 306 \n1455 93 306 598 \n1456 598 730 306 \n1457 355 8 697 \n1458 697 103 770 \n1459 19 770 355 \n1460 355 697 770 \n1461 719 105 317 \n1462 317 162 299 \n1463 18 299 719 \n1464 719 317 299 \n1465 708 56 643 \n1466 149 643 205 \n1467 103 205 708 \n1468 708 643 205 \n1469 295 93 306 \n1470 306 105 317 \n1471 317 162 295 \n1472 295 306 317 \n1473 632 149 217 \n1474 217 19 344 \n1475 98 344 632 \n1476 632 217 344 \n1477 485 78 364 \n1478 364 116 328 \n1479 126 328 485 \n1480 485 364 328 \n1481 770 103 205 \n1482 205 149 217 \n1483 217 19 770 \n1484 770 205 217 \n1485 375 50 278 \n1486 278 13 339 \n1487 116 339 375 \n1488 375 278 339 \n1489 335 16 267 \n1490 11 267 229 \n1491 121 229 335 \n1492 335 267 229 \n1493 289 14 474 \n1494 474 126 350 \n1495 13 350 289 \n1496 289 474 350 \n1497 256 11 241 \n1498 241 167 270 \n1499 270 34 256 \n1500 256 241 270 \n1501 328 116 339 \n1502 339 13 350 \n1503 350 126 328 \n1504 328 339 350 \n1505 259 167 253 \n1506 253 121 324 \n1507 56 324 259 \n1508 259 253 324 \n1509 197 62 389 \n1510 389 75 361 \n1511 57 361 197 \n1512 197 389 361 \n1513 229 11 241 \n1514 241 167 253 \n1515 253 121 229 \n1516 229 241 253 \n1517 400 80 587 \n1518 587 93 372 \n1519 75 372 400 \n1520 400 587 372 \n1521 598 72 762 \n1522 762 57 383 \n1523 93 383 598 \n1524 598 762 383 \n1525 361 75 372 \n1526 372 93 383 \n1527 383 57 361 \n1528 361 372 383 \n1529 662 50 584 \n1530 584 136 394 \n1531 192 394 662 \n1532 662 584 394 \n1533 595 42 511 \n1534 137 511 405 \n1535 136 405 595 \n1536 595 511 405 \n"
  },
  {
    "path": "test/mesh/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/mesh/square_missingcoord.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/mesh/square_spurious_vertex_ref.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 10\n"
  },
  {
    "path": "test/mesh/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nfaces\n\n1 1 2 3\n2 2 3 4\n3 3 4 1\n4 2 1 4\n"
  },
  {
    "path": "test/mesh/tetrahedron2.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/mesh/vertexposition.morpho",
    "content": "\nvar a = Mesh(\"tetrahedron.mesh\")\n\nprint a\n// expect: <Mesh: 4 vertices>\n\nprint a.vertexmatrix()\n// expect: [ 0 -0.288675 -0.288675 0.57735 ]\n// expect: [ 0 -0.5 0.5 0 ]\n// expect: [ 0.612372 -0.204124 -0.204124 -0.204124 ]\n\nprint a.vertexposition(1)\n// expect: [ -0.288675 ]\n// expect: [ -0.5 ]\n// expect: [ -0.204124 ]\n\na.setvertexposition(1, a.vertexposition(1)+Matrix([0.1,0.1,0.1]))\n\nprint a.vertexmatrix()\n// expect: [ 0 -0.188675 -0.288675 0.57735 ]\n// expect: [ 0 -0.4 0.5 0 ]\n// expect: [ 0.612372 -0.104124 -0.204124 -0.204124 ]\n\nprint a.vertexposition(5)\n// expect Error 'MshInvldId'\n"
  },
  {
    "path": "test/method/arity.morpho",
    "content": "class Foo {\n  method0() { return \"no args\" }\n  method1(a) { return a }\n  method2(a, b) { return a + b }\n  method3(a, b, c) { return a + b + c }\n  method4(a, b, c, d) { return a + b + c + d }\n  method5(a, b, c, d, e) { return a + b + c + d + e }\n  method6(a, b, c, d, e, f) { return a + b + c + d + e + f }\n  method7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g }\n  method8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h }\n}\n\nvar foo = Foo()\nprint foo.method0() // expect: no args\nprint foo.method1(1) // expect: 1\nprint foo.method2(1, 2) // expect: 3\nprint foo.method3(1, 2, 3) // expect: 6\nprint foo.method4(1, 2, 3, 4) // expect: 10\nprint foo.method5(1, 2, 3, 4, 5) // expect: 15\nprint foo.method6(1, 2, 3, 4, 5, 6) // expect: 21\nprint foo.method7(1, 2, 3, 4, 5, 6, 7) // expect: 28\nprint foo.method8(1, 2, 3, 4, 5, 6, 7, 8) // expect: 36\n"
  },
  {
    "path": "test/method/empty_block.morpho",
    "content": "// Methods that don't return anything return self. \nclass Foo {\n  bar() {}\n}\n\nprint Foo().bar() // expect: <Foo>\n"
  },
  {
    "path": "test/method/extra_arguments.morpho",
    "content": "class Foo {\n  method(a, b) {\n    print a\n    print b\n  }\n}\n\nFoo().method(1, 2, 3, 4) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/method/missing_arguments.morpho",
    "content": "class Foo {\n  method(a, b) {}\n}\n\nFoo().method(1) // expect error 'InvldArgs'\n"
  },
  {
    "path": "test/method/not_found.morpho",
    "content": "class Foo {}\n\nFoo().unknown() // expect error: 'ObjLcksPrp'\n"
  },
  {
    "path": "test/method/optional.morpho",
    "content": "// Optional arguments\n\nclass Foo {\n  func(x, y=true, z=2) {\n    print x\n    print y\n    print z\n  }\n}\n\nvar f = Foo()\n\nf.func(1)\n// expect: 1\n// expect: true\n// expect: 2\n\nf.func(1, y=false)\n// expect: 1\n// expect: false\n// expect: 2\n\nf.func(1, z=3)\n// expect: 1\n// expect: true\n// expect: 3\n\nf.func(1, z=3, y=false)\n// expect: 1\n// expect: false\n// expect: 3\n\nf.func(1, 2, y=false)\n// expect error 'InvldArgs'\n"
  },
  {
    "path": "test/method/optional_indirect.morpho",
    "content": "// Optional arguments\n\nclass Foo {\n  func(x, y=true, z=2) {\n    print x\n    print y\n    print z\n  }\n}\n\nvar f = Foo()\n\nvar g = f.func\n\ng(1)\n// expect: 1\n// expect: true\n// expect: 2\n\ng(1, y=false)\n// expect: 1\n// expect: false\n// expect: 2\n\ng(1, z=3)\n// expect: 1\n// expect: true\n// expect: 3\n\ng(1, z=3, y=false)\n// expect: 1\n// expect: false\n// expect: 3\n\ng(1, 2, y=false)\n// expect error 'InvldArgs'\n"
  },
  {
    "path": "test/method/print_bound_method.morpho",
    "content": "class Foo {\n  method() { }\n}\nvar foo = Foo()\nprint foo.method // expect: <Foo>.<fn method>\n"
  },
  {
    "path": "test/method/return_in_method.morpho",
    "content": "// Methods that don't return anything return self.\nclass Foo {\n  bar() {\n    return\n  }\n}\n\nprint Foo().bar() // expect: <Foo>\n"
  },
  {
    "path": "test/method/too_many_arguments.morpho",
    "content": "{\n  var a = 1\n  true.method(\n     a, // 1\n     a, // 2\n     a, // 3\n     a, // 4\n     a, // 5\n     a, // 6\n     a, // 7\n     a, // 8\n     a, // 9\n     a, // 10\n     a, // 11\n     a, // 12\n     a, // 13\n     a, // 14\n     a, // 15\n     a, // 16\n     a, // 17\n     a, // 18\n     a, // 19\n     a, // 20\n     a, // 21\n     a, // 22\n     a, // 23\n     a, // 24\n     a, // 25\n     a, // 26\n     a, // 27\n     a, // 28\n     a, // 29\n     a, // 30\n     a, // 31\n     a, // 32\n     a, // 33\n     a, // 34\n     a, // 35\n     a, // 36\n     a, // 37\n     a, // 38\n     a, // 39\n     a, // 40\n     a, // 41\n     a, // 42\n     a, // 43\n     a, // 44\n     a, // 45\n     a, // 46\n     a, // 47\n     a, // 48\n     a, // 49\n     a, // 50\n     a, // 51\n     a, // 52\n     a, // 53\n     a, // 54\n     a, // 55\n     a, // 56\n     a, // 57\n     a, // 58\n     a, // 59\n     a, // 60\n     a, // 61\n     a, // 62\n     a, // 63\n     a, // 64\n     a, // 65\n     a, // 66\n     a, // 67\n     a, // 68\n     a, // 69\n     a, // 70\n     a, // 71\n     a, // 72\n     a, // 73\n     a, // 74\n     a, // 75\n     a, // 76\n     a, // 77\n     a, // 78\n     a, // 79\n     a, // 80\n     a, // 81\n     a, // 82\n     a, // 83\n     a, // 84\n     a, // 85\n     a, // 86\n     a, // 87\n     a, // 88\n     a, // 89\n     a, // 90\n     a, // 91\n     a, // 92\n     a, // 93\n     a, // 94\n     a, // 95\n     a, // 96\n     a, // 97\n     a, // 98\n     a, // 99\n     a, // 100\n     a, // 101\n     a, // 102\n     a, // 103\n     a, // 104\n     a, // 105\n     a, // 106\n     a, // 107\n     a, // 108\n     a, // 109\n     a, // 110\n     a, // 111\n     a, // 112\n     a, // 113\n     a, // 114\n     a, // 115\n     a, // 116\n     a, // 117\n     a, // 118\n     a, // 119\n     a, // 120\n     a, // 121\n     a, // 122\n     a, // 123\n     a, // 124\n     a, // 125\n     a, // 126\n     a, // 127\n     a, // 128\n     a, // 129\n     a, // 130\n     a, // 131\n     a, // 132\n     a, // 133\n     a, // 134\n     a, // 135\n     a, // 136\n     a, // 137\n     a, // 138\n     a, // 139\n     a, // 140\n     a, // 141\n     a, // 142\n     a, // 143\n     a, // 144\n     a, // 145\n     a, // 146\n     a, // 147\n     a, // 148\n     a, // 149\n     a, // 150\n     a, // 151\n     a, // 152\n     a, // 153\n     a, // 154\n     a, // 155\n     a, // 156\n     a, // 157\n     a, // 158\n     a, // 159\n     a, // 160\n     a, // 161\n     a, // 162\n     a, // 163\n     a, // 164\n     a, // 165\n     a, // 166\n     a, // 167\n     a, // 168\n     a, // 169\n     a, // 170\n     a, // 171\n     a, // 172\n     a, // 173\n     a, // 174\n     a, // 175\n     a, // 176\n     a, // 177\n     a, // 178\n     a, // 179\n     a, // 180\n     a, // 181\n     a, // 182\n     a, // 183\n     a, // 184\n     a, // 185\n     a, // 186\n     a, // 187\n     a, // 188\n     a, // 189\n     a, // 190\n     a, // 191\n     a, // 192\n     a, // 193\n     a, // 194\n     a, // 195\n     a, // 196\n     a, // 197\n     a, // 198\n     a, // 199\n     a, // 200\n     a, // 201\n     a, // 202\n     a, // 203\n     a, // 204\n     a, // 205\n     a, // 206\n     a, // 207\n     a, // 208\n     a, // 209\n     a, // 210\n     a, // 211\n     a, // 212\n     a, // 213\n     a, // 214\n     a, // 215\n     a, // 216\n     a, // 217\n     a, // 218\n     a, // 219\n     a, // 220\n     a, // 221\n     a, // 222\n     a, // 223\n     a, // 224\n     a, // 225\n     a, // 226\n     a, // 227\n     a, // 228\n     a, // 229\n     a, // 230\n     a, // 231\n     a, // 232\n     a, // 233\n     a, // 234\n     a, // 235\n     a, // 236\n     a, // 237\n     a, // 238\n     a, // 239\n     a, // 240\n     a, // 241\n     a, // 242\n     a, // 243\n     a, // 244\n     a, // 245\n     a, // 246\n     a, // 247\n     a, // 248\n     a, // 249\n     a, // 250\n     a, // 251\n     a, // 252\n     a, // 253\n     a, // 254\n     a, // 255\n     a) // expect error: 'TooMnyArg'\n}\n"
  },
  {
    "path": "test/method/too_many_parameters.morpho",
    "content": "class Foo {\n  // 256 parameters.\n  method(\n    a1,\n    a2,\n    a3,\n    a4,\n    a5,\n    a6,\n    a7,\n    a8,\n    a9,\n    a10,\n    a11,\n    a12,\n    a13,\n    a14,\n    a15,\n    a16,\n    a17,\n    a18,\n    a19,\n    a20,\n    a21,\n    a22,\n    a23,\n    a24,\n    a25,\n    a26,\n    a27,\n    a28,\n    a29,\n    a30,\n    a31,\n    a32,\n    a33,\n    a34,\n    a35,\n    a36,\n    a37,\n    a38,\n    a39,\n    a40,\n    a41,\n    a42,\n    a43,\n    a44,\n    a45,\n    a46,\n    a47,\n    a48,\n    a49,\n    a50,\n    a51,\n    a52,\n    a53,\n    a54,\n    a55,\n    a56,\n    a57,\n    a58,\n    a59,\n    a60,\n    a61,\n    a62,\n    a63,\n    a64,\n    a65,\n    a66,\n    a67,\n    a68,\n    a69,\n    a70,\n    a71,\n    a72,\n    a73,\n    a74,\n    a75,\n    a76,\n    a77,\n    a78,\n    a79,\n    a80,\n    a81,\n    a82,\n    a83,\n    a84,\n    a85,\n    a86,\n    a87,\n    a88,\n    a89,\n    a90,\n    a91,\n    a92,\n    a93,\n    a94,\n    a95,\n    a96,\n    a97,\n    a98,\n    a99,\n    a100,\n    a101,\n    a102,\n    a103,\n    a104,\n    a105,\n    a106,\n    a107,\n    a108,\n    a109,\n    a110,\n    a111,\n    a112,\n    a113,\n    a114,\n    a115,\n    a116,\n    a117,\n    a118,\n    a119,\n    a120,\n    a121,\n    a122,\n    a123,\n    a124,\n    a125,\n    a126,\n    a127,\n    a128,\n    a129,\n    a130,\n    a131,\n    a132,\n    a133,\n    a134,\n    a135,\n    a136,\n    a137,\n    a138,\n    a139,\n    a140,\n    a141,\n    a142,\n    a143,\n    a144,\n    a145,\n    a146,\n    a147,\n    a148,\n    a149,\n    a150,\n    a151,\n    a152,\n    a153,\n    a154,\n    a155,\n    a156,\n    a157,\n    a158,\n    a159,\n    a160,\n    a161,\n    a162,\n    a163,\n    a164,\n    a165,\n    a166,\n    a167,\n    a168,\n    a169,\n    a170,\n    a171,\n    a172,\n    a173,\n    a174,\n    a175,\n    a176,\n    a177,\n    a178,\n    a179,\n    a180,\n    a181,\n    a182,\n    a183,\n    a184,\n    a185,\n    a186,\n    a187,\n    a188,\n    a189,\n    a190,\n    a191,\n    a192,\n    a193,\n    a194,\n    a195,\n    a196,\n    a197,\n    a198,\n    a199,\n    a200,\n    a201,\n    a202,\n    a203,\n    a204,\n    a205,\n    a206,\n    a207,\n    a208,\n    a209,\n    a210,\n    a211,\n    a212,\n    a213,\n    a214,\n    a215,\n    a216,\n    a217,\n    a218,\n    a219,\n    a220,\n    a221,\n    a222,\n    a223,\n    a224,\n    a225,\n    a226,\n    a227,\n    a228,\n    a229,\n    a230,\n    a231,\n    a232,\n    a233,\n    a234,\n    a235,\n    a236,\n    a237,\n    a238,\n    a239,\n    a240,\n    a241,\n    a242,\n    a243,\n    a244,\n    a245,\n    a246,\n    a247,\n    a248,\n    a249,\n    a250,\n    a251,\n    a252,\n    a253,\n    a254,\n    a255, a) {} // expect error: 'TooMnyPrm'\n}\n"
  },
  {
    "path": "test/modules/constants.morpho",
    "content": "// Test constants package\n\nimport constants\n\nprint Pi\n// expect: 3.14159\nprint E\n// expect: 2.71828\n"
  },
  {
    "path": "test/modules/delaunay/delaunay.morpho",
    "content": "// Demonstrate use of the Delaunay module\n\nimport delaunay\n\nvar N = 100 // Number of points \n\n// Explicitly check that all triangles have the property that no other point is in their circumcircle\nfn checkTriangulation(pts, tri, quiet=false) {\n    var success = true\n    for (t, k in tri) {\n        var sph = Circumsphere(pts, t)\n\n        for (i in 0...pts.count()) {\n            if (t.ismember(i)) continue // Skip the vertices of the triangle\n\n            if (sph.pointinsphere(pts[i])) {\n                if (!quiet) print \"Point ${i} is in triangle ${k}.\"\n                success=false; \n            }\n        }\n    }\n    return success\n}\n\n// 2D random point distribution \nvar a = []\nfor (i in 1..N) a.append(Matrix([2*random()-1, 2*(2*random()-1)]))\n\nvar del = Delaunay(a)\nvar tri = del.triangulate() // Returns a list of triangles [ [i, j, k], ... ]\n\nprint checkTriangulation(a, tri)\n// expect: true\n\n// 3D random point distribution \nvar b = []\nfor (i in 1..N) b.append(Matrix([2*random()-1, 2*(2*random()-1), 2*random()-1]))\n\ndel = Delaunay(b)\ntri = del.triangulate()\n\nprint checkTriangulation(b, tri)\n// expect: true\n"
  },
  {
    "path": "test/modules/meshgen/disk.morpho",
    "content": "// Domain composed of a single disk\nimport meshgen\nimport plot\n\nvar dom = fn (x) -(x[0]^2+x[1]^2-1)\nvar mg = MeshGen(dom, [-1..1:0.2, -1..1:0.2], quiet=true)\nvar m = mg.build()\n\nprint ismesh(m)\n// expect: true\n"
  },
  {
    "path": "test/modules/meshslice/slice.morpho",
    "content": "// Test slice \n\nimport meshslice\nimport plot\n\nvar m = Mesh(\"tetrahedron.mesh\")\nm.addgrade(1)\n\n//Show(plotmesh(m, grade=[0,1,2]))\n\nvar slice = MeshSlicer(m) \nvar m2 = slice.slice([0.1,0,0],[1,0,0]) \n\nprint ismesh(m2) // expect: true"
  },
  {
    "path": "test/modules/meshslice/slice_delaunay.morpho",
    "content": "import delaunay\nimport meshtools \nimport plot \nimport meshslice\n\nvar pts = [ Matrix([0,0,0]),\n            Matrix([1,0,0]),\n            Matrix([0,1,0]),\n            Matrix([0,0,1]) ]\nvar m = DelaunayMesh(pts)\n\nfor (g in 0..m.maxgrade()) print m.count(g)\n// expect: 4\n// expect: 0\n// expect: 0\n// expect: 1\n\nvar slice = MeshSlicer(m)\nvar sc = slice.slice(Matrix([0.5,0,0]), Matrix([1,0,0]))\n\nfor (g in 0..sc.maxgrade()) print sc.count(g)\n// expect: 3\n// expect: 0\n// expect: 1\n\n//Show(plotmesh(sc, grade=[0,1,2]) + plotmesh(m, grade=[0,1], color=Red))\n\n"
  },
  {
    "path": "test/modules/meshslice/slice_empty.morpho",
    "content": "// Test slice \n\nimport meshslice\nimport plot\n\nvar m = Mesh(\"tetrahedron.mesh\")\nvar f = Field(m)\n\nvar slice = MeshSlicer(m) \nvar f2 = slice.slicefield(f) // expect error 'SlcEmpty'\nvar m2 = slice.slice([0.1,0,0],[1,0,0]) \n"
  },
  {
    "path": "test/modules/meshslice/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nfaces\n\n1 1 2 3\n2 2 3 4\n3 3 4 1\n4 2 1 4\n\nvolumes \n\n1 1 2 3 4 "
  },
  {
    "path": "test/modules/meshtools/dimension_inconsistent.morpho",
    "content": "// Trigger an inconsistent dimension err \nimport meshtools\n\nvar mb = MeshBuilder() \n\nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0,1])\n\n// expect error 'MshBldDimIncnstnt'"
  },
  {
    "path": "test/modules/meshtools/dimension_unknown.morpho",
    "content": "// Trigger an unknown dimension err \nimport meshtools\n\nvar mb = MeshBuilder() \n\nmb.addelement(1, [0,0])\n\n// expect error 'MshBldDimUnknwn'"
  },
  {
    "path": "test/modules/meshtools/merge_duplicates.morpho",
    "content": "// MeshMerge with duplicate elemtns \nimport meshtools\n\nvar m1 = MeshBuilder()\nvar m2 = MeshBuilder()\n\nm1.addvertex([0,0,0])\nm1.addvertex([0, 1, 0])\nm1.addvertex([0,1,-1])\nm1.addedge([0,1])\nm1.addedge([1,2])\nm1.addedge([2,0])\nm1.addface([0,1,2])\n\nm2.addvertex([0,0,0])\nm2.addvertex([0, 1, 0])\nm2.addvertex([0,1,1])\nm2.addedge([0,1])\nm2.addedge([1,2])\nm2.addedge([2,0])\nm2.addface([0,1,2])\n\nvar mesh1 = m1.build()\nvar mesh2 = m2.build()\nvar merge = MeshMerge([mesh1, mesh2])\nvar final = merge.merge()\n\nprint final.count(0) // expect: 4\nprint final.count(1) // expect: 5\nprint final.count(2) // expect: 2\n\nmerge = MeshMerge([mesh1, mesh1])\nfinal = merge.merge()\n\nprint final.count(0) // expect: 3\nprint final.count(1) // expect: 3\nprint final.count(2) // expect: 1\n"
  },
  {
    "path": "test/modules/meshtools/mesh_still_too_large.morpho",
    "content": "// Individual stepsize is ok, but the product is too large\n\nimport meshtools\n\nvar L = 0.5\nvar dx = 0.0000001\n\nvar m = AreaMesh(fn (u, v) [u, v, 0], -L..L:dx, -L..L:dx) // expect error 'MltMaxVrt'\n"
  },
  {
    "path": "test/modules/meshtools/mesh_too_large.morpho",
    "content": "// Generate too large a mesh \n\nimport meshtools\n\nvar L = 0.5\nvar dx = 0.00000000001\n\nvar m = AreaMesh(fn (u, v) [u, v, 0], -L..L:dx, -L..L:dx) // expect error 'RngStpSz'\n"
  },
  {
    "path": "test/modules/meshtools/meshrefine.morpho",
    "content": "import meshtools\n\nvar m = Mesh(\"tetrahedron.mesh\")\nprint m\n// expect: <Mesh: 4 vertices>\n\nvar m2 = refinemesh(m)\nprint m2\n// expect: <Mesh: 10 vertices>\n\nprint m2.connectivitymatrix(0,1)\n// expect: [ 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 ]\n// expect: [ 0 1 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 ]\n// expect: [ 0 0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 1 1 ]\n// expect: [ 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 1 0 0 0 ]\n// expect: [ 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 1 ]\n\nprint m2.connectivitymatrix(0,2)\n// expect: [ 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 ]\n// expect: [ 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 ]\n// expect: [ 1 0 0 1 1 0 1 0 0 0 0 0 0 1 0 1 ]\n// expect: [ 1 0 1 0 0 1 1 0 0 0 1 0 1 0 0 0 ]\n// expect: [ 0 1 0 0 1 1 1 1 0 1 0 0 0 0 0 0 ]\n// expect: [ 0 1 0 0 0 0 0 0 1 1 0 0 0 1 1 1 ]\n// expect: [ 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 ]\n// expect: [ 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 1 ]\n"
  },
  {
    "path": "test/modules/meshtools/polyhedron.morpho",
    "content": "// Create a mesh from a polyhedron\n\nimport meshtools\n\nvar icos = [[-0.688191, 0, 0.131433], [0.688191,\n  0, -0.131433], [-0.212663, -0.654508, 0.131433], [-0.212663,\n  0.654508, 0.131433], [0.556758, -0.404508, 0.131433], [0.556758,\n  0.404508, 0.131433], [-0.131433, -0.404508, 0.556758], [-0.131433,\n  0.404508, 0.556758], [-0.344095, -0.25, -0.556758], [-0.344095,\n  0.25, -0.556758], [0.344095, -0.25, 0.556758], [0.344095, 0.25,\n  0.556758], [0.425325,\n  0, -0.556758], [-0.556758, -0.404508, -0.131433], [-0.556758,\n  0.404508, -0.131433], [-0.425325, 0.0,\n  0.556758], [0.131433, -0.404508, -0.556758], [0.131433,\n  0.404508, -0.556758], [0.212663, -0.654508, -0.131433], [0.212663,\n  0.654508, -0.131433]]\n\nvar faces = [[14, 9, 8, 13, 0], [1, 5, 11, 10, 4], [4, 10, 6, 2, 18], [10, 11, 7,\n             15, 6], [11, 5, 19, 3, 7], [5, 1, 12, 17, 19], [1, 4, 18, 16,\n             12], [3, 19, 17, 9, 14], [17, 12, 16, 8, 9], [16, 18, 2, 13, 8], [2,\n             6, 15, 0, 13], [15, 7, 3, 14, 0]]\n\nvar m=PolyhedronMesh(icos, faces)\n\nprint m\n// expect: <Mesh: 32 vertices>\n\n// More could be done here!\n"
  },
  {
    "path": "test/modules/meshtools/testprune.morpho",
    "content": "// Test pruning \n\nimport meshtools\n\nvar mx = AreaMesh(fn (u,v) [u,v,0], -0.5..0.5:0.1, -0.5..0.5:0.1) \nmx.addgrade(1)\n\nvar f = Field(mx, fn (x,y,z) x^2 + y^2)\nvar bnd = Selection(mx, boundary=true)\n\nvar sx = Selection(mx)\nsx[2,25]=true\nsx[1,50]=true\nsx[1,51]=true\nsx[1,53]=true \nsx[1,55]=true\nsx[2,50]=true\nsx[1,140]=true\nsx[2,100]=true\nsx[2,128]=true\n\nvar mpx = MeshPruner([mx, f, sx], fix=bnd)\nvar dictx = mpx.prune(sx) \nvar m2x = dictx[mx]\nvar fx = dictx[f]\nvar sxx = dictx[sx]\n\nprint mx.count() \n// expect: 121\nprint m2x.count() \n// expect: 109\n"
  },
  {
    "path": "test/modules/meshtools/testrefine3d.morpho",
    "content": "// Test refinement of 3D meshes to make sure duplicate elements aren't generated. \nimport meshtools\n\nvar mb = MeshBuilder()\nmb.addvertex([0, 0, 0.612372])\nmb.addvertex([-0.288675, -0.5, -0.204124]) \nmb.addvertex([-0.288675, 0.5, -0.204124]) \nmb.addvertex([0.57735, 0, -0.204124]) \nmb.addvolume([0,1,2,3])\nvar m = mb.build() \n\nm.addgrade(1)\nm.addgrade(2)\nm.addgrade(3)\n\nprint m.count(0) // expect: 4\nprint m.count(1) // expect: 6\nprint m.count(2) // expect: 4\nprint m.count(3) // expect: 1\n\nvar s = Selection(m, boundary=true)\ns.addgrade(1)\n\nprint \"S ${s.count(0)} ${s.count(1)} ${s.count(2)} ${s.count(3)}\" // expect: S 4 6 4 0\n\nvar mr = MeshRefiner([m,s])\nvar refmap = mr.refine()\n\nm = refmap[m]\ns = refmap[s]\n\nprint m.count(0) // expect: 10\nprint m.count(1) // expect: 25\nprint m.count(2) // expect: 24\nprint m.count(3) // expect: 8\n\nprint \"S+ ${s.count(0)} ${s.count(1)} ${s.count(2)} ${s.count(3)}\" // expect: S+ 10 12 16 0\n\nvar sref = Selection(m, boundary=true)\nsref.addgrade(1)\n\nprint \"Sr ${sref.count(0)} ${sref.count(1)} ${sref.count(2)} ${sref.count(3)}\" // expect: Sr 10 24 16 0\n"
  },
  {
    "path": "test/modules/meshtools/testrefineselection.morpho",
    "content": "// Test refinement of selections with down projection \nimport meshtools\nimport plot \n\nvar mx = AreaMesh(fn (u,v) [u,v,0], -0.5..0.5:0.5, -0.5..0.5:0.5) \nmx.addgrade(1)\n\nvar sel = Selection(mx, boundary=true)\nvar selcorner = Selection(mx, fn (x,y,z) x<-0.4 && y<-0.4)\nvar selmost = sel.difference(selcorner)\n\nvar mpx = MeshRefiner([mx, sel, selmost])\nvar dictx = mpx.refine() \n\nmx=dictx[mx]\nsel=dictx[sel]\nselmost=dictx[selmost]\n\nvar sel2 = Selection(mx, boundary=true)\n\nfn compareselections(mesh, sel1, sel2) {\n    for (g in 0..mesh.maxgrade()) {\n        for (id in 0...mesh.count(g)) {\n            if (sel1[g,id]!=sel2[g,id]) return false \n        }\n    }\n    return true \n}\n\nprint compareselections(mx, sel, sel2)\n// expect: true\n\nprint selmost.idlistforgrade(0).count() +1 == sel.idlistforgrade(0).count() \n// expect: true\n\n//Show(plotselection(mx, sel, grade=[0,1,2]))"
  },
  {
    "path": "test/modules/meshtools/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nfaces\n\n1 1 2 3\n2 2 3 4\n3 3 4 1\n4 2 1 4\n"
  },
  {
    "path": "test/modules/meshtools/tetrahedron.morpho",
    "content": "import meshtools\n\nvar file = \"tetrahedron2.mesh\"\n\nvar mb = MeshBuilder() \n\nmb.addvertex([0,0,0])\nmb.addvertex([1,0,0])\nmb.addvertex([0,1,0])\nmb.addvertex([0,0,1])\n\nmb.addedge([0,1])\nmb.addedge([0,2])\nmb.addedge([0,3])\nmb.addedge([1,2])\nmb.addedge([1,3])\nmb.addedge([2,3])\n\nmb.addface([0,1,2])\nmb.addface([0,1,3])\nmb.addface([0,2,3])\nmb.addface([1,2,3])\n\nmb.addvolume([0,1,2,3])\n\nvar m = mb.build() \n\nprint m\n// expect: <Mesh: 4 vertices>\nm.save(file)\n\nvar m2 = Mesh(file)\nfor (g in 0..3) print m.count(g)==m2.count(g)\n// expect: true\n// expect: true\n// expect: true\n// expect: true\n"
  },
  {
    "path": "test/modules/meshtools/tetrahedron2.mesh",
    "content": "vertices\n\n1 0 0 0 \n2 1 0 0 \n3 0 1 0 \n4 0 0 1 \n\nedges\n\n1 1 2 \n2 1 3 \n3 1 4 \n4 2 3 \n5 2 4 \n6 3 4 \nfaces\n\n1 1 2 3 \n2 1 2 4 \n3 1 3 4 \n4 2 3 4 \nvolumes\n\n1 1 2 3 4 \n"
  },
  {
    "path": "test/modules/meshtools/triangle.mesh",
    "content": "vertices\n\n1 0 0 \n2 1 0 \n3 0 1 \n\nedges\n\n1 1 2 \n2 2 3 \n3 1 3 \nfaces\n\n1 1 2 3 \n"
  },
  {
    "path": "test/modules/meshtools/triangle.morpho",
    "content": "import meshtools\n\nvar file = \"triangle.mesh\"\n\nvar mb = MeshBuilder() \n\nmb.addvertex([0,0])\nmb.addvertex([1,0])\nmb.addvertex([0,1])\n\nmb.addedge([0,1])\nmb.addedge([1,2])\nmb.addedge([2,0])\n\nmb.addface([0,1,2])\n\nvar m = mb.build() \n\nprint m\n// expect: <Mesh: 3 vertices>\nm.save(file)\n\nvar m2 = Mesh(file)\nfor (g in 0..2) print m.count(g)==m2.count(g)\n// expect: true\n// expect: true\n// expect: true\n"
  },
  {
    "path": "test/modules/optimize/cg.morpho",
    "content": "// Test conjugate gradient on a function\n\nimport optimize\n\nvar L = 5\n\nfn f(x) {\n  return (x[0]/L)^2 + x[1]^2 + 1\n}\n\nfn df(x) {\n  return Matrix([2*x[0]/L^2, 2*x[1]])\n}\n\n// We create subclass of Optimizer to optimize the function\nclass FunctionOptimizer is Optimizer {\n  init (f, df, x0) {\n    self.f = f\n    self.grad = df\n    super.init(f, x0)\n  }\n\n  gettarget() {\n    return self.target\n  }\n\n  settarget(val) {\n    self.target=val\n  }\n\n  totalenergy() {\n    return self.f(self.target)\n  }\n\n  totalforce() {\n    return self.grad(self.target)\n  }\n\n  initlocalconstraints() {}\n  subtractlocalconstraints(g) {}\n  subtractconstraints(g) {}\n  reprojectlocalconstraints() {}\n  reprojectconstraints() {}\n}\n\nvar opt = FunctionOptimizer(f, df, Matrix([0.2,3]))\n\nopt.steplimit=2\nopt.quiet = true\n\nopt.conjugategradient(20)\n\nprint (opt.energy[-1]-1)<1e-8 // expect: true\n"
  },
  {
    "path": "test/modules/povray.morpho",
    "content": "// Ensure povray imports correctly\n\nimport povray\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/namespace/function.xmorpho",
    "content": "\nfn f(x) {\n    return x\n}"
  },
  {
    "path": "test/namespace/namespace_class.morpho",
    "content": "// Check that classes are imported correctly\n\nimport color as c\n\nprint c.Color\n// expect: @Color\n\nvar gray = c.Color(0.5,0.5,0.5)\n\nprint gray\n// expect: <Color>"
  },
  {
    "path": "test/namespace/namespace_class_extends.morpho",
    "content": "// Check that classes imported into a namespace can be referred to in a class statement\n\nimport color as c\n\nclass Beep is c.Color {\n\n}\n\nvar a = Beep(1,1,1)\nprint a\n// expect: <Beep>"
  },
  {
    "path": "test/namespace/namespace_class_restrict.morpho",
    "content": "// Check that classes that imported into a namespace are not visible in the global context\n\nimport color as c\n\nprint Color\n// expect error 'SymblUndf'\n\n"
  },
  {
    "path": "test/namespace/namespace_functioncall.morpho",
    "content": "import \"function.xmorpho\" as func\n\nprint func.f \n// expect: <fn f>\n\nprint func.f(1)\n// expect: 1"
  },
  {
    "path": "test/newline/block.morpho",
    "content": "// Check semicolons aren't needed at the end of blocks\n\nfn f(x) { return x*x }\nprint f(2)\n// expect: 4\n\nfor (var i=0; i<2; i+=1) { print i }\n// expect: 0\n// expect: 1\n"
  },
  {
    "path": "test/newline/classes.morpho",
    "content": "// Newlines as statement terminators\n\n/*\n * Classes\n */\n\nclass Donut {\n init(type) {\n   self.type = type\n }\n\n eat() {\n   print \"A delicious ${self.type} donut!\"\n }\n}\n\nvar cruller = Donut(\"Cruller\")\n\ncruller.eat()\n// expect: A delicious Cruller donut!\n\n// Ambiguity: Is this a property access?\nprint cruller\n.eat\n// expect: <Donut>.<fn eat>\n"
  },
  {
    "path": "test/newline/for.morpho",
    "content": "// Newlines as statement terminators\n\n/* *********\n * For loops\n * ********* */\n\n// Ambiguity: Is the statement following the for in the loop?\nfor (var i=0;i<2; i=i+1)\nprint \"done\"\n// expect: done\n\n// Put the body on the same line\nfor (var i=0;i<2; i=i+1) print \"easy!\"\n// expect: easy!\n// expect: easy!\n\n// Or use curly braces\nfor (var i=0;i<2; i=i+1) {\n  print \"trouble\"\n}\n// expect: trouble\n// expect: trouble\n"
  },
  {
    "path": "test/newline/functions.morpho",
    "content": "// Newlines as statement terminators\n\n/* *********\n * Functions\n * ********* */\n\n// Ambiguity: Line terminator after return\nfn test1(x) {\n  return x*x\n}\n\nfn test2(x) {\n  return\n  x*x\n}\n\nfn test3(x) {\n  return x*\n  test1(x)\n}\n\nprint test1(2) // expect: 4\n\nprint test2(2) // expect: nil\n\nprint test3(2) // expect: 8\n\n// Ambiguity: Is this two statements or a function call?\nprint test1\n(1)\n// expect: <fn test1>\n"
  },
  {
    "path": "test/newline/variables.morpho",
    "content": "// Newlines as statement terminators\n\n/* *************************\n * Variables and expressions\n * ************************* */\n\nvar a\n=\n1\n2 // Note this is parsed as a separate expression statement\n\nvar b\n=a\n+\n1 // End of statement\na // This is also a separate statement\n\nprint a\n+\n(1)\n// expect: 2\n\n// Variable declaration without initializer\nvar c\n\n// Ambiguity: If we have a negation operation on the following line\n// then it is c\nprint b-a\n// expect: 1\n\nprint b\n-a\n// expect: 1\n\nprint (b\n-a)\n// expect: 1\n\n// Ambiguity: Is this two statements or a function call?\na = b\n(a)\n\nprint a\n// expect: 2\n"
  },
  {
    "path": "test/nil/literal.morpho",
    "content": "// Test that the nil literal works\n\nprint nil // expect: nil\n\nif (!nil) {\n  print \"True\"\n}\n// expect: True\n\nif (nil) {\n  print \"True\"\n} else {\n  print \"False\"\n}\n// expect: False\n"
  },
  {
    "path": "test/number/decimal_point_at_eof.morpho",
    "content": "// expect: 123\nprint 123.\n"
  },
  {
    "path": "test/number/float_negative_overflow.morpho",
    "content": "// Test for values outside floating point range\n\nprint -1.21e55555\n// expect error 'ValRng'"
  },
  {
    "path": "test/number/float_overflow.morpho",
    "content": "// Test for values outside floating point range\n\nprint 1.21e55555\n// expect error 'ValRng'"
  },
  {
    "path": "test/number/integer_overflow.morpho",
    "content": "// Test for values outside integer range\n\nprint 1221323213412343143\n// expect error 'ValRng'"
  },
  {
    "path": "test/number/leading_dot.morpho",
    "content": "// expect error: 'ExpExpr'\n.123;\n"
  },
  {
    "path": "test/number/literals.morpho",
    "content": "print 123      // expect: 123\nprint 987654   // expect: 987654\nprint 0        // expect: 0\nprint 0.       // expect: 0\nprint -0.0     // expect: -0\n\nprint 123.456  // expect: 123.456\nprint -0.001   // expect: -0.001\n"
  },
  {
    "path": "test/number/nan_equality.morpho",
    "content": "var nan = 0/0\n\nprint nan == 0 // expect: false\nprint nan != 1 // expect: true\n\n// NaN is not equal to self.\nprint nan == nan // expect: false\nprint nan != nan // expect: true\n"
  },
  {
    "path": "test/number/trailing_dot.morpho",
    "content": "123.; // expect error: 'ExpExpr'\n"
  },
  {
    "path": "test/object/baseclass.morpho",
    "content": "// Object is the default parent class for all other classes\n\nclass Foo { }\n\nvar a = Foo()\n\na.prnt()\nprint \"\"\n// expect: <Foo>\n\nprint a.respondsto(\"prnt\")\n// expect: true\n\na.invoke(\"prnt\")\nprint \"\"\n// expect: <Foo>\n\nprint a.superclass()\n// expect: @Object\n"
  },
  {
    "path": "test/object/builtin_inheritance.morpho",
    "content": "// Check inheritance of builtin objects\n\nvar a = Length()\n\nprint a.clss()\n// expect: @Length\n\nprint a.respondsto(\"invoke\")\n// expect: true\n\nprint a.respondsto(\"squiggle\")\n// expect: false\n"
  },
  {
    "path": "test/object/class_responds_to.morpho",
    "content": "// Check respondsto called on a class\n\nvar a = Object\n\nprint a.respondsto(\"invoke\")\n// expect: true\n\nprint a.respondsto(\"squiggle\")\n// expect: false\n\nprint a.respondsto(\"squiggle\",\"foo\")\n// expect error 'RspndsToArg'\n"
  },
  {
    "path": "test/object/clone.morpho",
    "content": "// Cloning an object\n\nclass Foo {\n  init(x) {\n    self.boo = x\n  }\n}\n\nvar a = Foo(5)\n\nprint a.boo\n// expect: 5\n\nvar b = a.clone()\n\nprint b.boo\n// expect: 5\n\nb.boo = 10\n\nprint a.boo\n// expect: 5\n\nprint b.boo\n// expect: 10\n"
  },
  {
    "path": "test/object/enumerate.morpho",
    "content": "// Check Object's compliance with enumerate\n\nvar a = Object()\n\na.foo = 1\na.goo = 2\na.hoo = 3\n\nprint a.count()\n// expect: 3\n\nvar q = []\nfor (i in a) q.append(i)\n\nq.sort()\nprint q\n// expect: [ foo, goo, hoo ]\n\nclass Foo { }\n\nprint Foo.count()\n// expect: 0\n\nprint Foo.enumerate(-1)\n// expect: 0\n\nprint Foo.enumerate(0)\n// expect: nil\n"
  },
  {
    "path": "test/object/has.morpho",
    "content": "// Check Object.respondsto()\n\nvar a = Object()\n\na.things = \"stuff\"\n\nprint a.has(\"things\")\n// expect: true\n\nprint a.has(\"stuff\")\n// expect: false\n\nprint a.has()\n// expect: [ things ]\n"
  },
  {
    "path": "test/object/index.morpho",
    "content": "// Check get and set index\n\nvar a = Object()\n\na[\"Foo\"] = 4\n\nprint a[\"Foo\"]\n// expect: 4\n\nprint a.Foo\n// expect: 4\n"
  },
  {
    "path": "test/object/invoke.morpho",
    "content": "// Check Object.invoke()\n\nvar a = Object()\n\na.invoke(\"prnt\")\nprint \"\"\n// expect: <Object>\n\nprint a.invoke(\"superclass\")\n// expect: nil\n\nprint a.invoke(\"respondsto\", \"superclass\")\n// expect: true\n"
  },
  {
    "path": "test/object/non_string_index.morpho",
    "content": "// Object indices need to be property labels\n\nvar a = Object()\n\nprint a[1] // expect error 'IndxArgs'\n"
  },
  {
    "path": "test/object/responds_to.morpho",
    "content": "// Check Object.respondsto()\n\nvar a = Object()\n\nprint a.respondsto(\"invoke\")\n// expect: true\n\nprint a.respondsto(\"squiggle\")\n// expect: false\n\nvar r = a.respondsto()\nr.sort()\nprint r\n// expect: [ clone, clss, count, enumerate, has, index, invoke, linearization, prnt, respondsto, serialize, setindex, superclass ]\nclass bar {\n    method1(){\n        1+1\n    }\n}\n\nvar b = bar()\nr = b.respondsto()\nr.sort()\nprint r\n// expect: [ clone, clss, count, enumerate, has, index, invoke, linearization, method1, prnt, respondsto, serialize, setindex, superclass ]\n\n\nprint a.respondsto(\"squiggle\",\"foo\")\n// expect error 'RspndsToArg'\n"
  },
  {
    "path": "test/operator/add.morpho",
    "content": "print 123 + 456 // expect: 579\nprint \"str\" + \"ing\" // expect: string\n"
  },
  {
    "path": "test/operator/add_bool_nil.morpho",
    "content": "true + nil // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/add_bool_num.morpho",
    "content": "true + 123 // expect error: 'InvldOp'\n"
  },
  {
    "path": "test/operator/add_bool_string.morpho",
    "content": "true + \"s\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/add_nil_nil.morpho",
    "content": "nil + nil // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/add_num_nil.morpho",
    "content": "1 + nil // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/add_string_nil.morpho",
    "content": "\"s\" + nil // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/comparison.morpho",
    "content": "print 1 < 2    // expect: true\nprint 2 < 2    // expect: false\nprint 2 < 1    // expect: false\n\nprint 1 <= 2    // expect: true\nprint 2 <= 2    // expect: true\nprint 2 <= 1    // expect: false\n\nprint 1 > 2    // expect: false\nprint 2 > 2    // expect: false\nprint 2 > 1    // expect: true\n\nprint 1 >= 2    // expect: false\nprint 2 >= 2    // expect: true\nprint 2 >= 1    // expect: true\n\n// Zero and negative zero compare the same.\nprint 0 < -0 // expect: false\nprint -0 < 0 // expect: false\nprint 0 > -0 // expect: false\nprint -0 > 0 // expect: false\nprint 0 <= -0 // expect: true\nprint -0 <= 0 // expect: true\nprint 0 >= -0 // expect: true\nprint -0 >= 0 // expect: true\n"
  },
  {
    "path": "test/operator/divide.morpho",
    "content": "print 8 / 2         // expect: 4\nprint 12.34 / 12.34  // expect: 1\n"
  },
  {
    "path": "test/operator/divide_nonnum_num.morpho",
    "content": "\"1\" / 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/divide_num_nonnum.morpho",
    "content": "1 / \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/equals.morpho",
    "content": "print nil == nil // expect: true\n\nprint true == true // expect: true\nprint true == false // expect: false\n\nprint 1 == 1 // expect: true\nprint 1 == 2 // expect: false\n\nprint \"str\" == \"str\" // expect: true\nprint \"str\" == \"ing\" // expect: false\n\nprint nil == false // expect: false\nprint false == 0 // expect: false\nprint 0 == \"0\" // expect: false\n"
  },
  {
    "path": "test/operator/equals_class.morpho",
    "content": "// Bound methods have identity equality.\nclass Foo {}\nclass Bar {}\n\nprint Foo == Foo // expect: true\nprint Foo == Bar // expect: false\nprint Bar == Foo // expect: false\nprint Bar == Bar // expect: true\n\nprint Foo == \"Foo\" // expect: false\nprint Foo == nil   // expect: false\nprint Foo == 123   // expect: false\nprint Foo == true  // expect: false\n"
  },
  {
    "path": "test/operator/equals_method.morpho",
    "content": "// Bound methods have identity equality.\nclass Foo {\n  method() {}\n}\n\nvar foo = Foo()\nvar fooMethod = foo.method\n\n// Same bound method.\nprint fooMethod == fooMethod // expect: true\n\n// Different closurizations.\nprint foo.method == foo.method // expect: false\n"
  },
  {
    "path": "test/operator/fpcompare.morpho",
    "content": "// Floating point limits\n\nvar eps = 2^-53 // Machine epsilon ~ 2.22e-16\n\nvar min = 2^-1022 // Smallest normalized representable number\n\nprint 0 < eps // expect: true\nprint 0 == eps // expect: false\n\n// Adding eps to 1 should yield the same number\nprint 1 == 1+eps // expect: true\nprint 1 == 1+10*eps // expect: false\n\nprint -eps < 0 // expect: true\nprint -eps == 0 // expect: false\n\n// DBL_EPS shouldn't equal zero\nprint min == 0 // expect: false\nprint eps*min == 0 // expect: true\n\n// Ensure signs don't matter\nprint -eps*min == eps*min // expect: true\n\n// 1000*eps is not normalized\nprint -(1000*eps)*min == (1000*eps)*min // expect: false\n"
  },
  {
    "path": "test/operator/greater_nonnum_num.morpho",
    "content": "\"1\" > 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/greater_num_nonnum.morpho",
    "content": "1 > \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/greater_or_equal_nonnum_num.morpho",
    "content": "\"1\" >= 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/greater_or_equal_num_nonnum.morpho",
    "content": "1 >= \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/less_nonnum_num.morpho",
    "content": "\"1\" < 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/less_num_nonnum.morpho",
    "content": "1 < \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/less_or_equal_nonnum_num.morpho",
    "content": "\"1\" <= 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/less_or_equal_num_nonnum.morpho",
    "content": "1 <= \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/more_comparison.morpho",
    "content": "// Test comparison operators\n\n// Equality\nprint 1==1 // expect: true\nprint 1==2 // expect: false\nprint 1.0==1 // expect: true\nprint 1==1.000000001 // expect: false\nprint 1==1.000000000000001 // expect: false\nprint 0==0.4 // expect: false\nprint 0.4==0 // expect: false\nprint \"Eggs\"==\"Eggs\" // expect: true\nprint \"Eggs\"==\"Bacon\" // expect: false\n\nprint \"-\" // expect: -\n\n// Neq\nprint 1!=1 // expect: false\nprint 1!=2 // expect: true\nprint 0!=0.4 // expect: true\nprint 0.4!=0 // expect: true\nprint \"Eggs\"!=\"Eggs\" // expect: false\nprint \"Eggs\"!=\"Bacon\" // expect: true\n\nprint \"-\" // expect: -\n\n// Lt\nprint 1<1 // expect: false\nprint 1<2 // expect: true\nprint 2<1 // expect: false\nprint 1<1.000000001 // expect: true\nprint 1.000000001<1.000000001 // expect: false\nprint 1.000000002<1.000000001 // expect: false\nprint 0<0.4 // expect: true\nprint 0.4<0 // expect: false\n\nprint \"-\" // expect: -\n\n// Lte\nprint 1<=1 // expect: true\nprint 1<=2 // expect: true\nprint 2<=1 // expect: false\nprint 1<=1.000000001 // expect: true\nprint 1.000000001<=1.000000001 // expect: true\nprint 1.000000002<=1.000000001 // expect: false\nprint 0<=0.4 // expect: true\nprint 0.4<=0 // expect: false\n\nprint \"-\" // expect: -\n\n// Gt\nprint 1>1 // expect: false\nprint 1>2 // expect: false\nprint 2>1 // expect: true\nprint 1>1.000000001 // expect: false\nprint 1.000000001>1.000000001 // expect: false\nprint 1.000000002>1.000000001 // expect: true\nprint 0>0.4 // expect: false\nprint 0.4>0 // expect: true\n\nprint \"-\" // expect: -\n\n// Gte\nprint 1>=1 // expect: true\nprint 1>=2 // expect: false\nprint 2>=1 // expect: true\nprint 1>=1.000000001 // expect: false\nprint 1.000000001>=1.000000001 // expect: true\nprint 1.000000002>=1.000000001 // expect: true\nprint 1.000000001>=1.000000002 // expect: false\nprint 0>=0.4 // expect: false\nprint 0.4>=0 // expect: true\n"
  },
  {
    "path": "test/operator/multiply.morpho",
    "content": "print 5 * 3 // expect: 15\nprint 12.34 * 0.3 // expect: 3.702\n"
  },
  {
    "path": "test/operator/multiply_nonnum_num.morpho",
    "content": "\"1\" * 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/multiply_num_nonnum.morpho",
    "content": "1 * \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/negate.morpho",
    "content": "print -(3) // expect: -3\nprint --(3) // expect: 3\nprint ---(3) // expect: -3\n"
  },
  {
    "path": "test/operator/negate_nonnum.morpho",
    "content": "-\"s\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/not.morpho",
    "content": "print !true     // expect: false\nprint !false    // expect: true\nprint !!true    // expect: true\n\nprint !123      // expect: false\nprint !0        // expect: false\n\nprint !nil     // expect: true\n\nprint !\"\"       // expect: false\n\nfn foo() {}\nprint !foo      // expect: false\n"
  },
  {
    "path": "test/operator/not_class.morpho",
    "content": "class Bar {}\nprint !Bar      // expect: false\nprint !Bar()    // expect: false\n"
  },
  {
    "path": "test/operator/not_equals.morpho",
    "content": "print nil != nil // expect: false\n\nprint true != true // expect: false\nprint true != false // expect: true\n\nprint 1 != 1 // expect: false\nprint 1 != 2 // expect: true\n\nprint \"str\" != \"str\" // expect: false\nprint \"str\" != \"ing\" // expect: true\n\nprint nil != false // expect: true\nprint false != 0 // expect: true\nprint 0 != \"0\" // expect: true\n"
  },
  {
    "path": "test/operator/subtract.morpho",
    "content": "print 4 - 3 // expect: 1\nprint 1.2 - 1.2 // expect: 0\n"
  },
  {
    "path": "test/operator/subtract_nonnum_num.morpho",
    "content": "\"1\" - 1 // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/subtract_num_nonnum.morpho",
    "content": "1 - \"1\" // expect error 'InvldOp'\n"
  },
  {
    "path": "test/operator/ternary.morpho",
    "content": "// Ternary operator \n\nvar a = 1\nvar b = 2 \n\nprint a < b ? 1 : 0 // expect: 1\nprint a > b ? 1 : 0 // expect: 0\n\nprint (a < b ? 1 : 0) // expect: 1\n\nprint 2 + (a > b ? 1 : 3) // expect: 5\n\n"
  },
  {
    "path": "test/operator/ternary_in_function.morpho",
    "content": "// Ternary operator \n\nfn f(a,b) {\n    return 1 + (a < b ? 1 : 0) \n}\n\nprint f(1,2) // expect: 2\nprint f(2,1) // expect: 1\n"
  },
  {
    "path": "test/operator/ternary_in_loop.morpho",
    "content": "// Ternary operator in a loop\n\nvar a = 3\n\nfor (i in 1..5) {\n    print (i<a ? \"less\" : \"more\")\n}\n\n// expect: less\n// expect: less\n// expect: more\n// expect: more\n// expect: more\n"
  },
  {
    "path": "test/operator/ternary_missing_colon.morpho",
    "content": "// Ternary operator with missing colon\n\nvar a = 1\nvar b = 2 \n\nprint (a < b ? 1) // expect error 'TrnryMssngColon'\n"
  },
  {
    "path": "test/operator/ternary_nested.morpho",
    "content": "// Nested ternary operators\n\nvar a = 3\n\nfor (i in 1..5) {\n    print (i<a ? \"less\" : (i==a ? \"same\" : \"more\"))\n}\n\n// expect: less\n// expect: less\n// expect: same\n// expect: more\n// expect: more\n"
  },
  {
    "path": "test/print/missing_argument.morpho",
    "content": "\nprint\n\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/programs/delta_blue.morpho",
    "content": "// Delta Blue\n// Non-benchmark version; just runs once to check that it works\n\nvar ORDERED\n\nclass Strength {\n\tinit(value, name) {\n\t\tself.value = value\n\t\tself.name = name\n\t}\n\n\tnextWeaker() { return ORDERED[self.value] }\n\n\tstronger(s1,s2) {\n\t\treturn s1.value < s2.value\n\t}\n\tweaker(s1,s2) {\n\t\treturn s1.value > s2.value\n\t}\n\tweakest(s1,s2) {\n\t\tif (Strength.weaker(s1,s2)) {\n\t\t\treturn s1\n\t\t} else {\n\t\t\treturn s2\n\t\t}\n\t}\n\tstrongest(s1,s2) {\n\t\tif (Strength.stronger(s1,s2)) {\n\t\t\treturn s1\n\t\t} else {\n\t\t\treturn s2\n\t\t}\n\t}\n}\n\nvar REQUIRED\t\t\t\t\t= Strength(0, \"required\")\nvar STRONG_PREFERRED\t= Strength(1, \"strongPreferred\")\nvar PREFERRED\t\t\t\t\t= Strength(2, \"preferred\")\nvar STRONG_DEFAULT\t  = Strength(3, \"strongDefault\")\nvar NORMAL \t\t\t\t\t\t= Strength(4, \"normal\")\nvar WEAK_DEFAULT\t\t\t= Strength(5, \"weakDefault\")\nvar WEAKEST\t\t\t\t\t  = Strength(6, \"weakest\")\n\nORDERED = List(\n  WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT, PREFERRED, STRONG_PREFERRED\n)\n\nvar ThePlanner\n\nclass Constraint {\n  init (strength) {\n    self.strength = strength\n  }\n\n  // Activate this constraint and attempt to satisfy it.\n  addConstraint() {\n    self.addToGraph()\n    ThePlanner.incrementalAdd(self)\n  }\n\n  // Attempt to find a way to enforce this constraint. If successful,\n  // record the solution, perhaps modifying the current dataflow\n  // graph. Answer the constraint that this constraint overrides, if\n  // there is one, or nil, if there isn't.\n  // Assume: I am not already satisfied.\n  satisfy(mark) {\n    self.chooseMethod(mark)\n    if (!self.isSatisfied()) {\n      if (self.strength == REQUIRED) {\n        print \"Could not satisfy a required constraint!\"\n      }\n      return nil\n    }\n\n    self.markInputs(mark)\n    var out = self.output()\n    var overridden = out.determinedBy\n    if (overridden != nil) overridden.markUnsatisfied()\n    out.determinedBy = self\n    if (!ThePlanner.addPropagate(self, mark)) print \"Cycle encountered\"\n    out.mark = mark\n    return overridden\n  }\n\n  destroyConstraint() {\n    if (self.isSatisfied()) ThePlanner.incrementalRemove(self)\n    self.removeFromGraph()\n  }\n\n  // Normal constraints are not input constraints.  An input constraint\n  // is one that depends on external state, such as the mouse, the\n  // keyboard, a clock, or some arbitrary piece of imperative code.\n  isInput() { return false; }\n}\n\n// Abstract superclass for constraints having a single possible output variable.\nclass UnaryConstraint < Constraint {\n  init(myOutput, strength) {\n    super.init(strength)\n    self.satisfied = false\n    self.myOutput = myOutput\n    self.addConstraint()\n  }\n\n  // Adds this constraint to the constraint graph.\n  addToGraph() {\n    self.myOutput.addConstraint(self)\n    self.satisfied = false\n  }\n\n  // Decides if this constraint can be satisfied and records that decision.\n  chooseMethod(mark) {\n    self.satisfied = (self.myOutput.mark != mark) &&\n        Strength.stronger(self.strength, self.myOutput.walkStrength)\n  }\n\n  // Returns true if this constraint is satisfied in the current solution.\n  isSatisfied() { return self.satisfied; }\n\n  markInputs(mark) {\n    // has no inputs.\n  }\n\n  // Returns the current output variable.\n  output() { return self.myOutput; }\n\n  // Calculate the walkabout strength, the stay flag, and, if it is\n  // 'stay', the value for the current output of this constraint. Assume\n  // this constraint is satisfied.\n  recalculate() {\n    self.myOutput.walkStrength = self.strength\n    self.myOutput.stay = !self.isInput()\n    if (self.myOutput.stay) self.execute() // Stay optimization.\n  }\n\n  // Records that this constraint is unsatisfied.\n  markUnsatisfied() {\n    self.satisfied = false\n  }\n\n  inputsKnown(mark) { return true }\n\n  removeFromGraph() {\n    if (self.myOutput != nil) self.myOutput.removeConstraint(self)\n    self.satisfied = false\n  }\n}\n\n// Variables that should, with some level of preference, stay the same.\n// Planners may exploit the fact that instances, if satisfied, will not\n// change their output during plan execution.  This is called \"stay\n// optimization\".\nclass StayConstraint < UnaryConstraint {\n  init(variable, strength) {\n    super.init(variable, strength)\n  }\n\n  execute() {\n    // Stay constraints do nothing.\n  }\n}\n\n// A unary input constraint used to mark a variable that the client\n// wishes to change.\nclass EditConstraint < UnaryConstraint {\n  init(variable, strength) {\n    super.init(variable, strength)\n  }\n\n  // Edits indicate that a variable is to be changed by imperative code.\n  isInput() { return true }\n\n  execute() {\n    // Edit constraints do nothing.\n  }\n}\n\n// Directions.\nvar NONE = 1\nvar FORWARD = 2\nvar BACKWARD = 0\n\n\n// Abstract superclass for constraints having two possible output\n// variables.\nclass BinaryConstraint < Constraint {\n  init(v1, v2, strength) {\n    super.init(strength)\n    self.v1 = v1\n    self.v2 = v2\n    self.direction = NONE\n    self.addConstraint()\n  }\n\n  // Decides if this constraint can be satisfied and which way it\n  // should flow based on the relative strength of the variables related,\n  // and record that decision.\n  chooseMethod(mark) {\n    if (self.v1.mark == mark) {\n      if (self.v2.mark != mark &&\n          Strength.stronger(self.strength, self.v2.walkStrength)) {\n        self.direction = FORWARD\n      } else {\n        self.direction = NONE\n      }\n    }\n\n    if (self.v2.mark == mark) {\n      if (self.v1.mark != mark &&\n          Strength.stronger(self.strength, self.v1.walkStrength)) {\n        self.direction = BACKWARD\n      } else {\n        self.direction = NONE\n      }\n    }\n\n    if (Strength.weaker(self.v1.walkStrength, self.v2.walkStrength)) {\n      if (Strength.stronger(self.strength, self.v1.walkStrength)) {\n        self.direction = BACKWARD\n      } else {\n        self.direction = NONE\n      }\n    } else {\n      if (Strength.stronger(self.strength, self.v2.walkStrength)) {\n        self.direction = FORWARD\n      } else {\n        self.direction = BACKWARD\n      }\n    }\n  }\n\n  // Add this constraint to the constraint graph.\n  addToGraph() {\n    self.v1.addConstraint(self)\n    self.v2.addConstraint(self)\n    self.direction = NONE\n  }\n\n  // Answer true if this constraint is satisfied in the current solution.\n  isSatisfied() { return self.direction != NONE; }\n\n  // Mark the input variable with the given mark.\n  markInputs(mark) {\n    self.input().mark = mark\n  }\n\n  // Returns the current input variable\n  input() {\n\t\tif (self.direction == FORWARD) {\n\t\t\treturn self.v1\n\t\t} else {\n\t\t\treturn self.v2\n\t\t}\n\t}\n\n  // Returns the current output variable.\n\toutput() {\n\t\tif (self.direction == FORWARD) {\n\t\t\treturn self.v2\n\t\t} else {\n\t\t\treturn self.v1\n\t\t}\n\t}\n\n  // Calculate the walkabout strength, the stay flag, and, if it is\n  // 'stay', the value for the current output of this\n  // constraint. Assume this constraint is satisfied.\n  recalculate() {\n    var ihn = self.input()\n    var out = self.output()\n    out.walkStrength = Strength.weakest(self.strength, ihn.walkStrength)\n    out.stay = ihn.stay\n    if (out.stay) self.execute()\n  }\n\n  // Record the fact that this constraint is unsatisfied.\n  markUnsatisfied() {\n    self.direction = NONE\n  }\n\n  inputsKnown(mark) {\n    var i = self.input()\n    return i.mark == mark || i.stay || i.determinedBy == nil\n  }\n\n  removeFromGraph() {\n    if (self.v1 != nil) self.v1.removeConstraint(self)\n    if (self.v2 != nil) self.v2.removeConstraint(self)\n    self.direction = NONE\n  }\n}\n\n// Relates two variables by the linear scaling relationship: \"v2 =\n// (v1 * scale) + offset\". Either v1 or v2 may be changed to maintain\n// this relationship but the scale factor and offset are considered\n// read-only.\nclass ScaleConstraint < BinaryConstraint {\n  init(src, scale, offset, dest, strength) {\n    self.scale = scale\n    self.offset = offset\n\t\tself.direction = NONE\n    super.init(src, dest, strength)\n  }\n\n  // Adds this constraint to the constraint graph.\n  addToGraph() {\n    super.addToGraph()\n    self.scale.addConstraint(self)\n    self.offset.addConstraint(self)\n  }\n\n  removeFromGraph() {\n    super.removeFromGraph()\n    if (self.scale != nil) self.scale.removeConstraint(self)\n    if (self.offset != nil) self.offset.removeConstraint(self)\n  }\n\n  markInputs(mark) {\n    super.markInputs(mark)\n    self.scale.mark = self.offset.mark = mark\n  }\n\n  // Enforce this constraint. Assume that it is satisfied.\n  execute() {\n    if (self.direction == FORWARD) {\n      self.v2.value = self.v1.value * self.scale.value + self.offset.value\n    } else {\n      // TODO: Is this the same semantics as ~/?\n      self.v1.value = floor((self.v2.value - self.offset.value) / self.scale.value)\n    }\n  }\n\n  // Calculate the walkabout strength, the stay flag, and, if it is\n  // 'stay', the value for the current output of this constraint. Assume\n  // this constraint is satisfied.\n  recalculate() {\n    var ihn = self.input()\n    var out = self.output()\n    out.walkStrength = Strength.weakest(self.strength, ihn.walkStrength)\n    out.stay = ihn.stay && self.scale.stay && self.offset.stay\n    if (out.stay) self.execute()\n  }\n}\n\n// Constrains two variables to have the same value.\nclass EqualityConstraint < BinaryConstraint {\n  init(v1, v2, strength) {\n    super.init(v1, v2, strength)\n  }\n\n  // Enforce this constraint. Assume that it is satisfied.\n  execute() {\n    self.output().value = self.input().value\n  }\n}\n\n// A constrained variable. In addition to its value, it maintains the\n// structure of the constraint graph, the current dataflow graph, and\n// various parameters of interest to the DeltaBlue incremental\n// constraint solver.\nclass Variable {\n  init(name, value) {\n    self.constraints = List()\n    self.determinedBy = nil\n    self.mark = 0\n    self.walkStrength = WEAKEST\n    self.stay = true\n    self.name = name\n    self.value = value\n  }\n\n  // Add the given constraint to the set of all constraints that refer\n  // this variable.\n  addConstraint(constraint) {\n    self.constraints.append(constraint)\n  }\n\n  // Removes all traces of c from this variable.\n  removeConstraint(constraint) {\n\t\tvar new = List()\n\t\tfor (c in self.constraints) if (c!=constraint) new.append(c)\n    self.constraints = new\n    if (self.determinedBy == constraint) self.determinedBy = nil\n  }\n}\n\n\n// A Plan is an ordered list of constraints to be executed in sequence\n// to resatisfy all currently satisfiable constraints in the face of\n// one or more changing inputs.\nclass Plan {\n  init() {\n    self.list = List()\n  }\n\n  addConstraint(constraint) {\n    self.list.append(constraint)\n  }\n\n  size() { return self.list.count() }\n\n  execute() {\n    for (constraint in self.list) {\n      constraint.execute()\n    }\n  }\n}\n\nclass Planner {\n  init() {\n    self.currentMark = 0\n  }\n\n  // Attempt to satisfy the given constraint and, if successful,\n  // incrementally update the dataflow graph.  Details: If satifying\n  // the constraint is successful, it may override a weaker constraint\n  // on its output. The algorithm attempts to resatisfy that\n  // constraint using some other method. This process is repeated\n  // until either a) it reaches a variable that was not previously\n  // determined by any constraint or b) it reaches a constraint that\n  // is too weak to be satisfied using any of its methods. The\n  // variables of constraints that have been processed are marked with\n  // a unique mark value so that we know where we've been. This allows\n  // the algorithm to avoid getting into an infinite loop even if the\n  // constraint graph has an inadvertent cycle.\n  incrementalAdd(constraint) {\n    var mark = self.newMark()\n    var overridden = constraint.satisfy(mark)\n    while (overridden != nil) {\n      overridden = overridden.satisfy(mark)\n    }\n  }\n\n  // Entry point for retracting a constraint. Remove the given\n  // constraint and incrementally update the dataflow graph.\n  // Details: Retracting the given constraint may allow some currently\n  // unsatisfiable downstream constraint to be satisfied. We therefore collect\n  // a list of unsatisfied downstream constraints and attempt to\n  // satisfy each one in turn. This list is traversed by constraint\n  // strength, strongest first, as a heuristic for avoiding\n  // unnecessarily adding and then overriding weak constraints.\n  // Assume: [c] is satisfied.\n  incrementalRemove(constraint) {\n    var out = constraint.output()\n    constraint.markUnsatisfied()\n    constraint.removeFromGraph()\n    var unsatisfied = self.removePropagateFrom(out)\n    var strength = REQUIRED\n    while (true) {\n      for (u in unsatisfied) {\n        if (u.strength == strength) self.incrementalAdd(u)\n      }\n      strength = strength.nextWeaker()\n      if (strength == WEAKEST) break\n    }\n  }\n\n  // Select a previously unused mark value.\n  newMark() { self.currentMark += 1; return self.currentMark; }\n\n  // Extract a plan for resatisfaction starting from the given source\n  // constraints, usually a set of input constraints. This method\n  // assumes that stay optimization is desired; the plan will contain\n  // only constraints whose output variables are not stay. Constraints\n  // that do no computation, such as stay and edit constraints, are\n  // not included in the plan.\n  // Details: The outputs of a constraint are marked when it is added\n  // to the plan under construction. A constraint may be appended to\n  // the plan when all its input variables are known. A variable is\n  // known if either a) the variable is marked (indicating that has\n  // been computed by a constraint appearing earlier in the plan), b)\n  // the variable is 'stay' (i.e. it is a constant at plan execution\n  // time), or c) the variable is not determined by any\n  // constraint. The last provision is for past states of history\n  // variables, which are not stay but which are also not computed by\n  // any constraint.\n  // Assume: [sources] are all satisfied.\n  makePlan(sources) {\n    var mark = self.newMark()\n    var plan = Plan()\n    var todo = sources\n    while (todo.count() > 0) {\n      var constraint = todo.pop()\n      if (constraint.output().mark != mark && constraint.inputsKnown(mark)) {\n        plan.addConstraint(constraint)\n        constraint.output().mark = mark\n        self.addConstraintsConsumingTo(constraint.output(), todo)\n      }\n    }\n    return plan\n  }\n\n  // Extract a plan for resatisfying starting from the output of the\n  // given [constraints], usually a set of input constraints.\n  extractPlanFromConstraints(constraints) {\n    var sources = List()\n    for (constraint in constraints) {\n      // if not in plan already and eligible for inclusion.\n      if (constraint.isInput() && constraint.isSatisfied()) sources.append(constraint)\n    }\n    return self.makePlan(sources)\n  }\n\n  // Recompute the walkabout strengths and stay flags of all variables\n  // downstream of the given constraint and recompute the actual\n  // values of all variables whose stay flag is true. If a cycle is\n  // detected, remove the given constraint and answer\n  // false. Otherwise, answer true.\n  // Details: Cycles are detected when a marked variable is\n  // encountered downstream of the given constraint. The sender is\n  // assumed to have marked the inputs of the given constraint with\n  // the given mark. Thus, encountering a marked node downstream of\n  // the output constraint means that there is a path from the\n  // constraint's output to one of its inputs.\n  addPropagate(constraint, mark) {\n\t\tvar todo = List(constraint)\n    while (todo.count() > 0) {\n      var d = todo.pop()\n      if (d.output().mark == mark) {\n        self.incrementalRemove(constraint)\n        return false\n      }\n      d.recalculate()\n      self.addConstraintsConsumingTo(d.output(), todo)\n    }\n    return true\n  }\n\n  // Update the walkabout strengths and stay flags of all variables\n  // downstream of the given constraint. Answer a collection of\n  // unsatisfied constraints sorted in order of decreasing strength.\n  removePropagateFrom(out) {\n    out.determinedBy = nil\n    out.walkStrength = WEAKEST\n    out.stay = true\n\t\tvar unsatisfied = List()\n    var todo = List(out)\n    while (todo.count() > 0) {\n      var v = todo.pop()\n\n      for (constraint in v.constraints) {\n        if (!constraint.isSatisfied()) unsatisfied.append(constraint)\n      }\n\n      var determining = v.determinedBy\n      for (next in v.constraints) {\n        if (next != determining && next.isSatisfied()) {\n          next.recalculate()\n          todo.append(next.output())\n        }\n      }\n    }\n\n    return unsatisfied\n  }\n\n  addConstraintsConsumingTo(v, coll) {\n    var determining = v.determinedBy\n    for (constraint in v.constraints) {\n      if (constraint != determining && constraint.isSatisfied()) {\n        coll.append(constraint)\n      }\n    }\n  }\n}\n\nvar total = 0\n\n// This is the standard DeltaBlue benchmark. A long chain of equality\n// constraints is constructed with a stay constraint on one end. An\n// edit constraint is then added to the opposite end and the time is\n// measured for adding and removing this constraint, and extracting\n// and executing a constraint satisfaction plan. There are two cases.\n// In case 1, the added constraint is stronger than the stay\n// constraint and values must propagate down the entire length of the\n// chain. In case 2, the added constraint is weaker than the stay\n// constraint so it cannot be accomodated. The cost in this case is,\n// of course, very low. Typical situations lie somewhere between these\n// two extremes.\nfn chainTest(n) {\n  ThePlanner = Planner()\n  var prev = nil\n  var first = nil\n  var last = nil\n\n  // Build chain of n equality constraints.\n  for (i in 0..n) {\n    var v = Variable(\"v\", 0)\n    if (prev != nil) EqualityConstraint(prev, v, REQUIRED)\n    if (i == 0) first = v\n    if (i == n) last = v\n    prev = v\n  }\n\n  StayConstraint(last, STRONG_DEFAULT)\n  var edit = EditConstraint(first, PREFERRED)\n  var plan = ThePlanner.extractPlanFromConstraints(List(edit))\n  for (i in 0..99) {\n    first.value = i\n    plan.execute()\n    total = total + last.value\n  }\n}\n\nfn change(v, newValue) {\n  var edit = EditConstraint(v, PREFERRED)\n  var plan = ThePlanner.extractPlanFromConstraints(List(edit))\n  for (i in 0..9) {\n    v.value = newValue\n    plan.execute()\n  }\n\n  edit.destroyConstraint()\n}\n\n// This test constructs a two sets of variables related to each\n// other by a simple linear transformation (scale and offset). The\n// time is measured to change a variable on either side of the\n// mapping and to change the scale and offset factors.\nfn projectionTest(n) {\n  ThePlanner = Planner()\n  var scale = Variable(\"scale\", 10)\n  var offset = Variable(\"offset\", 1000)\n  var src = nil\n  var dst = nil\n\n  var dests = List()\n  for (i in 0..n-1) {\n    src = Variable(\"src\", i)\n    dst = Variable(\"dst\", i)\n    dests.append(dst)\n    StayConstraint(src, NORMAL)\n    ScaleConstraint(src, scale, offset, dst, REQUIRED)\n  }\n\n  change(src, 17)\n  total = total + dst.value\n  if (dst.value != 1170) print \"Projection 1 failed\"\n\n  change(dst, 1050)\n  total = total + src.value\n  if (src.value != 5) print \"Projection 2 failed\"\n\n  change(scale, 5)\n  for (i in 0..n - 2) {\n    total = total + dests[i].value\n    if (dests[i].value != i * 5 + 1000) print \"Projection 3 failed\"\n  }\n\n  change(offset, 2000)\n  for (i in 0..n - 2) {\n    total = total + dests[i].value\n    if (dests[i].value != i * 5 + 2000) print \"Projection 4 failed\"\n  }\n}\n\n/*\n * Run the test\n */\n\n// Usually loop over 40 times for the benchmark, but here we'll just do one\nchainTest(100)\nprojectionTest(100)\n\nprint total\n// expect: 351635\n"
  },
  {
    "path": "test/programs/fannkuch.morpho",
    "content": "/* The Computer Language Benchmarks Game\n *   http://benchmarksgame.alioth.debian.org/\n *   reimplemented in morpho by T J Atherton\n */\n\n// Swap elements of an array\nfn swap(x, i, j) {\n    var tmp=x[i]\n    x[i]=x[j]\n    x[j]=tmp\n}\n\n// Count the number of swaps\nfn countswaps(n, p, q) {\n    var swaps = 0\n\n    // Take the first element, X, and reverse the order of the first X elements.\n    // Do this repeatedly until X==1\n    for (i in 0..n-1) q[i]=p[i] // Start by copying the array\n    for (var q0=q[0]; q0!=1; q0=q[0]) {\n        for (i in 0..Int(floor(q0/2))-1) { // Reverse first q0 elements\n            swap(q, i, q0-1-i)\n        }\n        swaps+=1\n    }\n    return swaps\n}\n\n// Fannkuch benchmark\nfn fannkuch(n) {\n    // Create a list of numbers 1..n\n    var p = List()\n    for (i in 1..n) p.append(i)\n    var q = p.clone()\n    var indx[n] // Used to keep track of the permutations\n    for (i in 0..n-1) indx[i]=0\n\n    var maxswaps = 0 // Track the maximum number of swaps needed\n\n    var i=0\n    var sign=1\n    while (i<n) {\n        var swaps=countswaps(n, p, q)\n        // Keep track of the maximum number of swaps\n        if (swaps>maxswaps) maxswaps=swaps\n\n        // Generate next permutation by Heap's algorithm\n        if (indx[i]<i) {\n            if (sign==1) {\n                swap(p, 0, i)\n            } else {\n                swap(p, indx[i], i)\n            }\n            indx[i]+=1\n            i=0\n            sign=1\n        } else {\n            indx[i]=0\n            i+=1\n            sign*=-1\n        }\n    }\n\n    return maxswaps\n}\n\nvar n = 7\nvar out = fannkuch(n)\n\nprint \"Pfannkuchen(${n}) = ${out}\"\n// expect: Pfannkuchen(7) = 16\n"
  },
  {
    "path": "test/programs/fibonacci.morpho",
    "content": "// Tests recursion\n\nfn f(x) {\n\tif (x<2) return x\n\treturn f(x-1)+f(x-2)\n}\n\nprint f(28)\n// expect: 317811\n"
  },
  {
    "path": "test/programs/histogram.morpho",
    "content": "// Find the minimum of an enumerable object\nfn min(x) {\n  var mn=x[0]\n  for (i in x) {\n    if (i<mn) mn=i\n  }\n  return mn\n}\n\n// Find the maximum of an enumerable object\nfn max(x) {\n  var mx=x[0]\n  for (i in x) {\n    if (i>mx) mx=i\n  }\n  return mx\n}\n\n// Display a histogram\nfn histogram(lst, nbins) {\n  var cnt[nbins]\n  var bins[nbins+1]\n  // Calculate the bin bounds\n  var mx = max(lst), mn = min(lst)\n  for (i in 0..nbins) {\n    bins[i]=mn+i*(mx-mn)/(nbins)\n  }\n\n  // Assign each element of lst to a bin\n  for (x in lst) {\n    var k=0\n    while (x>bins[k+1]) k+=1\n    cnt[k]+=1\n  }\n\n  // Show histogram\n  for (i in 0..nbins-1) {\n    print \"${bins[i]}-${bins[i+1]}: ${cnt[i]}\"\n  }\n}\n\n// Construct a list of random numbers\nvar N=1000000\nvar a=Matrix(N)\nfor (i in 0..N-1) a[i]=i\n\n// Show the histogram\nhistogram(a, 5)\n// expect: 0-200000: 200000\n// expect: 200000-400000: 200000\n// expect: 400000-599999: 200000\n// expect: 599999-799999: 200000\n// expect: 799999-999999: 200000\n"
  },
  {
    "path": "test/programs/integrate.morpho",
    "content": "// Simple Euler integrator for an oscillator\n\nvar Tmax = 1  // Maximum time\nvar dt = 0.01 // Timestep\nvar x0 = 2    // Initial position\nvar v0 = 0    // Initial velocity\nvar m = 1     // Mass\nvar g = -9.81 // Gravity constant (-ve means down)\n\nvar x = List(x0)\nvar v = List(v0)\n\n// Calculates the force given the position x and velocity v\nfn force(x, v) {\n  return m*g\n}\n\n// Main integration loop\nfor (t in 0..Tmax:dt) {\n  var vnew = v[-1] + force(x[-1], v[-1])*dt/m\n  var xnew = x[-1] + vnew*dt\n  v.append(vnew)\n  x.append(xnew)\n}\nprint \"${Tmax} ${x[-1]} ${v[-1]}\"\n// expect: 1 -3.05313 -9.9081\n"
  },
  {
    "path": "test/property/call_function_field.morpho",
    "content": "class Foo {}\n\nfn bar(a, b) {\n  print \"bar\"\n  print a\n  print b\n}\n\nvar foo = Foo()\nfoo.bar = bar\n\nfoo.bar(1, 2)\n// expect: bar\n// expect: 1\n// expect: 2\n"
  },
  {
    "path": "test/property/call_nonfunction_field.morpho",
    "content": "class Foo {}\n\nvar foo = Foo()\nfoo.bar = \"not fn\"\n\nfoo.bar() // expect error 'Uncallable'\n"
  },
  {
    "path": "test/property/get_on_bool.morpho",
    "content": "true.foo // expect error 'ClssLcksMthd'\n"
  },
  {
    "path": "test/property/get_on_class.morpho",
    "content": "class Foo {}\nFoo.bar // expect error 'ClssLcksMthd'\n"
  },
  {
    "path": "test/property/get_on_function.morpho",
    "content": "fn foo() {}\n\nfoo.bar // expect error 'ClssLcksMthd'\n"
  },
  {
    "path": "test/property/get_on_nil.morpho",
    "content": "nil.foo // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/get_on_num.morpho",
    "content": "// Try to get a property from an object\n\n123.foo // expect error 'ClssLcksMthd'\n"
  },
  {
    "path": "test/property/get_on_string.morpho",
    "content": "// Strings have methods but not properties\n\nprint \"Hello\".count\n// expect: Hello.<fn count>\n\n\"str\".foo // expect error 'ClssLcksMthd'\n"
  },
  {
    "path": "test/property/index_property_in_args.morpho",
    "content": "class Test {\n    init() {\n        \n        // This works\n\n        var x = Matrix([1,2,3])\n\n        self.lstx = [ x[0], x[1], x[2] ]\n        print self.lstx // expect: [ 1, 2, 3 ]\n\n        // *** Check that indexing a property works in a list ***\n        \n        self.y = Matrix([1,2,3])\n\n        self.lsty = [ self.y[0], self.y[1], self.y[2] ]\n        print self.lsty // expect: [ 1, 2, 3 ]\n        \n        // They individually work fine\n        print self.y[0] // expect: 1\n        print self.y[1] // expect: 2\n        print self.y[2] // expect: 3\n\n        // This works too\n        self.a = 1\n        self.b = 2\n        self.c = 3\n        self.lstz = [self.a, self.b, self.c]\n        print self.lstz  // expect: [ 1, 2, 3 ]\n\n    }\n    \n}\n\nvar t = Test()\n"
  },
  {
    "path": "test/property/many.morpho",
    "content": "class Foo {}\n\nvar foo = Foo()\nfn setFields() {\n  foo.bilberry = \"bilberry\"\n  foo.lime = \"lime\"\n  foo.elderberry = \"elderberry\"\n  foo.raspberry = \"raspberry\"\n  foo.gooseberry = \"gooseberry\"\n  foo.longan = \"longan\"\n  foo.mandarine = \"mandarine\"\n  foo.kiwifruit = \"kiwifruit\"\n  foo.orange = \"orange\"\n  foo.pomegranate = \"pomegranate\"\n  foo.tomato = \"tomato\"\n  foo.banana = \"banana\"\n  foo.juniper = \"juniper\"\n  foo.damson = \"damson\"\n  foo.blackcurrant = \"blackcurrant\"\n  foo.peach = \"peach\"\n  foo.grape = \"grape\"\n  foo.mango = \"mango\"\n  foo.redcurrant = \"redcurrant\"\n  foo.watermelon = \"watermelon\"\n  foo.plumcot = \"plumcot\"\n  foo.papaya = \"papaya\"\n  foo.cloudberry = \"cloudberry\"\n  foo.rambutan = \"rambutan\"\n  foo.salak = \"salak\"\n  foo.physalis = \"physalis\"\n  foo.huckleberry = \"huckleberry\"\n  foo.coconut = \"coconut\"\n  foo.date = \"date\"\n  foo.tamarind = \"tamarind\"\n  foo.lychee = \"lychee\"\n  foo.raisin = \"raisin\"\n  foo.apple = \"apple\"\n  foo.avocado = \"avocado\"\n  foo.nectarine = \"nectarine\"\n  foo.pomelo = \"pomelo\"\n  foo.melon = \"melon\"\n  foo.currant = \"currant\"\n  foo.plum = \"plum\"\n  foo.persimmon = \"persimmon\"\n  foo.olive = \"olive\"\n  foo.cranberry = \"cranberry\"\n  foo.boysenberry = \"boysenberry\"\n  foo.blackberry = \"blackberry\"\n  foo.passionfruit = \"passionfruit\"\n  foo.mulberry = \"mulberry\"\n  foo.marionberry = \"marionberry\"\n  foo.plantain = \"plantain\"\n  foo.lemon = \"lemon\"\n  foo.yuzu = \"yuzu\"\n  foo.loquat = \"loquat\"\n  foo.kumquat = \"kumquat\"\n  foo.salmonberry = \"salmonberry\"\n  foo.tangerine = \"tangerine\"\n  foo.durian = \"durian\"\n  foo.pear = \"pear\"\n  foo.cantaloupe = \"cantaloupe\"\n  foo.quince = \"quince\"\n  foo.guava = \"guava\"\n  foo.strawberry = \"strawberry\"\n  foo.nance = \"nance\"\n  foo.apricot = \"apricot\"\n  foo.jambul = \"jambul\"\n  foo.grapefruit = \"grapefruit\"\n  foo.clementine = \"clementine\"\n  foo.jujube = \"jujube\"\n  foo.cherry = \"cherry\"\n  foo.feijoa = \"feijoa\"\n  foo.jackfruit = \"jackfruit\"\n  foo.fig = \"fig\"\n  foo.cherimoya = \"cherimoya\"\n  foo.pineapple = \"pineapple\"\n  foo.blueberry = \"blueberry\"\n  foo.jabuticaba = \"jabuticaba\"\n  foo.miracle = \"miracle\"\n  foo.dragonfruit = \"dragonfruit\"\n  foo.satsuma = \"satsuma\"\n  foo.tamarillo = \"tamarillo\"\n  foo.honeydew = \"honeydew\"\n}\n\nsetFields()\n\nfn printFields() {\n  print foo.apple // expect: apple\n  print foo.apricot // expect: apricot\n  print foo.avocado // expect: avocado\n  print foo.banana // expect: banana\n  print foo.bilberry // expect: bilberry\n  print foo.blackberry // expect: blackberry\n  print foo.blackcurrant // expect: blackcurrant\n  print foo.blueberry // expect: blueberry\n  print foo.boysenberry // expect: boysenberry\n  print foo.cantaloupe // expect: cantaloupe\n  print foo.cherimoya // expect: cherimoya\n  print foo.cherry // expect: cherry\n  print foo.clementine // expect: clementine\n  print foo.cloudberry // expect: cloudberry\n  print foo.coconut // expect: coconut\n  print foo.cranberry // expect: cranberry\n  print foo.currant // expect: currant\n  print foo.damson // expect: damson\n  print foo.date // expect: date\n  print foo.dragonfruit // expect: dragonfruit\n  print foo.durian // expect: durian\n  print foo.elderberry // expect: elderberry\n  print foo.feijoa // expect: feijoa\n  print foo.fig // expect: fig\n  print foo.gooseberry // expect: gooseberry\n  print foo.grape // expect: grape\n  print foo.grapefruit // expect: grapefruit\n  print foo.guava // expect: guava\n  print foo.honeydew // expect: honeydew\n  print foo.huckleberry // expect: huckleberry\n  print foo.jabuticaba // expect: jabuticaba\n  print foo.jackfruit // expect: jackfruit\n  print foo.jambul // expect: jambul\n  print foo.jujube // expect: jujube\n  print foo.juniper // expect: juniper\n  print foo.kiwifruit // expect: kiwifruit\n  print foo.kumquat // expect: kumquat\n  print foo.lemon // expect: lemon\n  print foo.lime // expect: lime\n  print foo.longan // expect: longan\n  print foo.loquat // expect: loquat\n  print foo.lychee // expect: lychee\n  print foo.mandarine // expect: mandarine\n  print foo.mango // expect: mango\n  print foo.marionberry // expect: marionberry\n  print foo.melon // expect: melon\n  print foo.miracle // expect: miracle\n  print foo.mulberry // expect: mulberry\n  print foo.nance // expect: nance\n  print foo.nectarine // expect: nectarine\n  print foo.olive // expect: olive\n  print foo.orange // expect: orange\n  print foo.papaya // expect: papaya\n  print foo.passionfruit // expect: passionfruit\n  print foo.peach // expect: peach\n  print foo.pear // expect: pear\n  print foo.persimmon // expect: persimmon\n  print foo.physalis // expect: physalis\n  print foo.pineapple // expect: pineapple\n  print foo.plantain // expect: plantain\n  print foo.plum // expect: plum\n  print foo.plumcot // expect: plumcot\n  print foo.pomegranate // expect: pomegranate\n  print foo.pomelo // expect: pomelo\n  print foo.quince // expect: quince\n  print foo.raisin // expect: raisin\n  print foo.rambutan // expect: rambutan\n  print foo.raspberry // expect: raspberry\n  print foo.redcurrant // expect: redcurrant\n  print foo.salak // expect: salak\n  print foo.salmonberry // expect: salmonberry\n  print foo.satsuma // expect: satsuma\n  print foo.strawberry // expect: strawberry\n  print foo.tamarillo // expect: tamarillo\n  print foo.tamarind // expect: tamarind\n  print foo.tangerine // expect: tangerine\n  print foo.tomato // expect: tomato\n  print foo.watermelon // expect: watermelon\n  print foo.yuzu // expect: yuzu\n}\n\nprintFields()\n"
  },
  {
    "path": "test/property/method.morpho",
    "content": "class Foo {\n  bar(arg) {\n    print arg\n  }\n}\n\nvar bar = Foo().bar\nprint \"got method\" // expect: got method\nbar(\"arg\")          // expect: arg\n"
  },
  {
    "path": "test/property/method_binds_self.morpho",
    "content": "class Foo {\n  sayName(a) {\n    print self.name\n    print a\n  }\n}\n\nvar foo1 = Foo()\nfoo1.name = \"foo1\"\n\nvar foo2 = Foo()\nfoo2.name = \"foo2\"\n\n// Store the method reference on another object.\nfoo2.func = foo1.sayName\n// Still retains original receiver.\nfoo2.func(1)\n// expect: foo1\n// expect: 1\n"
  },
  {
    "path": "test/property/on_instance.morpho",
    "content": "class Foo {}\n\nvar foo = Foo()\n\nprint foo.bar = \"bar value\" // expect: bar value\nprint foo.baz = \"baz value\" // expect: baz value\n\nprint foo.bar // expect: bar value\nprint foo.baz // expect: baz value\n"
  },
  {
    "path": "test/property/property_error_in_index.morpho",
    "content": "var A = [1,2,3]\nprint A[[2].2]\n// expect error 'PptyNmRqd'\n"
  },
  {
    "path": "test/property/property_index.morpho",
    "content": "// Assign to property with index\n\nclass Foo {\n  init() {\n    self.prop = Array(1)\n    self.prop[0] = \"Bar\"\n  }\n}\n\nprint Foo().prop[0]\n// expect: Bar\n"
  },
  {
    "path": "test/property/set_evaluation_order.morpho",
    "content": "undefined1.bar // expect error 'SymblUndf'\n  = undefined2\n"
  },
  {
    "path": "test/property/set_index_property.morpho",
    "content": "\nclass Foo {\n  init() {\n    self.elements = Array(3)\n    for (i in 0...3) {\n      self.elements[i]=i\n    }\n  }\n}\n\nvar mb = Foo()\nfor (i in mb.elements) print i\n// expect: 0\n// expect: 1\n// expect: 2\n"
  },
  {
    "path": "test/property/set_on_bool.morpho",
    "content": "true.foo = \"value\" // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/set_on_class.morpho",
    "content": "class Foo {}\nFoo.bar = \"value\" // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/set_on_function.morpho",
    "content": "fn foo() {}\n\nfoo.bar = \"value\" // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/set_on_nil.morpho",
    "content": "nil.foo = \"value\" // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/set_on_num.morpho",
    "content": "123.foo = \"value\" // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/set_on_string.morpho",
    "content": "\"str\".foo = \"value\" // expect error 'NotAnObj'\n"
  },
  {
    "path": "test/property/undefined.morpho",
    "content": "class Foo {}\nvar foo = Foo()\n\nfoo.bar // expect error 'ObjLcksPrp'\n"
  },
  {
    "path": "test/range/constructor.morpho",
    "content": "// Ranges\n\n// Default step is 1 and the default is an exclusive range\nvar a = Range(1,3)\nprint a\n// expect: 1...3\nfor (x in a) print x\n// expect: 1\n// expect: 2\n\na = InclusiveRange(1,3)\nprint a\n// expect: 1..3\nfor (x in a) print x\n// expect: 1\n// expect: 2\n// expect: 3\n\n// Set a stepsize; upper limit is excluded\na = Range(1,5,2)\nprint a\n// expect: 1...5:2\nfor (x in a) print x\n// expect: 1\n// expect: 3\n\n// Step in reverse\na = Range(-0.1,-0.5,-0.1)\nprint a\n// expect: -0.1...-0.5:-0.1\nfor (x in a) print x\n// expect: -0.1\n// expect: -0.2\n// expect: -0.3\n// expect: -0.4\n\n// Step in reverse in an incommensurate way\na = Range(-0.1,-0.5,-0.13)\nprint a\n// expect: -0.1...-0.5:-0.13\nfor (x in a) print x\n// expect: -0.1\n// expect: -0.23\n// expect: -0.36\n// expect: -0.49\n\n// Inconsistent sign in step yields no elements\na = Range(-0.1,-0.5,0.13)\nprint a\n// expect: -0.1...-0.5:0.13\nfor (x in a) print x\n\n// Range with only one element\nvar a = Range(5,5)\nprint a\n// expect: 5...5\nfor (x in a) print x\n\n// InclusiveRange with only one element\nvar a = InclusiveRange(5,5)\nprint a\n// expect: 5..5\nfor (x in a) print x\n// expect: 5\n"
  },
  {
    "path": "test/range/count_down.morpho",
    "content": "// Count downwards\n\nfor (i in -0.1..-0.5:-0.1) print i\n// expect: -0.1\n// expect: -0.2\n// expect: -0.3\n// expect: -0.4\n// expect: -0.5\n"
  },
  {
    "path": "test/range/exclusive.morpho",
    "content": "// Exclusive Ranges\n\nfor (i in 1...5) print i\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n\nfor (i in 1...2:0.5) print i\n// expect: 1\n// expect: 1.5\n\nfor (i in 0...-2:-0.5) print i\n// expect: 0\n// expect: -0.5\n// expect: -1\n// expect: -1.5\n\nfor (i in 0.1...1.2:0.1) print i\n// expect: 0.1\n// expect: 0.2\n// expect: 0.3\n// expect: 0.4\n// expect: 0.5\n// expect: 0.6\n// expect: 0.7\n// expect: 0.8\n// expect: 0.9\n// expect: 1\n// expect: 1.1"
  },
  {
    "path": "test/range/inclusive.morpho",
    "content": "// Inclusive Ranges\n\nfor (i in 1..5) print i\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n// expect: 5\n\nfor (i in 1..2:0.5) print i\n// expect: 1\n// expect: 1.5\n// expect: 2\n\nfor (i in 0..-2:-0.5) print i\n// expect: 0\n// expect: -0.5\n// expect: -1\n// expect: -1.5\n// expect: -2\n\nfor (i in 0.1..1.2:0.1) print i\n// expect: 0.1\n// expect: 0.2\n// expect: 0.3\n// expect: 0.4\n// expect: 0.5\n// expect: 0.6\n// expect: 0.7\n// expect: 0.8\n// expect: 0.9\n// expect: 1\n// expect: 1.1\n// expect: 1.2\n\nfor (i in 0.1..0.35:0.1) print i\n// expect: 0.1\n// expect: 0.2\n// expect: 0.3"
  },
  {
    "path": "test/range/inherited.morpho",
    "content": "// Test Range methods inherited from Object \n\nvar a = 1..2\n\nprint a.respondsto(\"clss\") // expect: true\n\nprint islist(a.respondsto()) // expect: true\n\nprint a.clss() // expect: @Range\n\nprint a.superclass() // expect: @Object\n\nprint islist(a.invoke(\"respondsto\")) // expect: true\n\nprint a.has(\"a\") // expect: false\n\nprint a.count() // expect: 2"
  },
  {
    "path": "test/range/invalid_constructor_too_few_args.morpho",
    "content": "// Too few arguments to constructor function\n\nvar a = Range() // expect error 'RngArgs'"
  },
  {
    "path": "test/range/invalid_constructor_too_many_args.morpho",
    "content": "// Too many arguments to constructor function\n\nvar a = Range(1,2,3,4) // expect error 'RngArgs'"
  },
  {
    "path": "test/range/invalid_constructor_type.morpho",
    "content": "// Incorrect type of args to constructor function\n\nvar a = Range(1,5.0,\"Hello\") // expect error 'RngArgs'"
  },
  {
    "path": "test/range/list_constructor.morpho",
    "content": "// Construct a List from a Range with a problematic version\n\nprint List(1.0 ... 1.2 : 0.15)\n// expect: [ 1, 1.15 ]"
  },
  {
    "path": "test/range/step_too_fine.morpho",
    "content": "// Range with very small stepsize \n\nvar range = -1..1:0.00000000001 // expect error 'RngStpSz'\n"
  },
  {
    "path": "test/range/syntax.morpho",
    "content": "// Ranges\n\nvar a = 1..2\nprint a\n// expect: 1..2\n\nfor (i in a) print i\n// expect: 1\n// expect: 2\n\nvar b = 1..2:2\nprint b\n// expect: 1..2:2\n\nvar n=2\na = n..2*n:2\nprint a\n// expect: 2..4:2\n\nfor (i in 1..5:2) print i\n// expect: 1\n// expect: 3\n// expect: 5\n"
  },
  {
    "path": "test/return/after_else.morpho",
    "content": "fn f() {\n  if (false) \"no\" else return \"ok\"\n}\n\nprint f() // expect: ok\n"
  },
  {
    "path": "test/return/after_if.morpho",
    "content": "fn f() {\n  if (true) return \"ok\"\n}\n\nprint f() // expect: ok\n"
  },
  {
    "path": "test/return/after_while.morpho",
    "content": "fn f() {\n  while (true) return \"ok\"\n}\n\nprint f() // expect: ok\n"
  },
  {
    "path": "test/return/at_top_level.morpho",
    "content": "return \"nooooo\" // expect error 'GlblRtrn'\n"
  },
  {
    "path": "test/return/in_for_in.morpho",
    "content": "fn f() {\n  for (i in 1..10) {\n    print i\n    if (i==5) return\n  }\n}\n\nf()\n// expect: 1\n// expect: 2\n// expect: 3\n// expect: 4\n// expect: 5\n"
  },
  {
    "path": "test/return/in_function.morpho",
    "content": "fn f() {\n  return \"ok\"\n  print \"bad\"\n}\n\nprint f() // expect: ok\n"
  },
  {
    "path": "test/return/in_method.morpho",
    "content": "class Foo {\n  method() {\n    return \"ok\"\n    print \"bad\"\n  }\n}\n\nprint Foo().method() // expect: ok\n"
  },
  {
    "path": "test/return/return_nil_if_no_value.morpho",
    "content": "fn f() {\n  return\n  print \"bad\"\n}\n\nprint f() // expect: nil\n"
  },
  {
    "path": "test/selection/add_grade.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\nm.addgrade(1)\n\nfn f(x,y,z) {\n  return y>0.5\n}\n\nvar s = Selection(m, f)\n\ns.addgrade(1, partials=true)\nvar l = s.idlistforgrade(1)\nl.sort()\nprint l\n// expect: [ 1, 2, 3, 4 ]\n\ns.removegrade(1)\nprint s.idlistforgrade(1)\n// expect: [  ]\n\ns.addgrade(1)\nprint s.idlistforgrade(1)\n// expect: [ 4 ]\n\ns.addgrade(2, partials=true)\nprint s.idlistforgrade(2)\n// expect: [ 0, 1 ]\n"
  },
  {
    "path": "test/selection/boundary.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\nm.addgrade(1)\n\nvar s = Selection(m, boundary=true)\nvar lst = s.idlistforgrade(1)\nlst.sort()\n\nprint lst\n// expect: [ 0, 1, 3, 4 ]\n\nlst = s.idlistforgrade(0)\nlst.sort()\n\nprint lst\n// expect: [ 0, 1, 2, 3 ]\n"
  },
  {
    "path": "test/selection/circlesquare.mesh",
    "content": "vertices\n\n1 -2. -2. 0\n2 -2. -1.5 0\n3 -2. -1. 0\n4 -2. -0.5 0\n5 -2. 0. 0\n6 -2. 0.5 0\n7 -2. 1. 0\n8 -2. 1.5 0\n9 -2. 2. 0\n10 -1.5 -2. 0\n11 -1.5 -1.5 0\n12 -1.5 -1. 0\n13 -1.5 -0.5 0\n14 -1.5 0. 0\n15 -1.5 0.5 0\n16 -1.5 1. 0\n17 -1.5 1.5 0\n18 -1.5 2. 0\n19 -1. -2. 0\n20 -1. -1.5 0\n21 -1. -1. 0\n22 -1. -0.5 0\n23 -1. 0. 0\n24 -1. 0.5 0\n25 -1. 1. 0\n26 -1. 1.5 0\n27 -1. 2. 0\n28 -0.951057 -0.309017 0\n29 -0.951057 0.309017 0\n30 -0.809017 -0.587785 0\n31 -0.809017 0.587785 0\n32 -0.587785 -0.809017 0\n33 -0.587785 0.809017 0\n34 -0.5 -2. 0\n35 -0.5 -1.5 0\n36 -0.5 -1. 0\n37 -0.5 -0.5 0\n38 -0.5 0. 0\n39 -0.5 0.5 0\n40 -0.5 1. 0\n41 -0.5 1.5 0\n42 -0.5 2. 0\n43 -0.309017 -0.951057 0\n44 -0.309017 0.951057 0\n45 0. -2. 0\n46 0. -1.5 0\n47 0. -1. 0\n48 0. -0.5 0\n49 0. 0. 0\n50 0. 0.5 0\n51 0. 1. 0\n52 0. 1.5 0\n53 0. 2. 0\n54 0.309017 -0.951057 0\n55 0.309017 0.951057 0\n56 0.5 -2. 0\n57 0.5 -1.5 0\n58 0.5 -1. 0\n59 0.5 -0.5 0\n60 0.5 0. 0\n61 0.5 0.5 0\n62 0.5 1. 0\n63 0.5 1.5 0\n64 0.5 2. 0\n65 0.587785 -0.809017 0\n66 0.587785 0.809017 0\n67 0.809017 -0.587785 0\n68 0.809017 0.587785 0\n69 0.951057 -0.309017 0\n70 0.951057 0.309017 0\n71 1. -2. 0\n72 1. -1.5 0\n73 1. -1. 0\n74 1. -0.5 0\n75 1. 0. 0\n76 1. 0.5 0\n77 1. 1. 0\n78 1. 1.5 0\n79 1. 2. 0\n80 1.5 -2. 0\n81 1.5 -1.5 0\n82 1.5 -1. 0\n83 1.5 -0.5 0\n84 1.5 0. 0\n85 1.5 0.5 0\n86 1.5 1. 0\n87 1.5 1.5 0\n88 1.5 2. 0\n89 2. -2. 0\n90 2. -1.5 0\n91 2. -1. 0\n92 2. -0.5 0\n93 2. 0. 0\n94 2. 0.5 0\n95 2. 1. 0\n96 2. 1.5 0\n97 2. 2. 0\n\nedges\n\n1 1 10\n2 10 2\n3 2 1\n4 10 11\n5 11 2\n6 11 19\n7 19 20\n8 20 11\n9 10 19\n10 19 34\n11 34 20\n12 20 21\n13 21 11\n14 11 12\n15 12 2\n16 3 12\n17 12 4\n18 4 3\n19 3 2\n20 12 13\n21 13 4\n22 13 21\n23 21 22\n24 22 13\n25 12 21\n26 21 30\n27 30 22\n28 22 28\n29 28 13\n30 14 4\n31 13 14\n32 20 35\n33 35 21\n34 36 21\n35 35 36\n36 35 34\n37 34 45\n38 45 35\n39 43 36\n40 35 43\n41 32 21\n42 36 32\n43 45 46\n44 46 35\n45 45 56\n46 56 46\n47 47 46\n48 46 54\n49 54 47\n50 46 43\n51 47 43\n52 37 28\n53 28 30\n54 30 37\n55 32 30\n56 32 37\n57 37 43\n58 43 48\n59 48 37\n60 32 43\n61 47 48\n62 37 38\n63 38 28\n64 48 38\n65 54 48\n66 28 14\n67 5 14\n68 14 6\n69 6 5\n70 5 4\n71 14 15\n72 15 6\n73 24 15\n74 15 29\n75 29 24\n76 14 29\n77 23 29\n78 14 23\n79 24 16\n80 16 15\n81 28 23\n82 16 6\n83 8 7\n84 7 16\n85 16 8\n86 9 8\n87 8 17\n88 17 9\n89 16 17\n90 25 17\n91 16 25\n92 26 18\n93 18 17\n94 17 26\n95 18 9\n96 7 6\n97 24 25\n98 31 39\n99 39 33\n100 33 31\n101 31 29\n102 29 39\n103 33 25\n104 25 31\n105 24 31\n106 38 49\n107 49 39\n108 39 38\n109 44 39\n110 39 50\n111 50 44\n112 44 33\n113 29 38\n114 23 38\n115 44 40\n116 40 33\n117 25 26\n118 25 40\n119 40 26\n120 27 26\n121 26 41\n122 41 27\n123 27 18\n124 40 41\n125 44 41\n126 52 42\n127 42 41\n128 41 52\n129 42 27\n130 44 52\n131 48 49\n132 46 57\n133 57 54\n134 59 48\n135 54 59\n136 57 56\n137 56 71\n138 71 57\n139 57 58\n140 58 54\n141 57 72\n142 72 58\n143 71 72\n144 71 80\n145 80 72\n146 65 58\n147 58 73\n148 73 65\n149 65 54\n150 49 59\n151 59 60\n152 60 49\n153 59 69\n154 69 60\n155 60 61\n156 61 49\n157 67 69\n158 59 67\n159 73 67\n160 67 65\n161 74 69\n162 67 74\n163 59 65\n164 69 75\n165 75 60\n166 72 73\n167 72 81\n168 81 73\n169 80 81\n170 80 89\n171 89 81\n172 81 82\n173 82 73\n174 81 90\n175 90 82\n176 89 90\n177 90 91\n178 91 82\n179 82 83\n180 83 73\n181 84 69\n182 69 83\n183 83 84\n184 74 73\n185 83 74\n186 84 75\n187 83 92\n188 92 84\n189 83 91\n190 91 92\n191 92 93\n192 93 84\n193 84 70\n194 70 75\n195 70 60\n196 50 61\n197 61 55\n198 55 50\n199 50 49\n200 61 66\n201 66 55\n202 55 51\n203 51 50\n204 61 68\n205 68 66\n206 61 70\n207 70 68\n208 68 77\n209 77 66\n210 62 66\n211 77 62\n212 62 55\n213 51 52\n214 44 51\n215 55 52\n216 53 52\n217 52 63\n218 63 53\n219 53 42\n220 55 63\n221 77 63\n222 63 62\n223 78 64\n224 64 63\n225 63 78\n226 64 53\n227 70 76\n228 76 68\n229 76 85\n230 85 77\n231 77 76\n232 70 85\n233 85 86\n234 86 77\n235 85 94\n236 94 86\n237 85 93\n238 93 94\n239 94 95\n240 95 86\n241 84 85\n242 86 87\n243 87 77\n244 78 87\n245 87 79\n246 79 78\n247 78 77\n248 87 88\n249 88 79\n250 79 64\n251 96 88\n252 87 96\n253 97 88\n254 96 97\n255 87 95\n256 95 96\n\nfaces\n\n1 1 10 2\n2 2 10 11\n3 11 19 20\n4 19 11 10\n5 20 19 34\n6 11 20 21\n7 2 11 12\n8 3 12 4\n9 12 3 2\n10 4 12 13\n11 13 21 22\n12 21 13 12\n13 22 21 30\n14 13 22 28\n15 12 11 21\n16 14 4 13\n17 20 35 21\n18 36 21 35\n19 35 34 45\n20 43 36 35\n21 32 21 36\n22 45 46 35\n23 46 45 56\n24 47 46 54\n25 43 35 46\n26 43 46 47\n27 37 28 30\n28 28 22 30\n29 30 21 32\n30 37 30 32\n31 37 43 48\n32 43 37 32\n33 48 43 47\n34 28 37 38\n35 32 36 43\n36 38 37 48\n37 47 54 48\n38 20 34 35\n39 14 13 28\n40 5 14 6\n41 14 5 4\n42 6 14 15\n43 24 15 29\n44 14 29 15\n45 23 29 14\n46 15 24 16\n47 14 28 23\n48 6 15 16\n49 8 7 16\n50 9 8 17\n51 16 17 8\n52 25 17 16\n53 26 18 17\n54 17 18 9\n55 7 6 16\n56 25 16 24\n57 31 39 33\n58 39 31 29\n59 31 33 25\n60 31 25 24\n61 38 49 39\n62 44 39 50\n63 39 44 33\n64 38 39 29\n65 29 23 38\n66 33 44 40\n67 25 26 17\n68 26 25 40\n69 27 26 41\n70 26 27 18\n71 40 41 26\n72 41 40 44\n73 52 42 41\n74 41 42 27\n75 25 33 40\n76 52 41 44\n77 31 24 29\n78 23 28 38\n79 48 49 38\n80 46 57 54\n81 59 48 54\n82 57 56 71\n83 54 57 58\n84 57 72 58\n85 72 57 71\n86 72 71 80\n87 65 58 73\n88 54 58 65\n89 49 59 60\n90 48 59 49\n91 60 59 69\n92 49 60 61\n93 67 69 59\n94 73 67 65\n95 74 69 67\n96 67 59 65\n97 54 65 59\n98 60 69 75\n99 72 73 58\n100 46 56 57\n101 72 81 73\n102 81 72 80\n103 81 80 89\n104 73 81 82\n105 81 90 82\n106 90 81 89\n107 82 90 91\n108 73 82 83\n109 84 69 83\n110 74 73 83\n111 69 84 75\n112 83 69 74\n113 83 92 84\n114 92 83 91\n115 84 92 93\n116 82 91 83\n117 75 84 70\n118 73 74 67\n119 60 75 70\n120 50 61 55\n121 50 49 61\n122 55 61 66\n123 50 55 51\n124 61 68 66\n125 68 61 70\n126 66 68 77\n127 62 66 77\n128 70 61 60\n129 55 66 62\n130 51 52 44\n131 51 55 52\n132 53 52 63\n133 52 53 42\n134 63 52 55\n135 77 63 62\n136 78 64 63\n137 63 64 53\n138 62 63 55\n139 68 70 76\n140 50 51 44\n141 76 85 77\n142 76 70 85\n143 77 85 86\n144 76 77 68\n145 85 94 86\n146 94 85 93\n147 86 94 95\n148 84 85 70\n149 77 86 87\n150 78 87 79\n151 78 77 87\n152 79 87 88\n153 78 79 64\n154 96 88 87\n155 97 88 96\n156 96 87 95\n157 86 95 87\n158 77 78 63\n159 93 85 84\n160 49 50 39\n"
  },
  {
    "path": "test/selection/clone.morpho",
    "content": "// Clone\n\nvar m = Mesh(\"square.mesh\")\n\nvar s = Selection(m, fn (x,y,z) y>0.5)\nprint s // expect: <Selection>\n\nvar t = s.clone()\n\nprint t.isselected(0, 0) // expect: false\nprint t.isselected(0, 1) // expect: false\nprint t.isselected(0, 2) // expect: true\n\nprint t.idlistforgrade(0) // expect: [ 2, 3 ]\n"
  },
  {
    "path": "test/selection/count.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\n\nfn f(x,y,z) {\n  return y>0.5\n}\n\nvar s = Selection(m, f)\n\nprint s.count(0) // expect: 2\n"
  },
  {
    "path": "test/selection/get_index.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\n\nfn f(x,y,z) {\n  return y>0.5\n}\n\nvar s = Selection(m, f)\nprint s // expect: <Selection>\n\nprint s[0,0] // expect: false\nprint s[0,1] // expect: false\nprint s[0,2] // expect: true\n"
  },
  {
    "path": "test/selection/inherited.morpho",
    "content": "// Test Matrix methods inherited from Object \n\nvar m = Mesh() \n\nvar a = Selection(m)\n\nprint a.respondsto(\"respondsto\") // expect: true\n\nprint islist(a.respondsto()) // expect: true\n\nprint a.clss() // expect: @Selection\n\nprint a.superclass() // expect: @Object\n\nprint islist(a.invoke(\"respondsto\")) // expect: true\n\nprint a.has(\"a\") // expect: false\n\n"
  },
  {
    "path": "test/selection/no_mesh_error.morpho",
    "content": "import optimize\n\n// Create mesh and director field\nvar m = Mesh(\"circlesquare.mesh\")\n\n\n//Circle boundary & area\nvar bndCirc = Selection(m, fn(x,y,z) x^2+y^2<1.0001 && x^2+y^2>0.9999)\nbndCirc.addgrade(1)\n\n\n\t\n// Set up a regularization problem\nvar reg = OptimizationProblem(m)\n\n// Create shape and field optimizers\nvar ropt = ShapeOptimizer(reg, m)\n\n\n\t\n// Must ensure boundary remains correctly fixed\nropt.fix(Selection(bndCirc, boundary=true))\t\n// expect error 'SlNoMsh'"
  },
  {
    "path": "test/selection/selection.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\n\nfn f(x,y,z) {\n  return y>0.5\n}\n\nvar s = Selection(m, f)\nprint s // expect: <Selection>\n\nprint s.isselected(0, 0) // expect: false\nprint s.isselected(0, 1) // expect: false\nprint s.isselected(0, 2) // expect: true\n\nprint s.idlistforgrade(0) // expect: [ 2, 3 ]\n\nprint s.isselected(0) // expect Error: 'SlIsSlArg'\n"
  },
  {
    "path": "test/selection/selection_withmatrix.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\n\nvar p = ScalarPotential(\n  fn (x,y,z) (y-0.5),\n  fn (x,y,z) Matrix([0,-1,0])\n)\n\nvar s = Selection(m, fn (q) q>0, p.integrand(m))\nprint s // expect: <Selection>\n\nprint s.isselected(0, 0) // expect: false\nprint s.isselected(0, 1) // expect: false\nprint s.isselected(0, 2) // expect: true\n\nprint s.idlistforgrade(0) // expect: [ 2, 3 ]\n"
  },
  {
    "path": "test/selection/set_index.morpho",
    "content": "// Test selections\n\nvar m = Mesh(\"square.mesh\")\n\nvar s = Selection(m)\nprint s // expect: <Selection>\n\ns[0,1] = true\nprint s[0,1] // expect: true\n\ns[0,1] = false\nprint s[0,1] // expect: false\n"
  },
  {
    "path": "test/selection/set_operations.morpho",
    "content": "// Selection set operations\n\nvar m = Mesh(\"square.mesh\")\n\nfn f(x,y,z) {\n  return y>0.5\n}\n\nfn g(x,y,z) {\n  return x<0.5\n}\n\nvar s1 = Selection(m, f)\nprint s1.idlistforgrade(0)\n// expect: [ 2, 3 ]\n\nvar s2 = Selection(m, g)\nprint s2.idlistforgrade(0)\n// expect: [ 0, 2 ]\n\nprint s1.union(s2).idlistforgrade(0)\n// expect: [ 0, 2, 3 ]\n\nprint s1.intersection(s2).idlistforgrade(0)\n// expect: [ 2 ]\n\nprint s1.difference(s2).idlistforgrade(0)\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/selection/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/self/closure.morpho",
    "content": "class Foo {\n  getClosure() {\n    fn closure() {\n      return self.toString()\n    }\n    return closure\n  }\n\n  toString() { return \"Foo\" }\n}\n\nvar closure = Foo().getClosure()\nprint closure() // expect: Foo\n"
  },
  {
    "path": "test/self/nested_class.morpho",
    "content": "// Tests nested classes, which are currently forbidden \n\n/*class Outer {\n  method() {\n    print self // exp <Outer>\n\n    fn f() {\n      print self // exp <Outer>\n\n      class Inner {\n        method() {\n          print self // exp <Inner>\n        }\n      }\n\n      Inner().method()\n    }\n    f()\n  }\n}\n\nOuter().method()\n*/"
  },
  {
    "path": "test/self/nested_closure.morpho",
    "content": "class Foo {\n  getClosure() {\n    fn f() {\n      fn g() {\n        fn h() {\n          return self.toString()\n        }\n        return h\n      }\n      return g\n    }\n    return f\n  }\n\n  toString() { return \"Foo\" }\n}\n\nvar closure = Foo().getClosure()\nprint closure()()() // expect: Foo\n"
  },
  {
    "path": "test/self/self_at_top_level.morpho",
    "content": "self // Error 'SlfOtsdClss'\n"
  },
  {
    "path": "test/self/self_in_method.morpho",
    "content": "class Foo {\n  bar() { return self }\n  baz() { return \"baz\" }\n}\n\nprint Foo().bar().baz() // expect: baz\n"
  },
  {
    "path": "test/self/self_in_top_level_function.morpho",
    "content": "fn foo() {\n  self // error 'SlfOtsdClss'\n}\n"
  },
  {
    "path": "test/self/self_set_prop_index.morpho",
    "content": "\nclass A {\n  a(prop) {\n    self[prop]=true\n  }\n}\n\nvar b = A()\n\nb.a(\"foo\")\n\nprint b.foo\n// expect: true\n"
  },
  {
    "path": "test/slice/arraySlicing.morpho",
    "content": "var A = Array([[0,1,2,3],[4,5,6,7],[8,9,10,11]])\n// ranges\nprint A[0..1,1..2] \n// expect: [ [ 1, 2 ], [ 5, 6 ] ]\n\n// lists\nprint A[[1,2,0,1],[0,0,0,2]]\n// expect: [ [ 4, 4, 4, 6 ], [ 8, 8, 8, 10 ], [ 0, 0, 0, 2 ], [ 4, 4, 4, 6 ] ]\n\n// mix of range and list\nprint A[0..2,[0,0,0,2]]\n// expect: [ [ 0, 0, 0, 2 ], [ 4, 4, 4, 6 ], [ 8, 8, 8, 10 ] ]\n\n// mix of range and int\nprint A[0..1,3]\n// expect: [ [ 3 ], [ 7 ] ]\n\n// mix of list and int\nprint A[2,[0,1,2,1,2]]\n// expect: [ [ 8, 9, 10, 9, 10 ] ]\n\n// range out of bounds\ntry{\n\tprint A[-1..2,1..2] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\ntry{\n\tprint A[0..3,1..2] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\n\n\n// range is not int\ntry{\n\tprint A[0.0..2,1..2] \n} catch{\n\t\"NonNmIndx\": print \"NonNmIndx\"\n// expect: NonNmIndx\n}\n\n// list is out of bounds\ntry{\n\tprint A[[-1,2,-1],1..2] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\ntry{\n\tprint A[[100],1..2] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\n\n// list is not int\ntry{\n\tprint A[[1,2,1],[0.2,0.1]] \n} catch{\n\t\"NonNmIndx\": print \"NonNmIndx\"\n// expect: NonNmIndx\n}\ntry{\n\tprint A[[1,2,1],[[1]]] \n} catch{\n\t\"NonNmIndx\": print \"NonNmIndx\"\n// expect: NonNmIndx\n}\n\n// int + list but int is out of bounds\ntry{\n\tprint A[[0,2,1],-1] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\ntry{\n\tprint A[[0,2,1],100] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\ntry{\n\tprint A[[0,2,1],1.01] \n} catch{\n\t\"NonNmIndx\": print \"NonNmIndx\"\n// expect: NonNmIndx\n\n\n//wrong dim\ntry{\n\tprint A[[1,2],[2,3],[0,1]] \n} catch{\n\t\"ArrayDim\": print \"ArrayDim\"\n// expect: ArrayDim\n}\n// garbage in \n\ntry{\n\tprint A[A] \n} catch{\n\t\"NonNmIndx\": print \"NonNmIndx\"\n// expect: NonNmIndx\n}\ntry{\n\tprint A[nil] \n} catch{\n\t\"NonNmIndx\": print \"NonNmIndx\"\n// expect: NonNmIndx\n}"
  },
  {
    "path": "test/slice/listSlicing.morpho",
    "content": "var A = [[0,1,2,3],4,5,6,7,8,9,10,11]\n// ranges\nprint A[0..1] \n// expect: [ <List>, 4 ]\n\n// lists\nprint A[[1,2,0,1]]\n// expect: [ 4, 5, <List>, 4 ]\n\nprint A[-1..2] \n// expect: [ 11, <List>, 4, 5 ]\n\n\n// range out of bounds\ntry{\n\tprint A[0..100] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\n\n\n// range is not int\ntry{\n\tprint A[0.0..2] \n} catch{\n\t\"LstArgs\": print \"LstArgs\"\n// expect: LstArgs\n}\n\n// list is out of bounds\n\tprint A[[-1,2,-1]] \n// expect: [ 11, 5, 11 ]\n\ntry{\n\tprint A[[100]] \n} catch{\n\t\"IndxBnds\": print \"IndxBnds\"\n// expect: IndxBnds\n}\n\n// list is not int\ntry{\n\tprint A[[0.2,0.1]] \n} catch{\n\t\"LstArgs\": print \"LstArgs\"\n// expect: LstArgs\n}\ntry{\n\tprint A[[[1]]] \n} catch{\n\t\"LstArgs\": print \"LstArgs\"\n// expect: LstArgs\n}\n\n//wrong dim\ntry{\n\tprint A[[1],[2]] \n} catch{\n\t\"LstNumArgs\": print \"LstNumArgs\"\n// expect: LstNumArgs\n}\n\n\n// garbage in \n\ntry{\n\tprint A[A] \n} catch{\n\t\"LstArgs\": print \"LstArgs\"\n// expect: LstArgs\n}\ntry{\n\tprint A[nil] \n} catch{\n\t\"LstArgs\": print \"LstArgs\"\n// expect: LstArgs\n}"
  },
  {
    "path": "test/slice/matrixSlicing.morpho",
    "content": "var A = Matrix([[0,1,2,3],[4,5,6,7],[8,9,10,11]])\n// ranges\nprint A[0..1,1..2] \n// expect: [ 1 2 ]\n// expect: [ 5 6 ]\n\n// lists\nprint A[[1,2,0,1],[0,0,0,2]]\n// expect: [ 4 4 4 6 ]\n// expect: [ 8 8 8 10 ]\n// expect: [ 0 0 0 2 ]\n// expect: [ 4 4 4 6 ]\n\n// mix of range and list\nprint A[0..2,[0,0,0,2]]\n// expect: [ 0 0 0 2 ]\n// expect: [ 4 4 4 6 ]\n// expect: [ 8 8 8 10 ]\n\n// mix of range and int\nprint A[0..1,3]\n// expect: [ 3 ]\n// expect: [ 7 ]\n\n// mix of list and int\nprint A[2,[0,1,2,1,2]]\n// expect: [ 8 9 10 9 10 ]\n\nprint A[0..1]\n// expect: [ 0 ]\n// expect: [ 4 ]\n\n// range out of bounds\ntry{\n\tprint A[-1..2,1..2] \n} catch{\n\t\"MtrxBnds\": print \"MtrxBnds\"\n// expect: MtrxBnds\n}\ntry{\n\tprint A[0..3,1..2] \n} catch{\n\t\"MtrxBnds\": print \"MtrxBnds\"\n// expect: MtrxBnds\n}\n\n\n// range is not int\ntry{\n\tprint A[0.0..2,1..2] \n} catch{\n\t\"MtrxInvldIndx\": print \"MtrxInvldIndx\"\n// expect: MtrxInvldIndx\n}\n\n// list is out of bounds\ntry{\n\tprint A[[-1,2,-1],1..2] \n} catch{\n\t\"MtrxBnds\": print \"MtrxBnds\"\n// expect: MtrxBnds\n}\ntry{\n\tprint A[[100],1..2] \n} catch{\n\t\"MtrxBnds\": print \"MtrxBnds\"\n// expect: MtrxBnds\n}\n\n// list is not int\ntry{\n\tprint A[[1,2,1],[0.2,0.1]] \n} catch{\n\t\"MtrxInvldIndx\": print \"MtrxInvldIndx\"\n// expect: MtrxInvldIndx\n}\ntry{\n\tprint A[[1,2,1],[[1]]] \n} catch{\n\t\"MtrxInvldIndx\": print \"MtrxInvldIndx\"\n// expect: MtrxInvldIndx\n}\n\n// int + list but int is out of bounds\ntry{\n\tprint A[[0,2,1],-1] \n} catch{\n\t\"MtrxBnds\": print \"MtrxBnds\"\n// expect: MtrxBnds\n}\ntry{\n\tprint A[[0,2,1],100] \n} catch{\n\t\"MtrxBnds\": print \"MtrxBnds\"\n// expect: MtrxBnds\n}\ntry{\n\tprint A[[0,2,1],0.0] \n} catch{\n\t\"MtrxInvldIndx\": print \"MtrxInvldIndx\"\n// expect: MtrxInvldIndx\n}\n\n//wrong dim\ntry{\n\tprint A[[1,2],[2,3],[0,1]] \n} catch{\n\t\"MtrxInvldNumIndx\": print \"MtrxInvldNumIndx\"\n// expect: MtrxInvldNumIndx\n}\n// garbage in \n\ntry{\n\tprint A[A] \n} catch{\n\t\"MtrxInvldIndx\": print \"MtrxInvldIndx\"\n// expect: MtrxInvldIndx\n}\ntry{\n\tprint A[nil] \n} catch{\n\t\"MtrxInvldIndx\": print \"MtrxInvldIndx\"\n// expect: MtrxInvldIndx\n}\ntry{\n\tA[1,2,3,4,5]\n} catch{\n\t\"MtrxInvldNumIndx\": print \"MtrxInvldNumIndx\"\n// expect: MtrxInvldNumIndx\n}\n"
  },
  {
    "path": "test/sparse/arithmetic.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\nvar b = Sparse([[0,1,1],[1,0,1],[2,3,1],[3,2,1]])\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\nprint b\n// expect: [ 0 1 0 0 ]\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 0 0 1 ]\n// expect: [ 0 0 1 0 ]\n\nprint a+b\n// expect: [ 1 1 0 0 ]\n// expect: [ 1 1 -1 0 ]\n// expect: [ 0 -1 1 1 ]\n// expect: [ 0 0 1 1 ]\n\nprint a-b\n// expect: [ 1 -1 0 0 ]\n// expect: [ -1 1 -1 0 ]\n// expect: [ 0 -1 1 -1 ]\n// expect: [ 0 0 -1 1 ]\n\nprint a*b\n// expect: [ 0 1 0 0 ]\n// expect: [ 1 0 0 -1 ]\n// expect: [ -1 0 0 1 ]\n// expect: [ 0 0 1 0 ]\n"
  },
  {
    "path": "test/sparse/block_constructor_dimensions.morpho",
    "content": "// Ensure a block contructor produces the correct matrix dimensions\n\nvar a = Sparse()\n\nfor (i in 0..4:2) {\n  a[i,i]=1\n  a[i+1,i]=2\n  a[i+3,i]=3\n}\n\nprint Sparse([[a.column(0),a.column(1)]])\n// expect: [ 1 0 ]\n// expect: [ 2 0 ]\n// expect: [ 0 0 ]\n// expect: [ 3 0 ]\n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\n"
  },
  {
    "path": "test/sparse/clone.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nvar b = a.clone()\n\nprint b\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nb[1,1]=2\nb[3,3]=2\n\nprint b\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 2 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 2 ]\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nvar c = a*a\n\nprint c\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 2 -2 0 ]\n// expect: [ 0 -2 2 0 ]\n// expect: [ 0 0 0 1 ]\n\nvar d=c.clone()\n\nprint d\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 2 -2 0 ]\n// expect: [ 0 -2 2 0 ]\n// expect: [ 0 0 0 1 ]\n\nprint c\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 2 -2 0 ]\n// expect: [ 0 -2 2 0 ]\n// expect: [ 0 0 0 1 ]\n"
  },
  {
    "path": "test/sparse/col_indices.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse()\n\nfor (i in 0..10:2) {\n  a[i,i]=1\n}\nprint a.colindices()\n// expect: [ 0, 2, 4, 6, 8, 10 ]\n"
  },
  {
    "path": "test/sparse/column.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse()\n\nfor (i in 0..4:2) {\n  a[i,i]=1\n  a[i+1,i]=2\n  a[i+3,i]=3\n}\n\nprint a.column(0)\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 0 ]\n// expect: [ 3 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n\nprint a.column(1)\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n\nprint a.column(4)\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 0 ]\n// expect: [ 3 ]\n\n"
  },
  {
    "path": "test/sparse/concatenate.morpho",
    "content": "// Concatenate sparse matrices together\n\nvar a = Sparse([[0,1,1], [1,0,1]])\nvar b = Matrix([2,3])\n\nprint a\n// expect: [ 0 1 ]\n// expect: [ 1 0 ]\n\nvar c = Sparse([[a, b], [b.transpose(), 0]])\n\nprint c\n// expect: [ 0 1 2 ]\n// expect: [ 1 0 3 ]\n// expect: [ 2 3 0 ]\n\nvar c = Sparse([[a, 0, b], [0, a, b], [b.transpose(), b.transpose(), 0]])\n\nprint c\n// expect: [ 0 1 0 0 2 ]\n// expect: [ 1 0 0 0 3 ]\n// expect: [ 0 0 0 1 2 ]\n// expect: [ 0 0 1 0 3 ]\n// expect: [ 2 3 2 3 0 ]\n\nvar c = Sparse([[a, b], [b, 0]])\n// expect error 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/sparse/dense_sparse_mul.morpho",
    "content": "// Multiply a dense matrix by sparse matrix from the LHS \n\nvar A = Sparse(4,3) \nfor (i in 0...4) for (j in 0...3) A[i,j]=i+j\n\nprint A\n// expect: [ 0 1 2 ]\n// expect: [ 1 2 3 ]\n// expect: [ 2 3 4 ]\n// expect: [ 3 4 5 ]\n\nvar B = Matrix([[1,-2,3,-4],[-5,6,-7,8]])\n\nprint B\n// expect: [ 1 -2 3 -4 ]\n// expect: [ -5 6 -7 8 ]\n\nprint B * A\n// expect: [ -8 -10 -12 ]\n// expect: [ 16 18 20 ]\n\nvar A = Sparse(4,4) \nfor (i in 0...4) A[i,i]=i+1\nprint A \n// expect: [ 1 0 0 0 ]\n// expect: [ 0 2 0 0 ]\n// expect: [ 0 0 3 0 ]\n// expect: [ 0 0 0 4 ]\n\nvar B = Matrix([1,2,3,4])\nprint B\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n// expect: [ 4 ]\n\nprint B.transpose() * A\n// expect: [ 1 4 9 16 ]\n"
  },
  {
    "path": "test/sparse/dimensions.morpho",
    "content": "// Dimensions of a sparse matrix\n\nvar a = Sparse([[1,2,3],[2,4,1],[4,4,0]])\n\nprint a\n// expect: [ 0 0 0 0 0 ]\n// expect: [ 0 0 3 0 0 ]\n// expect: [ 0 0 0 0 1 ]\n// expect: [ 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 ]\n\nprint a.dimensions()\n// expect: [ 5, 5 ]\n\nvar b=a*a\nprint b.dimensions()\n// expect: [ 5, 5 ]\n"
  },
  {
    "path": "test/sparse/edit_sparse.morpho",
    "content": "\nvar N = 4\nvar a = Sparse(N,N)\n\nfor (i in 0...N) a[i,i]=i+1\n\nvar b = a*a \n\nprint b \n// expect: [ 1 0 0 0 ]\n// expect: [ 0 4 0 0 ]\n// expect: [ 0 0 9 0 ]\n// expect: [ 0 0 0 16 ]\n\nb[0,N-1] = 1\nb[N-1,0] = 1\n\nprint b \n// expect: [ 1 0 0 1 ]\n// expect: [ 0 4 0 0 ]\n// expect: [ 0 0 9 0 ]\n// expect: [ 1 0 0 16 ]\n\nprint b*b\n// expect: [ 2 0 0 17 ]\n// expect: [ 0 16 0 0 ]\n// expect: [ 0 0 81 0 ]\n// expect: [ 17 0 0 257 ]"
  },
  {
    "path": "test/sparse/empty_initializer.morpho",
    "content": "// Sparse with no initializer\n\nvar a = Sparse() \na[0,0] = 1\na[1,1] = 1\n\nprint a \n// expect: [ 1 0 ]\n// expect: [ 0 1 ]"
  },
  {
    "path": "test/sparse/enumerate.morpho",
    "content": "// Enumerate values in a sparse object\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\nvar b = Sparse([[0,1,1],[1,0,1],[2,3,1],[3,2,1]])\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nvar sum=0\nfor (x in a) sum+=x\n\nprint sum\n// expect: 2\n\nfor (x in a*a) print x\n// expect: 1\n// expect: 2\n// expect: -2\n// expect: -2\n// expect: 2\n// expect: 1\n"
  },
  {
    "path": "test/sparse/incompatible_add.morpho",
    "content": "// Incompatible dimensions\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\nvar b = Sparse([[0,1,1],[1,0,1]])\n\nprint a+b\n// expect Error: 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/sparse/incompatible_mul.morpho",
    "content": "// Incompatible dimensions\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\nvar b = Sparse([[0,1,1],[1,0,1]])\n\nprint a*b\n// expect Error: 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/sparse/indices.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\n\nfor (i in a.indices()) {\n  print i\n}\n// expect: [ 3, 3 ]\n// expect: [ 2, 2 ]\n// expect: [ 2, 1 ]\n// expect: [ 1, 2 ]\n// expect: [ 1, 1 ]\n// expect: [ 0, 0 ]\n"
  },
  {
    "path": "test/sparse/inherited.morpho",
    "content": "// Test Matrix methods inherited from Object \n\nvar err = Sparse([[1,2,3]])\n\nprint err.respondsto(\"respondsto\") // expect: true\n\nprint islist(err.respondsto()) // expect: true\n\nprint err.clss() // expect: @Sparse\n\nprint err.superclass() // expect: @Object\n\nprint islist(err.invoke(\"respondsto\")) // expect: true\n\nprint err.has(\"a\") // expect: false\n\n"
  },
  {
    "path": "test/sparse/initializer.morpho",
    "content": "// Initialize different matrices\n\nvar b = Matrix(2,2)\nb[1,1]=4\nprint b[0,1]\n// expect: 0\nprint b[1,1]\n// expect: 4\nprint b\n// expect: [ 0 0 ]\n// expect: [ 0 4 ]\n\nvar N = 4\nvar a = Sparse(N,N)\nfor(var i=0; i<N; i=i+1) a[i,i]=1\n\nprint a[2,2]\n// expect: 1\nprint a[3,2]\n// expect: 0\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 0 0 ]\n// expect: [ 0 0 1 0 ]\n// expect: [ 0 0 0 1 ]\n\n// Array initializer\nvar c = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\nprint c\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\nprint c[1,2]\n// expect: -1\n"
  },
  {
    "path": "test/sparse/invld_initializer.morpho",
    "content": "// Initialize different matrices\n\nvar b = Sparse([[\"Blob\",\"Bloop\", 1]])\n// expect Error: 'SprsInvldInit'\n"
  },
  {
    "path": "test/sparse/linearsolve.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse([[0,0,1],[1,1,1],[2,2,1],[3,3,1],[0,1,0.5],[1,0,0.5],[2,3,0.5],[3,2,0.5],[1,2,0.1],[2,1,0.1]])\nvar c = Matrix([1,2,3,4])\n\nprint a\n// expect: [ 1 0.5 0 0 ]\n// expect: [ 0.5 1 0.1 0 ]\n// expect: [ 0 0.1 1 0.5 ]\n// expect: [ 0 0 0.5 1 ]\n\nprint c\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n// expect: [ 4 ]\n\nprint c/a\n// expect: [ 0.0723982 ]\n// expect: [ 1.8552 ]\n// expect: [ 1.08597 ]\n// expect: [ 3.45701 ]\n"
  },
  {
    "path": "test/sparse/nonnum.morpho",
    "content": "// Sparse matrices can store non-numerical values\n\nvar a = Sparse([[0,1,\"Blob\"], [1,1,\"Bloop\"]])\nprint a\n// expect: [ 0 Blob ]\n// expect: [ 0 Bloop ]\n"
  },
  {
    "path": "test/sparse/row_indices.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nvar dim = a.dimensions()\n\nfor (i in 0...dim[1]) {\n  print a.rowindices(i)\n}\n// expect: [ 0 ]\n// expect: [ 1, 2 ]\n// expect: [ 1, 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/sparse/scalar_mul.morpho",
    "content": "// Scalar multiplication\n\nvar a = Sparse([[0,1,1], [1,1,1], [1,0,2]])\nprint a\n// expect: [ 0 1 ]\n// expect: [ 2 1 ]\n\nprint a*2\n// expect: [ 0 2 ]\n// expect: [ 4 2 ]\n\nprint 3*a \n// expect: [ 0 3 ]\n// expect: [ 6 3 ]"
  },
  {
    "path": "test/sparse/set_row_indices.morpho",
    "content": "// Manipulate row indices\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]])\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -1 0 ]\n// expect: [ 0 -1 1 0 ]\n// expect: [ 0 0 0 1 ]\n\na.setrowindices(0, [3])\na.setrowindices(1, [2,3])\na.setrowindices(2, [0,1])\na.setrowindices(3, [0])\n\nprint a\n// expect: [ 0 0 -1 1 ]\n// expect: [ 0 0 1 0 ]\n// expect: [ 0 1 0 0 ]\n// expect: [ 1 -1 0 0 ]\n\na.setrowindices(3, [1,1])\n// expect error 'MtrxIncmptbl'\n"
  },
  {
    "path": "test/sparse/sparse_dense_mul.morpho",
    "content": "var A = Sparse(4,4) \nfor (i in 0...4) A[i,i]=2\n\nvar B = Matrix([[1,2,3,4],[5,6,7,8]])\nprint B \n// expect: [ 1 2 3 4 ]\n// expect: [ 5 6 7 8 ]\n\nprint A*B.transpose() \n// expect: [ 2 10 ]\n// expect: [ 4 12 ]\n// expect: [ 6 14 ]\n// expect: [ 8 16 ]\n\nprint A*B \n// expect error 'MtrxIncmptbl'"
  },
  {
    "path": "test/sparse/sparse_dense_mul_dimensions.morpho",
    "content": "// \n\nvar N = 3\n\nvar A = Sparse(2*N,N)\nfor (i in 0...N) {\n    A[i,i]=1\n    A[i+N,i]=1\n}\n\nvar b = Matrix(List(1..N))\n\nprint A.dimensions() // expect: [ 6, 3 ]\nprint b.dimensions() // expect: [ 3, 1 ]\n\nprint (A*b).dimensions() // expect: [ 6, 1 ]\n\nprint A*b\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]"
  },
  {
    "path": "test/sparse/sparse_empty.morpho",
    "content": "// Sparse with no initializer\n\nvar a = Sparse() \na[0,0] = 1\na[1,1] = 1\n\nprint a \n// expect: [ 1 0 ]\n// expect: [ 0 1 ]"
  },
  {
    "path": "test/sparse/sparse_empty_list.morpho",
    "content": "// Sparse with an empty list initializer\n\nvar a = Sparse([]) \na[0,0] = 1\na[1,1] = 1\n\nprint a \n// expect: [ 1 0 ]\n// expect: [ 0 1 ]"
  },
  {
    "path": "test/sparse/todensematrix.morpho",
    "content": "// Convert a Sparse matrix to a Dense Matrix \n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-2],[2,1,-3],[2,2,1],[3,3,1]])\nvar b = Sparse([[0,1,1],[1,0,2],[2,3,3],[3,2,4]])\n\nvar ad = Matrix(a) \nvar bd = Matrix(b) \n\nprint ad\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -2 0 ]\n// expect: [ 0 -3 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nprint bd\n// expect: [ 0 1 0 0 ]\n// expect: [ 2 0 0 0 ]\n// expect: [ 0 0 0 3 ]\n// expect: [ 0 0 4 0 ]\n\nvar aad = Matrix(a*a) \nprint aad\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 7 -4 0 ]\n// expect: [ 0 -6 7 0 ]\n// expect: [ 0 0 0 1 ]"
  },
  {
    "path": "test/sparse/transpose.morpho",
    "content": "// Sparse matrices\n\nvar a = Sparse([[0,0,1],[1,1,1],[1,2,-2],[2,1,-3],[2,2,1],[3,3,1]])\nvar b = Sparse([[0,1,1],[1,0,2],[2,3,3],[3,2,4]])\n\nprint a\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -2 0 ]\n// expect: [ 0 -3 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nprint b\n// expect: [ 0 1 0 0 ]\n// expect: [ 2 0 0 0 ]\n// expect: [ 0 0 0 3 ]\n// expect: [ 0 0 4 0 ]\n\nprint a.transpose()\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 1 -3 0 ]\n// expect: [ 0 -2 1 0 ]\n// expect: [ 0 0 0 1 ]\n\nprint b.transpose()\n// expect: [ 0 2 0 0 ]\n// expect: [ 1 0 0 0 ]\n// expect: [ 0 0 0 4 ]\n// expect: [ 0 0 3 0 ]\n"
  },
  {
    "path": "test/string/ctrl_in_string.morpho",
    "content": "// Test string literals with unescaped control character\n\nprint \"  \"\n// expect error 'UnescpdCtrl'\n"
  },
  {
    "path": "test/string/empty_interpolation.morpho",
    "content": "// Empty string interpolation\n\nprint \"${}x\"\n// expect: x\n\n"
  },
  {
    "path": "test/string/error_after_multiline.morpho",
    "content": "// Tests that we correctly track the line info across multiline strings.\nvar a = \"1\n2\n3\n\";\n\nerr; // expect error 'SymblUndf'\n"
  },
  {
    "path": "test/string/escape.morpho",
    "content": "// Test string literals with escaped characters\n\nprint \"Hello\\nGoodbye\"\n// expect: Hello\n// expect: Goodbye\n\nprint \"\\\"Hello\\\"\"\n// expect: \"Hello\"\n\n\nvar i = \"hello\"\nprint \"Hello\\n${i}Goodbye\"\n// expect: Hello\n// expect: helloGoodbye\n"
  },
  {
    "path": "test/string/escchar.morpho",
    "content": "// Test string literals with byte specifiers\n\nprint \"\\x09\"==\"\\t\"\n// expect: true\n\nprint \"\\x08\"==\"\\b\"\n// expect: true\n\nprint \"\\x0a\"==\"\\n\"\n// expect: true\n"
  },
  {
    "path": "test/string/index.morpho",
    "content": "var s = \"Hello\"\n\nprint s[0]\n// expect: H\nprint s[2]\n// expect: l\n\nprint s[20]\n// expect Error 'IndxBnds'\n"
  },
  {
    "path": "test/string/inherited.morpho",
    "content": "// Test String methods inherited from Object \n\nvar str = \"12\"\n\nprint str.respondsto(\"clss\") // expect: true\n\nprint islist(str.respondsto()) // expect: true\n\nprint str.clss() // expect: @String\n\nprint str.superclass() // expect: @Object\n\nprint islist(str.invoke(\"respondsto\")) // expect: true\n\nprint str.has(\"a\") // expect: false\n\nprint str.count() // expect: 2"
  },
  {
    "path": "test/string/interpolation.morpho",
    "content": "// String interpolation\n\nvar i = 1;\nprint \"Just ${i} interpolation here...\";\n// expect: Just 1 interpolation here...\n\nprint \"and ${2*(i+1)} here\";\n// expect: and 4 here\n\nvar drink = \"Tea\";\nvar steep = 4;\nvar cool = 2;\nprint \"${drink} will be ready in ${steep + cool} minutes.\";\n// expect: Tea will be ready in 6 minutes.\n\nclass Drink {\n\tinit(type) {\n\t\tself.type = type;\n\t}\n\n\tconsume() {\n\t\treturn self.type;\n\t}\n}\n\nvar beverage = Drink(\"coffee\");\n\nprint \"I'd like ${i} cup of ${beverage.type} please!\";\n// expect: I'd like 1 cup of coffee please!\n\nprint \"Another cup of ${beverage.consume()}, please!\";\n// expect: Another cup of coffee, please!\n\n{ print \"Check that ${ i } interpolation ${ i } closes properly\"; }\n// expect: Check that 1 interpolation 1 closes properly\n\nprint \"Nested ${\"interpolation?! Are you ${\"mad?!\"}\"}\";\n// expect: Nested interpolation?! Are you mad?!\n"
  },
  {
    "path": "test/string/interpolation_keyword.morpho",
    "content": "\nprint \"The keyword is ${while}\"\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/string/interpolation_propertyindex.morpho",
    "content": "\nclass Foo {\n}\n\nvar a = Foo()\na.list = [ 1, 2, 3]\n\nprint \"<<${a.list[1]}>>\"\n// expect: <<2>>\n"
  },
  {
    "path": "test/string/interpolation_syntaxerror.morpho",
    "content": "\nvar a = 1.21321321213\n\nprint \"${a:###sasdsad}\"\n// expect error 'IntrpIncmp'"
  },
  {
    "path": "test/string/interpolation_types.morpho",
    "content": "// Check interpolation types\n\nvar i = 1\nvar x = 0.5\nvar tr = true\nvar fl = false\nvar n = nil\n\nprint \"${i} ${x} ${tr} ${fl} ${n}\" // expect: 1 0.5 true false nil\n\nvar lst = [ 1, 2, 3 ]\nprint \"${lst}\" // expect: [ 1, 2, 3 ]\n\n// Functions \nfn fun() { return x }\nprint \"${fun}\" // expect: <fn fun>\n\n// Closures\nfn f(a) {\n  fn g() { return a }\n  return g\n}\n\nvar q = f(1)\n\nprint \"${q}\" // expect: <<fn g>>\n\n// Invocations\nvar q = Object().respondsto\nprint \"${q}\" // expect: <Object>.<fn respondsto>\n\n// Built in functions\nprint \"${cos}\" // expect: <fn cos>\n\nclass Klass {}\n\nprint \"${Klass}\" // expect: @Klass\n\nclass Chatty {\n  init(x) { self.name = x }\n  tostring() { return \"@#%!\"+self.name+\"*&^\" }\n}\n\nvar s = Chatty(\"hello\")\nprint \"${s}\" // expect: @#%!hello*&^\n"
  },
  {
    "path": "test/string/invalid_unicode.morpho",
    "content": "// Test string literals with invalid unicode specifier\n\nprint \"\\U00.F98B\"\n// expect error 'InvldUncd'\n"
  },
  {
    "path": "test/string/literals.morpho",
    "content": "// Test string literals\n\nprint \"(\" + \"\" + \")\"   // expect: ()\nprint \"a string\" // expect: a string\n\n// Non-ASCII.\nprint \"A~¶Þॐஃ\" // expect: A~¶Þॐஃ\n"
  },
  {
    "path": "test/string/multiline.morpho",
    "content": "var a = \"1\n2\n3\"\nprint a\n// expect: 1\n// expect: 2\n// expect: 3\n"
  },
  {
    "path": "test/string/split.morpho",
    "content": "// Splitting strings\n\nvar t = \"A~;¶Þॐ,ஃ\"\nprint t.split(\";,\")\n// expect: [ A~, ¶Þॐ, ஃ ]\n\nprint \"A~;¶Þॐ,ஃ;\".split(\";,\")\n// expect: [ A~, ¶Þॐ, ஃ,  ]\n\nprint \";;;\".split(\";\")\n// expect: [ , , ,  ]\n\nprint \"hello\".split(\";\")\n// expect: [ hello ]\n"
  },
  {
    "path": "test/string/split_gc.morpho",
    "content": "// Ensure garbage collector and string split are interacting properly\n\nvar str = \"a b c d f g\"\nvar w\n//var c\nfor (i in 1..10){\n    print str.split(\" \")\n}\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]\n// expect: [ a, b, c, d, f, g ]"
  },
  {
    "path": "test/string/string_veneer.morpho",
    "content": "// Test string veneer class\n\nvar a = \"Hello\"\nprint a\n// expect: Hello\n\nprint a.count()\n// expect: 5\n\nvar b = a.count\nprint b()\n// expect: 5\n\nprint b\n// expect: Hello.<fn count>\n"
  },
  {
    "path": "test/string/tonumber.morpho",
    "content": "// Convert strings to numbers\n\nvar a = \"123\"\nvar b = \"baa\"\n\nprint a.isnumber() \n// expect: true\n\nprint b.isnumber() \n// expect: false\n\nprint \"+1.23e-12\".isnumber() \n// expect: true\n\nprint Int(\"122\")+1\n// expect: 123\n\nprint Int(\"125x + 3 #@#@\")+1 \n// expect: 126\n\nprint Int(\"12.343\")+1 \n// expect: 13\n\nprint Float(\"1.1\")\n// expect: 1.1\n\nprint Float(\"-1.1\")\n// expect: -1.1\n\nprint Float(\"+1e-3\")\n// expect: 0.001\n\n"
  },
  {
    "path": "test/string/unicode.morpho",
    "content": "// Test string literals with unicode character\n\nprint \"\\U0001F98B\"\n// expect: 🦋\n"
  },
  {
    "path": "test/string/unterminated.morpho",
    "content": "// expect error 'UntrmStrng'\n\"this string has no close quote\n"
  },
  {
    "path": "test/string/utf8.morpho",
    "content": "// UTF8\n\nvar s = \"A~¶Þॐஃ\"\n\nprint s.count()\n// expect: 6\n\nfor (x in s) print x\n// expect: A\n// expect: ~\n// expect: ¶\n// expect: Þ\n// expect: ॐ\n// expect: ஃ\n"
  },
  {
    "path": "test/super/bound_method.morpho",
    "content": "class A {\n  method(arg) {\n    print \"A.method(\" + arg + \")\"\n  }\n}\n\nclass B is A {\n  getClosure() {\n    return super.method\n  }\n\n  method(arg) {\n    print \"B.method(\" + arg + \")\"\n  }\n}\n\n\nvar closure = B().getClosure()\nclosure(\"arg\") // expect: A.method(arg)\n"
  },
  {
    "path": "test/super/call_other_method.morpho",
    "content": "class Base {\n  foo() {\n    print \"Base.foo()\"\n  }\n}\n\nclass Derived is Base {\n  bar() {\n    print \"Derived.bar()\"\n    super.foo()\n  }\n}\n\nDerived().bar()\n// expect: Derived.bar()\n// expect: Base.foo()\n"
  },
  {
    "path": "test/super/call_same_method.morpho",
    "content": "class Base {\n  foo() {\n    print \"Base.foo()\"\n  }\n}\n\nclass Derived is Base {\n  foo() {\n    print \"Derived.foo()\"\n    super.foo()\n  }\n}\n\nDerived().foo()\n// expect: Derived.foo()\n// expect: Base.foo()\n"
  },
  {
    "path": "test/super/closure.morpho",
    "content": "class Base {\n  toString() { return \"Base\" }\n}\n\nclass Derived is Base {\n  getClosure() {\n    fn closure() {\n      return super.toString()\n    }\n    return closure\n  }\n\n  toString() { return \"Derived\" }\n}\n\nvar closure = Derived().getClosure()\nprint closure() // expect: Base\n"
  },
  {
    "path": "test/super/constructor.morpho",
    "content": "class Base {\n  init(a, b) {\n    print \"Base.init(\" + a + \", \" + b + \")\"\n  }\n}\n\nclass Derived is Base {\n  init() {\n    print \"Derived.init()\"\n    super.init(\"a\", \"b\")\n  }\n}\n\nDerived()\n// expect: Derived.init()\n// expect: Base.init(a, b)\n"
  },
  {
    "path": "test/super/extra_arguments.morpho",
    "content": "class Base {\n  foo(a, b) {\n    print \"Base.foo(\" + a + \", \" + b + \")\"\n  }\n}\n\nclass Derived is Base {\n  foo() {\n    print \"Derived.foo()\" // expect: Derived.foo()\n    super.foo(\"a\", \"b\", \"c\", \"d\") // expect error 'InvldArgs'\n  }\n}\n\nDerived().foo()\n"
  },
  {
    "path": "test/super/indirectly_inherited.morpho",
    "content": "class A {\n  foo() {\n    print \"A.foo()\"\n  }\n}\n\nclass B is A {}\n\nclass C is B {\n  foo() {\n    print \"C.foo()\"\n    super.foo()\n  }\n}\n\nC().foo()\n// expect: C.foo()\n// expect: A.foo()\n"
  },
  {
    "path": "test/super/missing_arguments.morpho",
    "content": "class Base {\n  foo(a, b) {\n    print \"Base.foo(\" + a + \", \" + b + \")\"\n  }\n}\n\nclass Derived is Base {\n  foo() {\n    super.foo(1) // expect error 'InvldArgs'\n  }\n}\n\nDerived().foo()\n"
  },
  {
    "path": "test/super/no_superclass_bind.morpho",
    "content": "class Base {\n  foo() {\n    super.doesNotExist // expect error 'ClssLcksMthd'\n  }\n}\n\nBase().foo()\n"
  },
  {
    "path": "test/super/no_superclass_call.morpho",
    "content": "class Base {\n  foo() {\n    super.doesNotExist(1) // expect error 'ClssLcksMthd'\n  }\n}\n\nBase().foo();\n"
  },
  {
    "path": "test/super/no_superclass_method.morpho",
    "content": "class Base {}\n\nclass Derived is Base {\n  foo() {\n    super.doesNotExist(1) // expect error 'ClssLcksMthd'\n  }\n}\n\nDerived().foo();\n"
  },
  {
    "path": "test/super/parenthesized.morpho",
    "content": "class A {\n  method() {}\n}\n\nclass B < A {\n  method() {\n    // expect error 'ExpctDtSpr'\n    (super).method()\n  }\n}\n"
  },
  {
    "path": "test/super/reassign_superclass.morpho",
    "content": "class Base {\n  method() {\n    print \"Base.method()\"\n  }\n}\n\nclass Derived < Base {\n  method() {\n    super.method()\n  }\n}\n\nclass OtherBase {\n  method() {\n    print \"OtherBase.method()\"\n  }\n}\n\nvar derived = Derived()\nderived.method() // expect: Base.method()\nBase = OtherBase\nderived.method() // expect: Base.method()\n"
  },
  {
    "path": "test/super/self_in_superclass_method.morpho",
    "content": "class Base {\n  init(a) {\n    self.a = a\n  }\n}\n\nclass Derived < Base {\n  init(a, b) {\n    super.init(a)\n    self.b = b\n  }\n}\n\nvar derived = Derived(\"a\", \"b\")\nprint derived.a // expect: a\nprint derived.b // expect: b\n"
  },
  {
    "path": "test/super/super_at_top_level.morpho",
    "content": "super.foo // expect error 'SprOtsdClss'\n"
  },
  {
    "path": "test/super/super_in_closure_in_inherited_method.morpho",
    "content": "class A {\n  say() {\n    print \"A\"\n  }\n}\n\nclass B < A {\n  getClosure() {\n    fn closure() {\n      super.say()\n    }\n    return closure\n  }\n\n  say() {\n    print \"B\"\n  }\n}\n\nclass C < B {\n  say() {\n    print \"C\"\n  }\n}\n\nC().getClosure()() // expect: A\n"
  },
  {
    "path": "test/super/super_in_inherited_method.morpho",
    "content": "class A {\n  say() {\n    print \"A\"\n  }\n}\n\nclass B < A {\n  test() {\n    super.say()\n  }\n\n  say() {\n    print \"B\"\n  }\n}\n\nclass C < B {\n  say() {\n    print \"C\"\n  }\n}\n\nC().test() // expect: A\n"
  },
  {
    "path": "test/super/super_in_top_level_function.morpho",
    "content": "fn foo() {\n  super.bar() // expect error 'SprOtsdClss'\n}\n"
  },
  {
    "path": "test/super/super_without_dot.morpho",
    "content": "class A {}\n\nclass B < A {\n  method() {\n    // expect error 'ExpctDtSpr'\n    super\n  }\n}\n"
  },
  {
    "path": "test/super/super_without_name.morpho",
    "content": "class A {}\n\nclass B < A {\n  method() {\n    super. // expect error 'ExpExpr'\n  }\n}\n"
  },
  {
    "path": "test/syntax/comments.morpho",
    "content": "/* **********************************\n * morpho test suite\n * comments.morpho\n * Test comments\n * ********************************** */\n\nprint 1 // print 2\n// expect: 1\n\nprint // 1\n2\n// expect: 2\n\n/* print 3 */\n\n/* Nested /* comment */ */\n"
  },
  {
    "path": "test/syntax/empty_file.morpho",
    "content": ""
  },
  {
    "path": "test/syntax/illegal_chars_in_symbol.morpho",
    "content": "/* ********************************************************************\n * morpho test suite\n * illegal_chars_in_symbol.morpho\n * Check that an err. is raised if symbols contain illegal chars.\n * ******************************************************************** */\n\nvar @notasymbol = 4 // expect error 'VarExpct'\nprint @notasymbol\n"
  },
  {
    "path": "test/syntax/symbols.morpho",
    "content": "/* **********************************\n * morpho test suite\n * symbols.morpho\n * Test symbols\n * ********************************** */\n\nvar _symbol = 2\nprint _symbol // expect: 2\n\nvar Breakfast = \"Eggs\"\nvar breakfast = \"Bacon\"\nprint Breakfast // expect: Eggs\n\nclass My_class {}\nvar c = My_class()\nprint c // expect: <My_class>\n\nvar sym123 = 6\nprint sym123 // expect: 6\n"
  },
  {
    "path": "test/system/args.morpho",
    "content": "// Gets the arguments and prints them \n\nsystem(\"morpho6 system/args.xmorpho\")\n// expect: [  ]\n\nsystem(\"morpho6 system/args.xmorpho hello world\")\n// expect: [ hello, world ]\n"
  },
  {
    "path": "test/system/args.xmorpho",
    "content": "// Gets the arguments and prints them \n\nvar args = System.arguments() \n\nprint args "
  },
  {
    "path": "test/system/system.morpho",
    "content": "// Test System class\n\nprint isstring(System.platform())\n// expect: true\n\nprint isstring(System.version())\n// expect: true\n\nprint isnumber(System.clock())\n// expect: true\n\nSystem.exit() \n\nprint \"We never get here\""
  },
  {
    "path": "test/test.py",
    "content": "#!/usr/bin/env python3\n# Simple automated testing\n# T J Atherton Sept 2020\n#\n# Each input file is supplied to a test command, the results\n# are piped to a file and the output is compared with expectations\n# extracted from the input file.\n# Expectations are coded into comments in the input file as follows:\n\n# import necessary modules\nimport os, glob, sys\nimport regex as rx\nfrom functools import reduce\nimport operator\nimport colored\nfrom colored import stylize\n\n# define what command to use to invoke the interpreter\ncommand = 'morpho6'\n\n# define the file extension to test\next = 'morpho'\n\n# We reduce any errors to this value\nerr = '@error'\n\n# We reduce any stacktrace lines to this values\nstk = '@stacktrace'\n\n# Removes control characters\ndef remove_control_characters(str):\n    return rx.sub(r'\\x1b[^m]*m', '', str.rstrip())\n\n# Simplify error reports\ndef simplify_errors(str):\n    # this monster regex extraxts NAME from error messages of the form error ... 'NAME'\n    return rx.sub('.*[E|e]rror[ :]*\\'([A-z;a-z]*)\\'.*', err+'[\\\\1]', str.rstrip())\n\n# Simplify stacktrace\ndef simplify_stacktrace(str):\n    return rx.sub(r'.*at line.*', stk, str.rstrip())\n\n# Find an expected value\ndef findvalue(str):\n    return rx.findall(r'// expect: ?(.*)', str)\n\n# Find an expected error\ndef finderror(str):\n    #return rx.findall(r'\\/\\/ expect ?(.*) error', str)\n    return rx.findall(r'.*[E|e]rror[ :].*?(.*)', str)\n\n# Find an expected error\ndef iserror(str):\n    #return rx.findall(r'\\/\\/ expect ?(.*) error', str)\n    test=rx.findall(r'@error.*', str)\n    return len(test)>0\n\n# Find an expected error\ndef isin(str):\n    #return rx.findall(r'\\/\\/ expect ?(.*) error', str)\n    test=rx.findall(r'.*in .*', str)\n    return len(test)>0\n\n# Remove elements from a list\ndef remove(list, remove_list):\n    test_list = list\n    for i in remove_list:\n        try:\n            test_list.remove(i)\n        except ValueError:\n            pass\n    return test_list\n\n# Find what is expected\ndef findexpected(str):\n    out = finderror(str) # is it an error?\n    if (out!=[]):\n        out = [simplify_errors(str)] # if so, simplify it\n    else:\n        out = findvalue(str) # or something else?\n    return out\n\n# Works out what we expect from the input file\ndef getexpect(filepath):\n    # Load the file\n    file_object = open(filepath, 'r', encoding=\"utf8\")\n    lines = file_object.readlines()\n    file_object.close()\n    #Find any expected values over all lines\n    if (lines != []):\n        out = list(map(findexpected, lines))\n        out = reduce(operator.concat, out)\n    else:\n        out = []\n    return out\n\n# Gets the output generated\ndef getoutput(filepath):\n    # Load the file\n    file_object = open(filepath, 'r', encoding=\"utf8\")\n    lines = file_object.readlines()\n    file_object.close()\n    # remove all control characters\n    lines = list(map(remove_control_characters, lines))\n    # Convert errors to our universal error code\n    lines = list(map(simplify_errors, lines))\n    # Identify stack trace lines\n    lines = list(map(simplify_stacktrace, lines))\n    for i in range(len(lines)-1):\n        if (iserror(lines[i])):\n            if (isin(lines[i+1])):\n                lines[i+1]=stk\n    # and remove them\n    return list(filter(lambda x: x!=stk, lines))\n\n# Test a file\ndef test(file,testLog,CI):\n    ret = 0\n    if not CI:\n        print(file+\":\", end=\" \")\n\n    # Create a temporary file in the same directory\n    tmp = file + '.out'\n\n    #Get the expected output\n    expected=getexpect(file)\n\n    # Run the test\n    os.system(command + ' ' +file + ' > ' + tmp)\n\n    # If we produced output\n    if os.path.exists(tmp):\n        # Get the output\n        out=getoutput(tmp)\n\n        # Was it expected?\n        if(expected==out):\n            if not CI:\n                print(stylize(\"Passed\",colored.fg(\"green\")))\n            ret = 1\n        else:\n            if not CI:\n                print(stylize(\"Failed\",colored.fg(\"red\")))\n                print(\"  Expected: \", expected)\n                print(\"    Output: \", out)\n            else:\n                print(\"\\n::error file = {\",file,\"}::{\",file,\" Failed}\")\n\n\n            #also print to the test log\n            print(file+\":\", end=\" \",file = testLog)\n            print(\"Failed\", file = testLog)\n\n            if len(out) == len(expected):\n                failedTests = list(i for i in range(len(out)) if expected[i] != out[i])\n                print(\"Tests \" + str(failedTests) + \" did not match expected results.\", file = testLog)\n                for testNum in failedTests:\n                    print(\"Test \"+str(testNum), file = testLog)\n                    print(\"  Expected: \", expected[testNum], file = testLog)\n                    print(\"    Output: \", out[testNum], file = testLog)\n            else:\n                print(\"  Expected: \", expected, file = testLog)\n                print(\"    Output: \", out, file = testLog)\n\n\n            print(\"\\n\",file = testLog)\n\n\n        # Delete the temporary file\n        os.remove(tmp)\n\n    return ret\n\nprint('--Begin testing---------------------')\n\n# open a test log\n# write failures to log\nsuccess=0 # number of successful tests\ntotal=0   # total number of tests\n\n# look for a command line arguement that says\n# this is being run for continous integration\nCI = False\n# Also look for a command line argument that says this is being run with multiple threads\nMT = False\nfor arg in sys.argv:\n    if arg == '-c': # if the argument is -c, then we are running in CI mode\n        CI = True\n    if arg == '-m': # if the argument is -m, then we are running in multi-thread mode\n        MT = True\n\nfailedTestsFileName = \"FailedTests.txt\"\nif MT:\n    failedTestsFileName = \"FailedTestsMultiThreaded.txt\"\n    command += \" -w4\" \n    print(\"Running tests with 4 threads\")\n\nfiles=glob.glob('**/**.'+ext, recursive=True)\nwith open(failedTestsFileName,'w', encoding=\"utf8\") as testLog:\n\n    for f in files:\n        # print(f)\n        success+=test(f,testLog,CI)\n        total+=1\n\n# if (not CI) and (not success == total):\n#     os.system(\"emacs FailedTests.txt &\")\n\nprint('--End testing-----------------------')\nprint(success, 'out of', total, 'tests passed.')\nif CI and success<total:\n    exit(-1)\n"
  },
  {
    "path": "test/try/break_in_catch.morpho",
    "content": "// Continue in catch block\n\nfor (_ in 1..3) {\n  try {\n    Error(\"Err\", \"Error\").throw() \n  } catch {\n    \"Err\": \n      break \n  }\n  print \"yes\"\n}\n// expect: yes\n// expect: yes\n// expect: yes\n\n\nprint \"ok\"\n// expect: ok"
  },
  {
    "path": "test/try/continue_in_catch.morpho",
    "content": "// Continue in catch block\n\nfor (_ in 1..10) {\n  try {\n    Error(\"Err\", \"Error\").throw() \n  } catch {\n    \"Err\": \n      continue \n  }\n  print \"Nope\"\n}\n\nprint \"ok\"\n// expect: ok"
  },
  {
    "path": "test/try/err_stack_overflw.morpho",
    "content": "\nfn func() {\n  try {\n    return func() \n  } catch {\n  }\n  return false \n}\n\nfunc() \n// expect error 'ErrStckOvflw'"
  },
  {
    "path": "test/try/return_in_try.morpho",
    "content": "\nfn func() {\n  try {\n    return true\n  } catch {\n  }\n  return false \n}\n\nfor (i in 1..100) {\n  func()\n}\n\nprint \"ok\"\n// expect: ok"
  },
  {
    "path": "test/try/try.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try.morpho\n * Test try/catch statement\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\ntry {\n  print \"No err here!\" // expect: No err here!\n} catch {\n  \"Foo\" :\n    print \"Caught the hoo\"\n}\nprint \"landed\" // expect: landed\n\n\ntry {\n  err.throw()\n} catch {\n  \"Foo\" :\n    print \"Caught the foo\" // expect: Caught the foo\n    print \"(boohoo)\" // expect: (boohoo)\n  \"Bar\" :\n    print \"Caught the bar\"\n}\n\n// Now for an uncaught err\nerr.throw()\n// expect error 'Foo'\n"
  },
  {
    "path": "test/try/try_builtin_err.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_builtin_err.morpho\n * Test try/catch statement with builtin error\n * ********************************** */\n\nvar obj = Object()\n\ntry {\n  print obj.foo\n} catch {\n  \"ObjLcksPrp\":\n    print \"No foo\" // expect: No foo\n}\n"
  },
  {
    "path": "test/try/try_empty_block.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try.morpho\n * Empty block\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\ntry {\n\n} catch {\n  \"Foo\" : print \"Caught!\"\n}\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/try/try_empty_catch.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try.morpho\n * Empty catch block\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\ntry {\n  print \"foo\" // expect: foo\n} catch {\n\n}\n"
  },
  {
    "path": "test/try/try_empty_label.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_empty_label.morpho\n * Test empty labels in catch \n * ********************************** */\n\nvar err = Error(\"Foo\", \"foo\")\n\ntry {\n  err.throw()\n} catch {\n  \"Foo\":\n}\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/try/try_err_in_function.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_err_in_function.morpho\n * Throw the err in a called function\n * ********************************** */\n\nfn foo() {\n  Error(\"Foo\", \"foo\").throw()\n}\n\ntry {\n  foo()\n} catch {\n  \"Foo\": print \"Caught\" // expect: Caught\n}\n"
  },
  {
    "path": "test/try/try_in_function.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_in_function.morpho\n * Test try/catch in a function\n * ********************************** */\n\nvar err = Error(\"Boo\", \"boo\")\nvar er2 = Error(\"Hoo\", \"hoo\")\nvar obj = Object()\n\nfn f(o) {\n  try {\n    print o.foo\n\n    err.throw()\n  } catch {\n    \"ObjLcksPrp\":\n      print \"No foo\"\n    \"Boo\":\n      print \"No boo\"\n      er2.throw()\n  }\n}\n\nf(obj) // expect: No foo\n\nobj.foo = true\n\nf(obj)\n// expect: true\n// expect: No boo\n// expect error 'Hoo'\n"
  },
  {
    "path": "test/try/try_in_loop.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_in_function.morpho\n * Test try/catch in a loop\n * ********************************** */\n\nvar err = Error(\"Foo\", \"foo\")\n\nfor (i in 1..5) {\n  try {\n    err.throw()\n  } catch {\n    \"Foo\": print \"Catching ${i}\"\n  }\n}\n\n// expect: Catching 1\n// expect: Catching 2\n// expect: Catching 3\n// expect: Catching 4\n// expect: Catching 5\n"
  },
  {
    "path": "test/try/try_in_method.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_in_method.morpho\n * Test try/catch in a method\n * ********************************** */\n\nvar err = Error(\"Boo\", \"boo\")\nvar er2 = Error(\"Hoo\", \"hoo\")\nvar obj = Object()\n\nclass Bar {\n  f(o) {\n    try {\n      print o.foo\n\n      err.throw()\n    } catch {\n      \"ObjLcksPrp\":\n        print \"No foo\"\n      \"Boo\":\n        print \"No boo\"\n        er2.throw()\n    }\n  }\n}\n\nvar o = Bar()\n\no.f(obj) // expect: No foo\n\nobj.foo = true\n\no.f(obj)\n// expect: true\n// expect: No boo\n// expect error 'Hoo'\n"
  },
  {
    "path": "test/try/try_missing_catch.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try.morpho\n * Missing catch statement\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\ntry {\n  err.throw()\n}\n\n// expect error 'ExpctCtch'\n"
  },
  {
    "path": "test/try/try_missing_catch_block.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try.morpho\n * Catch statement\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\ntry {\n  err.throw()\n} catch\n\nprint \"hi\"\n\n// expect error 'ExpctHndlr'\n"
  },
  {
    "path": "test/try/try_nested.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try.morpho\n * Nested try/catch\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\nvar err2 = Error(\"Hoo\", \"A Hoo occurred\")\n\ntry {\n  try {\n    err.throw()\n  } catch {\n    \"Foo\" :\n      print \"caught in inner\"\n      err2.throw()\n  }\n} catch {\n  \"Foo\" : print \"caught in outer\"\n  \"Hoo\" : print \"also caught in outer\"\n}\n\n// expect: caught in inner\n// expect: also caught in outer\n"
  },
  {
    "path": "test/try/try_recurse.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_recurse.morpho\n * try/catch from deep in a recursion\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\nfn rec(x) {\n  if (x==0) err.throw()\n\n  rec(x-1)\n}\n\ntry {\n  rec(50)\n} catch {\n  \"Foo\": print \"Caught\"\n}\n\n// expect: Caught\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/try/try_reentrancy.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_reentrancy.morpho\n * try/catch from within renentered vm\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\n\nfn bar (x) {\n  err.throw()\n}\n\nfor (i in 1..4) {\n  try {\n    apply(bar, nil)\n  } catch {\n    \"Foo\": print \"Caught\"\n  }\n}\n\n// expect: Caught\n// expect: Caught\n// expect: Caught\n// expect: Caught\n"
  },
  {
    "path": "test/try/try_reentrancy_uncaught.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_reentrancy.morpho\n * try/catch from within renentered vm\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\nvar err2 = Error(\"Boo\", \"A Foo occurred\")\n\nfn bar (x) {\n  err2.throw()\n}\n\nfor (i in 1..4) {\n  try {\n    apply(bar, nil)\n  } catch {\n    \"Foo\": print \"Caught\"\n  }\n}\n\n// expect error 'Boo'\n"
  },
  {
    "path": "test/try/try_var_in_catch.morpho",
    "content": "/* **********************************\n * morpho test suite\n * try_var_in_catch.morpho\n * Empty catch block\n * ********************************** */\n\nvar err = Error(\"Foo\", \"A Foo occurred\")\nvar str = \"Foo\"\ntry {\n  err.throw()\n} catch {\n  str:\n    print \"Caught the foo\"\n}\n// expect error 'InvldLbl'\n\n"
  },
  {
    "path": "test/tuple/nested_tuple.morpho",
    "content": "// Nested tuples \n\nvar a = ( ( 1, 2 ), ( 3, 4 ) )\n\nfor (u in a) {\n    print \"${u[0]},${u[1]}\"\n}\n// expect: 1,2\n// expect: 3,4\n"
  },
  {
    "path": "test/tuple/single_nested_tuple.morpho",
    "content": "// Nested tuples \n\nvar a = ( ( 1, 2 ) )\n\nfor (u in a) {\n    print \"${u[0]},${u[1]}\"\n}\n// expect: 1,2\n"
  },
  {
    "path": "test/tuple/tuple.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple.morpho\n * Test tuple creation\n * ********************************** */\n\nvar t = Tuple(1,2,3)\n\nprint t\n// expect: (1, 2, 3)\n\nprint t[0]\n// expect: 1\n\nprint t.clss()\n// expect: @Tuple\n\nt[0]=5\n// expect error 'ObjImmutable'"
  },
  {
    "path": "test/tuple/tuple_anonymousfn.morpho",
    "content": "// Construct a tuple of anonymous functions\n\nvar a = (fn (x) x, fn (x) x^2, fn (x) x^3, fn (x) x^4)\n\nfor (f in a) print f(2)\n// expect: 2\n// expect: 4\n// expect: 8\n// expect: 16\n"
  },
  {
    "path": "test/tuple/tuple_apply.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_apply.morpho\n * Apply with tuples\n * ********************************** */\n\nfn f(x, y, z) {\n    print x\n    print y\n    print z\n}\n\nvar a = (1,2,3)\nprint a\n// expect: (1, 2, 3)\n\napply(f, a)\n// expect: 1\n// expect: 2\n// expect: 3\n"
  },
  {
    "path": "test/tuple/tuple_compare.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_compare.morpho\n * Compare tuples\n * ********************************** */\n\nvar a = Tuple(1,2,3)\nvar b = Tuple(1,2,3)\nvar c = Tuple(1,2,3,4) // Different length\nvar d = Tuple(2,2,3) // Different contents\n\nprint a==b // expect: true\nprint a==c // expect: false\nprint a==d // expect: false"
  },
  {
    "path": "test/tuple/tuple_enumerate.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_index_out_of_bounds.morpho\n * Test bounds checking in tuples\n * ********************************** */\n\nvar t = Tuple(1,2,3)\n\nfor (i in t) print i\n// expect: 1\n// expect: 2\n// expect: 3\n"
  },
  {
    "path": "test/tuple/tuple_getindex.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_join.morpho\n * Test tuple indexing\n * ********************************** */\n\nvar t = Tuple(1,2,3)\n\nprint t[0]\n// expect: 1\n\nprint t[1]\n// expect: 2\n\nprint t[2]\n// expect: 3\n\nprint t[-1]\n// expect: 3\n\nprint t[-2]\n// expect: 2\n\nprint t[-3]\n// expect: 1\n\ntry {\n    print t[3]\n} catch {\n    \"IndxBnds\": print \"success\"\n}\n// expect: success\n\ntry {\n    print t[-4]\n} catch {\n    \"IndxBnds\": print \"success\"\n}\n// expect: success"
  },
  {
    "path": "test/tuple/tuple_in_dictionary.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_in_dictionary.morpho\n * Check that tuples work in dictionaries\n * ********************************** */\n\nvar a = Tuple(1,2,3)\nvar b = Tuple(1,2,3)\nvar c = Tuple(2,2,3)\n\nvar dict = {} \n\ndict[a]=1\n\nprint dict[a] // expect: 1\n\nprint dict[b] // expect: 1\n\nprint dict[c] // expect error 'DctKyNtFnd'"
  },
  {
    "path": "test/tuple/tuple_index_out_of_bounds.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_index_out_of_bounds.morpho\n * Test bounds checking in tuples\n * ********************************** */\n\nvar t = Tuple(1,2,3)\n\nprint t[3]\n// expect error 'IndxBnds'\n"
  },
  {
    "path": "test/tuple/tuple_ismember.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_ismember.morpho\n * Test ismember method\n * ********************************** */\n\nvar t = Tuple(1,2,3,4,5)\n\nprint t.ismember(4)\n// expect: true\n\nprint t.ismember(8)\n// expect: false"
  },
  {
    "path": "test/tuple/tuple_join.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_join.morpho\n * Test tuple joining\n * ********************************** */\n\nvar t = Tuple(1,2,3)\n\nprint t.join(t)\n// expect: (1, 2, 3, 1, 2, 3)\n"
  },
  {
    "path": "test/tuple/tuple_slice.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_join.morpho\n * Test tuple slicing\n * ********************************** */\n\nvar t = Tuple(1,2,3,4,5,6,7,8,9)\n\nprint t[0..2]\n// expect: (1, 2, 3)\n\nprint t[5..8]\n// expect: (6, 7, 8, 9)\n\nprint t[0..10]\n// expect error 'IndxBnds'\n"
  },
  {
    "path": "test/tuple/tuple_syntax.morpho",
    "content": "/* **********************************\n * morpho test suite\n * tuple_syntax.morpho\n * Tuple syntax\n * ********************************** */\n\nvar a = (1) \nprint a\n// expect: 1\n\nprint (1,) // Python-like syntax works\n// expect: (1)\n\nvar b = (1,2) \nprint b\n// expect: (1, 2)\n\nvar c = (1,2,3)\nprint c\n// expect: (1, 2, 3)\n\nprint (1,2,3)[-2]\n// expect: 2\n\nprint (1,2,3).join((4,5,6))\n// expect: (1, 2, 3, 4, 5, 6)\n"
  },
  {
    "path": "test/type/iscallable.morpho",
    "content": "// Check whether iscallable works correctly\n\nclass Foo {\n  bar() { }\n}\n\nvar a[2,2]\n\nvar f = Foo()\n\nfn g(x) { return x }\n\nfn h(t) {\n  fn q() { return t }\n  return q\n}\n\nvar s = h(1)\n\nvar vals = [  f, // expect: false\n              g, // expect: true\n              f.bar, // expect: true\n              9, // expect: false\n              9.0, // expect: false\n              \"Hi\", // expect: false\n              a, // expect: false\n              sin, // expect: true\n              s, // expect: true\n              h(2) // expect: true\n              ]\n\nfor (x in vals) {\n  print iscallable(x)\n}\n"
  },
  {
    "path": "test/type/typecheck.morpho",
    "content": "// Check that type checking functions work correctly\n\nclass Foo {\n  init(x) { self.bar = x }\n}\n\nvar a[2,2]\n\nvar vals = [  nil, 1, 0.2, true, false, Object(), \"Hi\",\n              Foo, 1..2, { \"a\": 1, \"b\": 2}, [], a,\n              Matrix(2,2), Sparse([[1,1,1]]) ]\nvar check = [ isnil, isint, isfloat, isbool, isbool, isobject, isstring,\n              isclass, isrange, isdictionary, islist, isarray,\n              ismatrix, issparse ]\n\nvar tst=Matrix(check.count(),vals.count())\n\nfor (i in 0...vals.count()) {\n  for (j in 0...vals.count()) {\n    if (apply(check[i], [].append(vals[j]))) tst[i,j]=1\n  }\n}\n\nprint tst\n// expect: [ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 1 0 0 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 1 1 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 1 1 0 0 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 1 1 1 1 1 1 1 1 1 ]\n// expect: [ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 ]\n// expect: [ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ]\n"
  },
  {
    "path": "test/types/builtin_class_signature.morpho",
    "content": "// Test multiple dispatch on builtin class\n\nvar lst = [4,3,2,1]\n\nlst.sort()\nprint lst  // expect: [ 1, 2, 3, 4 ]\n\nfn f (a, b) { return b-a }\nlst.sort(f)\nprint lst // expect: [ 4, 3, 2, 1 ]\n"
  },
  {
    "path": "test/types/function_signature.morpho",
    "content": "// Typed parameters in function signature\n\nfn f(String x, List y) {\n    print x\n}\n\nf(\"Hello\", []) \n// expect: Hello\n\nf([], [])\n// expect error 'MltplDsptchFld'\n"
  },
  {
    "path": "test/types/multiple_dispatch/apply.morpho",
    "content": "// Dispatch with apply\n\nfn f(String x) {\n    print x\n}\n\nfn f(Float x) {\n    print x+1\n}\n\napply(f, \"Hi\")\n// expect: Hi\n\napply(f, 1.5)\n// expect: 2.5\n"
  },
  {
    "path": "test/types/multiple_dispatch/apply_invocation_multiple_dispatch.morpho",
    "content": "class A {\n    a() { return 0 }\n    a(x) { return 1 }\n    a(x, y) { return 2 }\n    a(x, y, z) { return 3 }\n}\n\nvar a = A()\nvar b = a.a\n\nprint apply(b, [])       // expect: 0\nprint apply(b, [1])      // expect: 1\nprint apply(b, [1,1])    // expect: 2\nprint apply(b, [1,1,1])  // expect: 3\n"
  },
  {
    "path": "test/types/multiple_dispatch/check_args.morpho",
    "content": "// Arg checking\n\nfn f(Int x, Int y) {\n    return 0\n}\n\nfn f(Int x, Float y) {\n    return 1 \n}\n\nprint f(1,2) \n// expect: 0\n\nprint f(1,0.5) \n// expect: 1\n\nprint f(0.1,0.5)\n// expect error 'MltplDsptchFld'\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/class_multiple_dispatch.morpho",
    "content": "// Dipatch methods on multiple types\n\nclass A {\n    f(String x) {\n        print x\n    }\n\n    f(Float x) {\n        print x+1\n    }\n\n    f(List x) {\n        print x[-1]\n    }\n}\n\nvar a = A()\n\na.f(\"Hi\")\n// expect: Hi\n\nA.f(1.5)\n// expect: 2.5\n\nA.f([1,2])\n// expect: 2\n\nvar q = a.f\n\na.f(\"Souper\")\n// expect: Souper\n"
  },
  {
    "path": "test/types/multiple_dispatch/class_multiple_dispatch_args.morpho",
    "content": "// Dipatch methods on multiple types\n\nclass A {\n    f(Float x, Int y) {\n        print x + y \n    }\n\n    f(Float x, String y) {\n        print y\n    }\n\n    f(List x) {\n        print x[-1]\n    }\n}\n\nA.f([1]) // expect: 1\n\nA.f(1.2, 1) // expect: 2.2\n\nA.f(1.2, \"Hello\") // expect: Hello\n\nA.f(4) // expect error 'MltplDsptchFld'"
  },
  {
    "path": "test/types/multiple_dispatch/class_multiple_dispatch_post.morpho",
    "content": "// Dipatch methods on multiple types with a type defined after the class\n\nclass A {\n    f(String x) {\n        print x\n    }\n\n    f(Float x) {\n        print x+1\n    }\n\n    f(List x) {\n        print x[-1]\n    }\n\n    f(x) {\n        print \"Backup\"\n    }\n}\n\nclass B {\n\n}\n\nvar a = A()\n\na.f(B())\n// expect: Backup\n"
  },
  {
    "path": "test/types/multiple_dispatch/class_multiple_initializer.morpho",
    "content": "// Multiple dispatch in ititializer\n\nclass A {\n    init(problem) {\n        self.problem = problem\n    }\n}\n\nclass B is A {\n    init(problem, List target) {\n        super.init(problem)\n        self.target = target \n    }\n\n    init(x, String s) { self.init(x, [s]) }\n    init(x, Dictionary d) { self.init(x, [d]) }\n}\n\nvar b = B(\"\", \"Hello\")\n\nprint b.target\n// expect: [ Hello ]"
  },
  {
    "path": "test/types/multiple_dispatch/class_visitor.morpho",
    "content": "// Multiple dispatch version of the visitor pattern\n\nclass A { }\n\nclass Visitor {\n  init(...set) {\n    self.lst = []\n    for (s in set) self.lst.append(s)\n  }   \n\n  visit(A x) { return \"A\" }\n  visit(List x) { return \"List\" }\n  visit(String x) { return \"String\" }\n  visit(Int x) { return \"Int\" }\n  visit(Dictionary x) { return \"Dictionary\" }\n\n  process() {\n    for (l in self.lst) { print self.visit(l) }\n  }\n}\n\nvar a = Visitor(A(), \"Hello\", 1, {}, [1], A()) \n\na.process()\n// expect: A\n// expect: String\n// expect: Int\n// expect: Dictionary\n// expect: List\n// expect: A\n"
  },
  {
    "path": "test/types/multiple_dispatch/closure.morpho",
    "content": "// Multiple dispatch with closures\n\nfn f(a) {\n    fn g() { return a }\n    fn g(x) { return x + a }\n\n    return g\n}\n\nvar p = f(1)\n\nprint p() \n// expect: 1\n\nprint p(1) \n// expect: 2"
  },
  {
    "path": "test/types/multiple_dispatch/deep_nested_closure.morpho",
    "content": "// Multiple implementations with nested closures \n\nfn foo(List x) {\n    return x.count() \n}\n\nfn f(Int x) {\n    fn foo(Int y) {\n        return y + x \n    }\n\n    fn g() {\n        fn foo(Float y) {\n            return y + 2*x \n        }\n\n        return foo \n    }\n\n    return g\n}\n\nvar a = f(1) \nvar b = a()\n\nprint b(1) // expect: 2\nprint b(1.5) // expect: 3.5\nprint b([1,2,3]) // expect: 3\n"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate.morpho",
    "content": "// Duplicate implementations\n\nfn f(x, Int y) {\n    return 0\n}\n\nfn f(x, Int y) {\n    return 1 \n}\n\nprint f(1,1)\n// expect error 'MltplDisptchAmbg'\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate_no_arg.morpho",
    "content": "// Duplicate implementations with no arguments. \n\nfn f() {\n    return 0\n}\n\nfn f() {\n    return 1 \n}\n\nprint f()\n// expect error 'MltplDisptchAmbg'"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate_no_type.morpho",
    "content": "// Duplicate implementations with one argument no Type annotations.\n\nfn f(x) {\n    return 0\n}\n\nfn f(x) {\n    return 1 \n}\n\nprint f(1)\n// expect error 'MltplDisptchAmbg'\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate_no_type_two.morpho",
    "content": "// Duplicate implementations with one argument no Type annotations.\n\nfn f(x, y) {\n    return 0\n}\n\nfn f(x, y) {\n    return 1 \n}\n\nprint f(1)\n// expect error 'MltplDisptchAmbg'\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate_no_type_two_arguments.morpho",
    "content": "// Duplicate implementations with two arguments and no type annotations\n\nfn f(x, y) {\n    return 0\n}\n\nfn f(x, y) {\n    return 1 \n}\n\nprint f(1,1)\n// expect error 'MltplDisptchAmbg'\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate_one_arg.morpho",
    "content": "// Duplicate implementations with a single Typed argument. \n// This also segfaults similar to `duplicate_no_type.morpho`\n\nfn f(Int x) {\n    return 0\n}\n\nfn f(Int x) {\n    return 1 \n}\n\nprint f(1)\n// expect error 'MltplDisptchAmbg'\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/duplicate_scope.morpho",
    "content": "// Duplicate implementations separated by scope\n\nfn f(Int x, Int y) {\n    return 0\n}\n\n{\n    fn f(Int x, Int y) {\n        return 1 \n    }\n\n    print f(1,1) // expect: 1\n}\n"
  },
  {
    "path": "test/types/multiple_dispatch/free.morpho",
    "content": "// Dispatch a function with an implementation that catches any type\n\nfn f(Int x) {\n    return 0\n}\n\nfn f(Float x) {\n    return 1 \n}\n\nfn f(x) {\n    return -1\n}\n\nprint f(\"Hello\")\n// expect: -1\n\nprint f(true)\n// expect: -1\n\nprint f(1) \n// expect: 0\n\nprint f(0.1)\n// expect: 1\n\n"
  },
  {
    "path": "test/types/multiple_dispatch/import.morpho",
    "content": "// Dispatch a function defined in a module\n\nimport \"namespace.xmorpho\" \n\nf(\"Hi\")\n// expect: Hi\n\nf(1)\n// expect: Foo\n\nf(1.5)\n// expect: 2.5\n\nvar a = []\nf(a)\nprint a \n// expect: [ Ho ]\n\nf({})\n// expect: Foo\n"
  },
  {
    "path": "test/types/multiple_dispatch/import_for.morpho",
    "content": "// Import functions with 'for' keyword\n\nimport \"namespace.xmorpho\" for f \n\nf(\"Hi\") // Never gets to execute\n\ng()\n// expect error 'SymblUndf'"
  },
  {
    "path": "test/types/multiple_dispatch/in_function.morpho",
    "content": "// Multiple dispatch on locally defined functions\n\nfn g(x) {\n    fn f(String x) {\n        print x\n    }\n\n    fn f(Float x) {\n        print x+1\n    }\n\n    f(x)\n}\n\ng(\"Hi\")\n// expect: Hi\n\n\ng(1.5)\n// expect: 2.5\n"
  },
  {
    "path": "test/types/multiple_dispatch/inheritance.morpho",
    "content": "// Multiple dispatch with inheritance\n\nclass A {}\n\nclass B is A {} \n\nclass C is B {} \n\nclass D { }\n\nclass E is A with D { }\n\nclass F { }\n\nfn f(A x) { return \"A\" }\n\nfn f(B x) { return \"B\" }\n\nfn f(D x) { return \"D\" }\n\nprint f(A()) // expect: A\n\nprint f(B()) // expect: B\n\nprint f(C()) // expect: B\n\nprint f(E()) // expect: A\n\nprint f(D()) // expect: D\n\nprint f(F()) // expect error 'MltplDsptchFld'\n"
  },
  {
    "path": "test/types/multiple_dispatch/inheritance_single.morpho",
    "content": "// Single implementation with typecheck and inheritance\n\nclass A {}\n\nclass B is A {} \n\nclass C {} \n\nfn f(A x) { return \"A\" }\n\nprint f(A()) // expect: A\n\nprint f(B()) // expect: A\n\nprint f(C()) // expect error 'MltplDsptchFld'\n"
  },
  {
    "path": "test/types/multiple_dispatch/instance.morpho",
    "content": "// Dispatch a function on instance types\n\nclass A {}\nclass B {}\n\nfn f(A x) {\n    return \"A\"\n}\n\nfn f(B x) {\n    return \"B\"\n}\n\nprint f(A()) \n// expect: A\n\nprint f(B())\n// expect: B\n"
  },
  {
    "path": "test/types/multiple_dispatch/invocation_multiple_dispatch.morpho",
    "content": "class A {\n    a() { return 0 }\n    a(x) { return 1 }\n    a(x, y) { return 2 }\n    a(x, y, z) { return 3 }\n}\n\nvar a = A()\nvar b = a.a\n\nprint b()       // expect: 0\nprint b(1)      // expect: 1\nprint b(1,1)    // expect: 2\nprint b(1,1,1)  // expect: 3"
  },
  {
    "path": "test/types/multiple_dispatch/multi_nested_closure.morpho",
    "content": "// Nested closures \n\nfn g(String x) {\n    return x.count() \n}\n\nfn f(Int x) {\n    fn g(Int y) {\n        return y + x \n    }\n\n    fn g(List y) {\n        return y.count() + x \n    }\n\n    return g\n}\n\nvar a = f(2)\nprint a(1) // expect: 3\nprint a([1,1,1]) // expect: 5\nprint a(\"Hello!\") // expect: 6\n"
  },
  {
    "path": "test/types/multiple_dispatch/multiple_dispatch.morpho",
    "content": "// Dipatch a function on multiple types\n\nfn f(String x) {\n    print x\n}\n\nfn f(List x) {\n    x.append(\"Ho\")\n}\n\nfn f(Float x) {\n    print x+1\n}\n\nfn f(x) {\n    print \"Foo\"\n}\n\nf(\"Hi\")\n// expect: Hi\n\nf(1)\n// expect: Foo\n\nf(1.5)\n// expect: 2.5\n\nvar a = []\nf(a)\nprint a \n// expect: [ Ho ]\n\nf({})\n// expect: Foo\n"
  },
  {
    "path": "test/types/multiple_dispatch/namespace.morpho",
    "content": "// Dispatch a function defined in a namespace\n\nimport \"namespace.xmorpho\" as nm\n\nnm.f(\"Hi\")\n// expect: Hi\n\nnm.f(1)\n// expect: Foo\n\nnm.f(1.5)\n// expect: 2.5\n\nvar a = []\nnm.f(a)\nprint a \n// expect: [ Ho ]\n\nnm.f({})\n// expect: Foo\n"
  },
  {
    "path": "test/types/multiple_dispatch/namespace.xmorpho",
    "content": "// Use namespaces\n\nfn f(String x) {\n    print x\n}\n\nfn f(List x) {\n    x.append(\"Ho\")\n}\n\nfn f(Float x) {\n    print x+1\n}\n\nfn f(x) {\n    print \"Foo\"\n}\n\nfn g() {\n    return 0\n}\n\nfn g(x) {\n    return 1\n}\n\nfn h() {\n    return 0\n}"
  },
  {
    "path": "test/types/multiple_dispatch/namespace_for.morpho",
    "content": "// Namespace with 'for' keyword \n\nimport \"namespace.xmorpho\" as nm for f \n\nnm.g()\n// expect error 'SymblUndfNmSpc'\n"
  },
  {
    "path": "test/types/multiple_dispatch/namespace_for_new.morpho",
    "content": "// Namespace with 'for' keyword, with new implementation in local file\n\nimport \"namespace.xmorpho\" for f \n\n// Here, we add a new implementation for a Matrix input\n\nfn f(Matrix a) {\n    print a.dimensions()\n\n}\n\nf(Matrix(2,2))\n// expect: [ 2, 2 ]\n"
  },
  {
    "path": "test/types/multiple_dispatch/namespace_for_overwrite.morpho",
    "content": "// Namespace with 'for' keyword, and try to overwrite an existing signature\n\nimport \"namespace.xmorpho\" for f \n\n// Here, we define an implementation for a String input, which already exists in the namespace, so it should raise an error.\n\nfn f(String a) {\n    print a.count()\n}\n\nf(\"Hi\")\n// expect error 'MltplDisptchAmbg'\n"
  },
  {
    "path": "test/types/multiple_dispatch/nested_closure.morpho",
    "content": "// Nested closures \n\nfn f(Int x) {\n    fn g(Int y) {\n        return y + x \n    }\n\n    fn g(List y) {\n        return y.count() + x \n    }\n\n    return g\n}\n\nvar a = f(2)\nprint a(1) // expect: 3\nprint a([1,1,1]) // expect: 5\n"
  },
  {
    "path": "test/types/multiple_dispatch/nparams.morpho",
    "content": "// Dispatch a function on multiple types\n\nfn f() {\n    return 0\n}\n\nfn f(x) {\n    return 1\n}\n\nfn f(x, y) {\n    return 2\n}\n\nfn f(x, y, z) {\n    return 3\n}\n\nprint f()\n// expect: 0\n\nprint f(\"Hi\")\n// expect: 1\n\nprint f(1, 2)\n// expect: 2\n\nprint f(1,2,3)\n// expect: 3\n\nprint f(1,2,3,4)\n// expect error 'MltplDsptchFld'\n"
  },
  {
    "path": "test/types/multiple_dispatch/nparams_varg.morpho",
    "content": "// Dispatch a function with variadic args\n\nfn f() {\n    return 0\n}\n\nfn f(x) {\n    return 1\n}\n\nfn f(y, ...x) {\n    return \"V\"\n}\n\nprint f()\n// expect: 0\n\nprint f(\"Hi\")\n// expect: 1\n\nprint f(1, 2)\n// expect: V\n\nprint f(1,2,3)\n// expect: V\n\nprint f(1,2,3,4)\n// expect: V\n"
  },
  {
    "path": "test/types/multiple_dispatch/object_overflow.morpho",
    "content": "// Attempt to use an object type that exceeds the bounds\n// of the metafunction's branch table\n\nfn f(String x) {\n    return 0\n}\n\nfn f(Float x) {\n    return 2 \n}\n\ntry {\n    print f(1.0) \n} catch {\n    \"MltplDsptchFld\": print \"ok\"\n}\n// expect: 2\n\ntry {\n    print f(1) \n} catch {\n    \"MltplDsptchFld\": print \"ok\"\n}\n// expect: ok\n\ntry {\n    print f(Matrix(1,1))\n} catch {\n    \"MltplDsptchFld\": print \"ok\"\n}\n// expect: ok\n"
  },
  {
    "path": "test/types/multiple_dispatch/optional.morpho",
    "content": "// Multiple dispatch with optional arguments\n\nfn f() { return 0 } \n\nfn f(x) { return x }\n\nfn f(x,y,z=0) { return x + y + z }\n\nprint f() \n// expect: 0\n\nprint f(1) \n// expect: 1\n\nprint f(1,2) \n// expect: 3\n\nprint f(1,2,z=3) \n// expect: 6"
  },
  {
    "path": "test/types/multiple_dispatch/optional_invld.morpho",
    "content": "// Multiple dispatch that doesn't include optional arguments\n\nfn f() { return 0 } \n\nfn f(x) { return x }\n\nfn f(x,y,z,a) { return -1 }\n\nprint f() \n// expect: 0\n\nprint f(1,z=3) \n// expect error 'NoOptArg'\n"
  },
  {
    "path": "test/types/multiple_dispatch/pets.morpho",
    "content": "class Dog {\n    init(name) {\n        self.name = name\n    }\n}\n\nclass Cat {\n    init(name) {\n        self.name = name\n    }\n}\n\nfn meets(Dog a, Dog b) {\n    return \"wags its tail\"\n}\n\nfn meets(Dog a, Cat b) {\n    return \"barks\"\n}\n\nfn meets(Cat a, Dog b) {\n    return \"hisses\"\n}\n\nfn meets(Cat a, Cat b) {\n    return \"purrs\"\n}\n\nfn encounter(a, b) {\n    var verb = meets(a, b)\n    print(\"${a.name} meets ${b.name} and ${verb}\")\n}\n\nvar fido = Dog(\"Fido\")\nvar whiskers = Cat(\"Whiskers\")\nvar rex = Dog(\"Rex\")\nvar simba = Cat(\"Simba\")\n\nencounter(fido, rex)       // expect: Fido meets Rex and wags its tail\nencounter(fido, whiskers)  // expect: Fido meets Whiskers and barks\nencounter(whiskers, rex)   // expect: Whiskers meets Rex and hisses\nencounter(whiskers, simba) // expect: Whiskers meets Simba and purrs\n"
  },
  {
    "path": "test/types/multiple_dispatch/pets.zmorpho",
    "content": "class Dog {\n    init(name) {\n        self.name = name\n    }\n}\n\nclass Cat {\n    init(name) {\n        self.name = name\n    }\n}\n\nfn encounter(a, b) {\n    var verb = meets(a, b)\n    print(\"${a.name} meets ${b.name} and ${verb}\")\n}\n\nfn meets(Dog a, Dog b) {\n    return \"wags its tail\"\n}\n\nfn meets(Dog a, Cat b) {\n    return \"barks\"\n}\n\nfn meets(Cat a, Dog b) {\n    return \"hisses\"\n}\n\nfn meets(Cat a, Cat b) {\n    return \"purrs\"\n}\n\nvar fido = Dog(\"Fido\")\nvar whiskers = Cat(\"Whiskers\")\nvar rex = Dog(\"Rex\")\nvar simba = Cat(\"Simba\")\n\nencounter(fido, rex)\nencounter(fido, whiskers)\nencounter(whiskers, rex)\nencounter(whiskers, simba)\n"
  },
  {
    "path": "test/types/multiple_dispatch/pets_subclass.morpho",
    "content": "\n\nclass Pet {\n    init(name) {\n        self.name = name\n    }\n}\n\nclass Dog is Pet {} \nclass Cat is Pet {}\n\nfn meets(Dog a, Dog b) {\n    return \"wags its tail\"\n}\n\nfn meets(Dog a, Cat b) {\n    return \"barks\"\n}\n\nfn meets(Cat a, Dog b) {\n    return \"hisses\"\n}\n\nfn meets(Cat a, Cat b) {\n    return \"purrs\"\n}\n\nfn encounter(Pet a, Pet b) {\n    var verb = meets(a, b)\n    print(\"${a.name} meets ${b.name} and ${verb}\")\n}\n\nvar fido = Dog(\"Fido\")\nvar whiskers = Cat(\"Whiskers\")\nvar rex = Dog(\"Rex\")\nvar simba = Cat(\"Simba\")\n\nencounter(fido, rex)       // expect: Fido meets Rex and wags its tail\nencounter(fido, whiskers)  // expect: Fido meets Whiskers and barks\nencounter(whiskers, rex)   // expect: Whiskers meets Rex and hisses\nencounter(whiskers, simba) // expect: Whiskers meets Simba and purrs\n\nencounter([], rex) // expect error 'MltplDsptchFld'\n"
  },
  {
    "path": "test/types/multiple_dispatch/recursion.morpho",
    "content": "// Multiple dispatch with recursion\n\nfn f(String x) {\n  print x\n}\n\nfn f(List x) {\n  if (x.count()>0) {\n    f(String(x.pop()))\n    f(x)\n  }\n}\n\nf([1,2,3])\n// expect: 3\n// expect: 2\n// expect: 1\n"
  },
  {
    "path": "test/types/multiple_dispatch/scope.morpho",
    "content": "// Dispatch a function on multiple types\n\nfn f() {\n    return 0\n}\n\nfn f(x) {\n    return 1\n}\n\n{\n    fn f(x,y) {\n        return 1\n    }\n}\n\nprint f()\n// expect: 0\n\nprint f(1)\n// expect: 1\n\nprint f(1,2)\n// expect error 'MltplDsptchFld'\n"
  },
  {
    "path": "test/types/multiple_dispatch/two.morpho",
    "content": "// Dispatch a function on multiple types\n\nfn f() {\n    return 0\n}\n\nfn f(x) {\n    return 1\n}\n\nprint f()\n// expect: 0\n\nprint f(\"Hi\")\n// expect: 1\n"
  },
  {
    "path": "test/types/multiple_dispatch/value.morpho",
    "content": "// Dipatch a function on value types\n\nfn f(Int x) {\n    return 0\n}\n\nfn f(Float x) {\n    return 1 \n}\n\nprint f(1) \n// expect: 0\n\nprint f(0.1)\n// expect: 1\n"
  },
  {
    "path": "test/types/multiple_dispatch/varg.morpho",
    "content": "// Dispatch a function with variadic args\n\nfn f() {\n    return 0\n}\n\nfn f(...x) {\n    return \"V\"\n}\n\nprint f()\n// expect: 0\n\nprint f(1, 2)\n// expect: V\n\nprint f(1,2,3)\n// expect: V\n\nprint f(1,2,3,4)\n// expect: V\n"
  },
  {
    "path": "test/types/multiple_dispatch/varg_as_backup.morpho",
    "content": "// Dispatch a function with variadic args\n\nfn f() {\n    return 0\n}\n\nfn f(a) {\n    return 1\n}\n\nfn f(a,b) {\n    return 2\n}\n\nfn f(...x) {\n    return \"V\"\n}\n\nprint f()\n// expect: 0\n\nprint f(1)\n// expect: 1\n\nprint f(1, 2)\n// expect: 2\n\nprint f(1,2,3)\n// expect: V\n\nprint f(1,2,3,4)\n// expect: V\n"
  },
  {
    "path": "test/types/type_from_namespace.morpho",
    "content": "// Use a type that doesn't exist from a namespace in a function definition \n\nimport \"type_namespace.xmorpho\" as ns \n\nns.Cat x = ns.Cat(\"Phineas\")\n\nx.hiss() // expect: Phineas hisses\n"
  },
  {
    "path": "test/types/type_in_function.morpho",
    "content": "// Type definition in function\n\nfn f() {\n    String u = \"Boo\"\n\n    print u \n}\n\nf() \n// expect: Boo"
  },
  {
    "path": "test/types/type_in_function_fake_namespace.morpho",
    "content": "// Use a type that doesn't exist from a namespace in a function definition \n\nimport \"type_namespace.xmorpho\" as ns \n\nfn f(foo.Dog x) {\n    x.hiss()\n}\n\nf(ns.Cat(\"Moggies\")) // expect error 'UnknwnNmSpc'"
  },
  {
    "path": "test/types/type_in_function_namespace.morpho",
    "content": "// Use types from a namespace in a function definition \n\nimport \"type_namespace.xmorpho\" as ns \n\nprint ns.Cat // expect: @Cat\n\nfn f(ns.Cat x) {\n    x.hiss()\n}\n\nf(ns.Cat(\"Moggies\")) // expect: Moggies hisses\n"
  },
  {
    "path": "test/types/type_in_function_namespace_not_found.morpho",
    "content": "// Use a type that doesn't exist from a namespace in a function definition \n\nimport \"type_namespace.xmorpho\" as ns \n\nfn f(ns.Hamster x) {\n    x.hiss()\n}\n\nf(ns.Cat(\"Moggies\")) // expect error 'SymblUndfNmSpc'"
  },
  {
    "path": "test/types/type_in_global.morpho",
    "content": "// Check type \n\nString a = \"Foo\"\n\nprint a \n// expect: Foo\n"
  },
  {
    "path": "test/types/type_inheritance.morpho",
    "content": "// Types and inheritance\n\nclass A { }   \n\nclass B is A { } \n\nclass C is B { } \n\nclass D is B { } \n\nfn f() {\n    A a = B()\n    A x = C() \n    B y = D()\n\n    print a // expect: <B>\n    print x // expect: <C>\n    print y // expect: <D>\n}\n\nf()\n"
  },
  {
    "path": "test/types/type_instance.morpho",
    "content": "// Check type of an instance\n\nclass A { } \n\nA a = A()\n\nprint a // expect: <A>\n"
  },
  {
    "path": "test/types/type_namespace.xmorpho",
    "content": "// Define some classes in a namespace\n \nclass Pet { \n    init(name) {\n        self.name = name\n    }\n}\n\nclass Dog is Pet { } \n\nclass Cat is Pet { \n    hiss() {\n        print \"${self.name} hisses\"\n    }\n}\n"
  },
  {
    "path": "test/types/type_violation_constant.morpho",
    "content": "// Type definition in function\n\nfn f() {\n    String u = 1\n}\n\nf() \n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/types/type_violation_dictionary.morpho",
    "content": "// Type violation in function from a Dictionary\n\nfn f() {\n    String u \n\n    u = { \"a\" : \"b\" }\n}\n\nf() \n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/types/type_violation_global.morpho",
    "content": "// Type definition in global context\n\nString u = 1\n\n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/types/type_violation_in_function.morpho",
    "content": "// Type violation in function\n\nfn f() {\n    String u \n\n    u = []\n}\n\nf() \n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/types/type_violation_inheritance.morpho",
    "content": "// Types and inheritance\n\nclass A { } \n\nclass B is A { } \n\nclass C is B { } \n\nclass D is A { } \n\nfn f() {\n    A a = B()\n    A x = C() \n    B y = C()\n    B z = D() // expect error 'TypeErr'\n}\n\nf()\n"
  },
  {
    "path": "test/types/type_violation_instance.morpho",
    "content": "// Type violation by assigning incorrect instance\n\nclass A { } \n\nclass B { } \n\nfn f() {\n    A a = B() // expect error 'TypeErr'\n}\n\nf()\n"
  },
  {
    "path": "test/types/type_violation_matrix.morpho",
    "content": "// Type violation in function from a Matrix\n\nfn f() {\n    String u \n\n    u = Matrix(2,2)\n}\n\nf() \n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/types/type_violation_propagation.morpho",
    "content": "// Detect type violations from propagation\n\nfn f() {\n    String u \n\n    var a = 1\n\n    u = a\n}\n\nf() \n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/types/type_violation_range.morpho",
    "content": "// Type violation in function from a Range\n\nfn f() {\n    String u \n\n    u = 1..3\n}\n\nf() \n// expect error 'TypeErr'\n"
  },
  {
    "path": "test/valgrind.py",
    "content": "#!/usr/bin/env python3\n# Simple automated testing with valgrind\n# T J Atherton Jan 2025\n\n# import necessary modules\nimport os, glob, sys\nimport regex as rx\nfrom functools import reduce\nimport operator\nimport colored\nfrom colored import stylize\n\n# define what command to use to invoke valgrind\nvalgrind = 'valgrind'\n\n# define what command to use to invoke the interpreter\ncommand = 'morpho6'\n\n# define the file extension to test\next = 'morpho'\n\ndef checkvalgrindlog(filepath):\n    file_object = open(filepath, 'r')\n    lines = file_object.readlines()\n    file_object.close()\n    for l in lines: \n        if (rx.search('in use at exit: 0 bytes in 0 blocks', l)):\n            return True\n    return False\n\n# Test a file\ndef test(file, CI):\n    ret = 0\n    if not CI:\n        print(file+\":\", end=\" \")\n\n    # Create a temporary file in the same directory\n    tmp = file + '.valgrind'\n\n    # Run the test\n    os.system(valgrind + ' --log-file='+tmp + ' ' + command + ' ' +file + ' > /dev/null')\n\n    valpass = False\n    if os.path.exists(tmp):\n        valpass = checkvalgrindlog(tmp)\n\n        # Was it expected?\n        if(valpass):\n            if not CI:\n                print(stylize(\"Passed\",colored.fg(\"green\")))\n\n            ret = 1\n        else:\n            if not CI:\n                print(stylize(\"Failed\",colored.fg(\"red\")))\n            else:\n                print(file + \" Failed\")\n\n\n        # Delete the temporary files\n        os.system('rm ' + tmp)\n\n    return ret\n\nprint('--Begin testing---------------------')\n\n# open a test log\n# write failures to log\nsuccess=0 # number of successful tests\ntotal=0   # total number of tests\n\n# look for a command line arguement that says\n# this is being run for continous integration\nCI = False\nfor arg in sys.argv:\n    if arg == '-c': # if the argument is -c, then we are running in CI mode\n        CI = True\n\nfiles=glob.glob('**/**.'+ext, recursive=True)\nfor f in files:\n    success+=test(f,CI)\n    total+=1\n\nprint('--End testing-----------------------')\nprint(success, 'out of', total, 'tests passed.')\nif CI and success<total:\n    exit(-1)\n"
  },
  {
    "path": "test/variable/duplicate_local.morpho",
    "content": "{\n  var a = \"value\"\n  var a = \"other\" // expect error: 'VblDcl'\n}\n"
  },
  {
    "path": "test/variable/duplicate_parameter.morpho",
    "content": "fn foo(arg,\n        arg) { // expect error: 'VblDcl'\n  \"body\"\n}\n"
  },
  {
    "path": "test/variable/early_bound.morpho",
    "content": "// Checks that a closure is not gazumped by an local variable declared after it\nvar a = \"outer\"\n{\n  fn foo() {\n    print a\n  }\n\n  foo() // expect: outer\n  var a = \"inner\"\n  foo() // expect: outer\n}\n"
  },
  {
    "path": "test/variable/in_middle_of_block.morpho",
    "content": "{\n  var a = \"a\"\n  print a // expect: a\n  var b = a + \" b\"\n  print b // expect: a b\n  var c = a + \" c\"\n  print c // expect: a c\n  var d = b + \" d\"\n  print d // expect: a b d\n}\n"
  },
  {
    "path": "test/variable/in_nested_block.morpho",
    "content": "{\n  var a = \"outer\"\n  {\n    print a // expect: outer\n  }\n}\n"
  },
  {
    "path": "test/variable/local_from_method.morpho",
    "content": "var foo = \"variable\"\n\nclass Foo {\n  method() {\n    print foo\n  }\n}\n\nFoo().method() // expect: variable\n"
  },
  {
    "path": "test/variable/multiple.morpho",
    "content": "// Multiple variable declarations with one var\n\nvar a=1,b=2,c[3]=[1,2,3]\n\nprint a\n// expect: 1\n\nprint b\n// expect: 2\n\nprint c[2]\n// expect: 3\n"
  },
  {
    "path": "test/variable/redeclare_global.morpho",
    "content": "var a = \"1\"\nvar a\nprint a // expect: nil\n"
  },
  {
    "path": "test/variable/redefine_global.morpho",
    "content": "var a = \"1\"\nvar a = \"2\"\nprint a // expect: 2\n"
  },
  {
    "path": "test/variable/scope_reuse_in_different_blocks.morpho",
    "content": "{\n  var a = \"first\"\n  print a // expect: first\n}\n\n{\n  var a = \"second\"\n  print a // expect: second\n}\n"
  },
  {
    "path": "test/variable/shadow_and_local.morpho",
    "content": "{\n  var a = \"outer\"\n  {\n    print a // expect: outer\n    var a = \"inner\"\n    print a // expect: inner\n  }\n}\n"
  },
  {
    "path": "test/variable/shadow_global.morpho",
    "content": "var a = \"global\"\n{\n  var a = \"shadow\"\n  print a // expect: shadow\n}\nprint a // expect: global\n"
  },
  {
    "path": "test/variable/shadow_local.morpho",
    "content": "{\n  var a = \"local\"\n  {\n    var a = \"shadow\"\n    print a // expect: shadow\n  }\n  print a // expect: local\n}\n"
  },
  {
    "path": "test/variable/undefined_global.morpho",
    "content": "print notDefined  // expect error 'SymblUndf'\n"
  },
  {
    "path": "test/variable/undefined_local.morpho",
    "content": "{\n  print notDefined  // expect error: 'SymblUndf'\n}\n"
  },
  {
    "path": "test/variable/uninitialized.morpho",
    "content": "var a\nprint a // expect: nil\n"
  },
  {
    "path": "test/variable/unreached_undefined.morpho",
    "content": "// Code included in an unreachable block should be ignored\nif (false) {\n  print notDefined\n}\n\nif (false) {\n  print stillNotDefined\n} else {\n  print \"foo\" // expect: foo\n}\n\nprint \"ok\" // expect: ok\n"
  },
  {
    "path": "test/variable/use_false_as_var.morpho",
    "content": "// expect error 'VarExpct'\nvar false = \"value\"\n"
  },
  {
    "path": "test/variable/use_global_in_initializer.morpho",
    "content": "var a = \"value\"\nvar a = a\nprint a // expect: value\n"
  },
  {
    "path": "test/variable/use_nil_as_var.morpho",
    "content": "var nil = \"value\"\n// expect error: 'VarExpct'\n"
  },
  {
    "path": "test/variable/use_self_as_var.morpho",
    "content": "var self = \"value\"\n// expect error: 'VarExpct'\n"
  },
  {
    "path": "test/variable/var_dictionary_as_label.morpho",
    "content": "// Check that a variable name with a dictionary produces an error\n\nvar {} \n// expect error: 'VarExpct'\n"
  },
  {
    "path": "test/veneer/bool.morpho",
    "content": "// Bool veneer class \n \ntrue.prnt() // expect: true\nprint \"\"\nfalse.prnt() // expect: false\nprint \"\"\n\nprint islist(true.respondsto()) // expect: true\n\nprint true.clss() // expect: @Bool\n"
  },
  {
    "path": "test/veneer/float.morpho",
    "content": "//Test Float veneer class\n\nprint 123.2323.format(\"%.2f\")\n// expect: 123.23\n\nvar a = 2e-5\nprint a.format(\"%.4e\")\n// expect: 2.0000e-05\n\nprint 123.4444.format(\"%10.1f\")\n// expect:      123.4\n\nprint 1e10.format(\"%g\")\n// expect: 1e+10"
  },
  {
    "path": "test/veneer/format_args.morpho",
    "content": "// Format called with no format string\n\nprint 123.2323.format()\n// expect error 'FrmtArg'"
  },
  {
    "path": "test/veneer/format_invld_args.morpho",
    "content": "// Format called with no format string\n\nprint 1.format(\"%d\")\n// expect error 'InvldFrmt'"
  },
  {
    "path": "test/veneer/int.morpho",
    "content": "//Test Int veneer class\n\nvar a = 100\n\nprint a.format(\"%i\")\n// expect: 100\n\nprint a.format(\"0x%.4x\")\n// expect: 0x0064\n\nprint a.format(\"0o%o\")\n// expect: 0o144\n\nprint \"'${a.format(\"%10o\")}\"\n// expect: '       144"
  },
  {
    "path": "test/veneer/invocation.morpho",
    "content": "// Test forming invocations from values\n\nvar a = 100\nvar b = a.format\n\nprint b(\"%x\")\n// expect: 64"
  },
  {
    "path": "test/vtk/ensurevtkfilename.morpho",
    "content": "// Testing whether importing / exporting files with filenames that don't\n// end in a \".vtk\" works. The desired behavior is to append the filename\n// with a \".vtk\" in case it doesn't end that way\n\nimport vtk \nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar filenames = [\"data\", \"data.case2\", \"data.case3.0\"]\n\nvar vtkE\nvar vtkI\n\n// test whether exporting with these filenames and then importing with\n// an added \".vtk\" works\n\nfor (filename in filenames) {\n\n    vtkE = VTKExporter(m1)\n    \n    vtkE.export(filename)\n\n    vtkI = VTKImporter(\"${filename}.vtk\")\n\n    var m2 = vtkI.mesh()\n\n    print m2 \n}\n// expect: <Mesh: 2 vertices>\n// expect: <Mesh: 2 vertices>\n// expect: <Mesh: 2 vertices>\n\n\n// Test whether importing without .vtk works\n\nfor (filename in filenames) {\n\n    vtkE = VTKExporter(m1)\n    \n    vtkE.export(filename)\n\n    vtkI = VTKImporter(filename)\n\n    var m2 = vtkI.mesh()\n\n    print m2 \n}\n// expect: <Mesh: 2 vertices>\n// expect: <Mesh: 2 vertices>\n// expect: <Mesh: 2 vertices>\n\n"
  },
  {
    "path": "test/vtk/export_and_import_mesh.morpho",
    "content": "import vtk \nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar filename = \"data.vtk\"\n\nvar vtkE = VTKExporter(m1)\n\nvtkE.export(filename)\n\nvar vtkI = VTKImporter(filename)\n\nvar m2 = vtkI.mesh()\n\nprint m1.vertexmatrix() // expect: [ -1 1 ]\n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\n\nprint m2.vertexmatrix() // expect: [ -1 1 ]\n// expect: [ 0 0 ]\n// expect: [ 0 0 ]\n"
  },
  {
    "path": "test/vtk/export_and_import_no_fieldname.morpho",
    "content": "// Testing whether exporting and importing a field with no field name\n// works. In this case, the fieldname will be either \"scalars\" or \"vectors\"\n\nimport vtk\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar f1 = Field(m1, fn(x,y,z) x)\n\nvar g1 = Field(m1, fn(x,y,z) Matrix([x,2*x,3*x]))\n\nvar vtkE = VTKExporter(f1)\nvtkE.addfield(g1)\n\nvar filename = \"data.vtk\"\n\nvtkE.export(filename)\n\nvar vtkI = VTKImporter(filename)\n\nvar m2 = vtkI.mesh()\n\nvar f2 = vtkI.field(\"scalars\")\n\nvar g2 = vtkI.field(\"vectors\")\n\nprint m2 // expect: <Mesh: 2 vertices>\n\nprint f2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ 1 ]\n\nprint g2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ -2 ]\n// expect: [ -3 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/vtk/export_and_import_scalar.morpho",
    "content": "// Testing whether exporting and importing a scalar field works \n\nimport vtk\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar f1 = Field(m1, fn(x,y,z) x)\n\nvar vtkE = VTKExporter(f1, fieldname=\"f\")\n\nvar filename = \"data.vtk\"\n\nvtkE.export(filename)\n\nvar vtkI = VTKImporter(filename)\n\nvar m2 = vtkI.mesh()\n\nvar f2 = vtkI.field(\"f\")\n\nprint m2 // expect: <Mesh: 2 vertices>\n\nprint f2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ 1 ]\n"
  },
  {
    "path": "test/vtk/export_and_import_scalar_and_vector.morpho",
    "content": "// Testing whether exporting and importing a scalar and a vector field\n// together works\n\nimport vtk\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar f1 = Field(m1, fn(x,y,z) x)\n\nvar g1 = Field(m1, fn(x,y,z) Matrix([x,2*x,3*x]))\n\nvar vtkE = VTKExporter(f1, fieldname=\"f\")\nvtkE.addfield(g1, fieldname=\"g\")\n\nvar filename = \"data.vtk\"\n\nvtkE.export(filename)\n\nvar vtkI = VTKImporter(filename)\n\nvar m2 = vtkI.mesh()\n\nvar f2 = vtkI.field(\"f\")\n\nvar g2 = vtkI.field(\"g\")\n\nprint m2 // expect: <Mesh: 2 vertices>\n\nprint f2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ 1 ]\n\nprint g2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ -2 ]\n// expect: [ -3 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/vtk/export_and_import_vector.morpho",
    "content": "// Testing whether exporting and importing a vector field works \n\nimport vtk\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar g1 = Field(m1, fn(x,y,z) Matrix([x,2*x,3*x]))\n\nvar vtkE = VTKExporter(g1, fieldname=\"g\")\n\nvar filename = \"data.vtk\"\n\nvtkE.export(filename)\n\nvar vtkI = VTKImporter(filename)\n\nvar m2 = vtkI.mesh()\n\nvar g2 = vtkI.field(\"g\")\n\nprint m2 // expect: <Mesh: 2 vertices>\n\nprint g2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ -2 ]\n// expect: [ -3 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/vtk/export_and_import_vector_2d.morpho",
    "content": "// Testing whether exporting and importing a 2D vector field works. This\n// should add a 0 in the z component of the vector in the saved file. \n\nimport vtk\nimport meshtools\n\nvar m1 = LineMesh(fn (t) [t,0,0], -1..1:2)\n\nvar g1 = Field(m1, fn(x,y,z) Matrix([x,2*x]))\n\nvar vtkE = VTKExporter(g1, fieldname=\"g\")\n\nvar filename = \"data.vtk\"\n\nvtkE.export(filename)\n\nvar vtkI = VTKImporter(filename)\n\nvar m2 = vtkI.mesh()\n\nvar g2 = vtkI.field(\"g\")\n\nprint m2 // expect: <Mesh: 2 vertices>\n\nprint g2 // expect: <Field>\n// expect: [ -1 ]\n// expect: [ -2 ]\n// expect: [ 0 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 0 ]\n"
  },
  {
    "path": "test/vtk/export_import_2d.morpho",
    "content": "import meshtools\nimport vtk \n\nvar m = Mesh(\"square.mesh\")\n\nvar fname = \"square.vtk\"\n\nVTKExporter(m).export(fname)\n\nvar m2 = VTKImporter(fname).mesh()\n\nprint m2.maxgrade() // expect: 2\n"
  },
  {
    "path": "test/vtk/export_import_3d.morpho",
    "content": "import meshtools\nimport vtk \n\nvar m = Mesh(\"tetrahedron.mesh\")\n\nvar fname = \"tetrahedron.vtk\"\n\nVTKExporter(m).export(fname)\n\nvar m2 = VTKImporter(fname).mesh()\n\nprint m2.maxgrade() // expect: 3\n"
  },
  {
    "path": "test/vtk/export_incorrect_field_4d_vector.morpho",
    "content": "// Trying to export a field of dimension not currently supported by the VTKExporter\nimport vtk\nimport meshtools\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:0.5, -1..1:0.5)\nm.addgrade(1)\n\nvar f3 = Field(m, Matrix([0,1,2,3])) // Vector field of >3 dimensions\n\nVTKExporter(f3).export() // expect: Error 'FieldDimErr' : Expected a scalar or a 2D/3D vector field, but received a vector field with 4 dimensions.\n"
  },
  {
    "path": "test/vtk/export_incorrect_field_grade1.morpho",
    "content": "// Trying to export a field of dimension not currently supported by the VTKExporter\nimport vtk\nimport meshtools\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:0.5, -1..1:0.5)\nm.addgrade(1)\n\nvar f1 = Field(m, 0, grade=1) // Field defined on the edges\n\nVTKExporter(f1).export() // expect: Error 'FieldShapeErr' : Received a field with shape `[0,1,0]`. Fields with shape other than [1,0,0] are not currently supported.\n"
  },
  {
    "path": "test/vtk/export_incorrect_field_grade2.morpho",
    "content": "// Trying to export a field of dimension not currently supported by the VTKExporter\nimport vtk\nimport meshtools\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:0.5, -1..1:0.5)\nm.addgrade(1)\n\nvar f2 = Field(m, 0, grade=2) // Field defined on the facets\n\nVTKExporter(f2).export() // expect: Error 'FieldShapeErr' : Received a field with shape `[0,0,1]`. Fields with shape other than [1,0,0] are not currently supported.\n"
  },
  {
    "path": "test/vtk/export_incorrect_field_tensor.morpho",
    "content": "// Trying to export a field of dimension not currently supported by the VTKExporter\nimport vtk\nimport meshtools\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:0.5, -1..1:0.5)\nm.addgrade(1)\n\nvar f4 = Field(m, Matrix([[0,1,2],[3,4,5],[6,7,8]])) // Matrix field of non-columnar vectors\n\nVTKExporter(f4).export() // expect: Error 'FieldDimErr' : Expected a scalar or a 2D/3D vector field, but received a non-columnar matrix with dimensions [3,3].\n"
  },
  {
    "path": "test/vtk/import_external_vtk.morpho",
    "content": "// Attempting to open a file rbc_001.vtk obtained from\n// https://people.sc.fsu.edu/~jburkardt/data/vtk/vtk.html\n\nimport vtk\nimport plot \n\nvar vtkI = VTKImporter(\"rbc_001.vtk\")\n\nvar m = vtkI.mesh()\n\nprint m.count(0) // expect: 500\nprint m.count(2) // expect: 996\n"
  },
  {
    "path": "test/vtk/import_mesh.morpho",
    "content": "// Import a file containing just a mesh\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh.vtk\")\n\nvar m = vtkI.mesh()\n\nprint m // expect: <Mesh: 2 vertices>\n"
  },
  {
    "path": "test/vtk/import_scalar.morpho",
    "content": "// Import a file containing a mesh and a scalar field\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh_scalar.vtk\")\n\nvar m = vtkI.mesh()\n\nprint m // expect: <Mesh: 2 vertices>\n\nvar f = vtkI.field(\"f\")\n\nprint f // expect: <Field>\n// expect: [ -1 ]\n// expect: [ 1 ]\n"
  },
  {
    "path": "test/vtk/import_scalar_vector.morpho",
    "content": "// Import a file containing a mesh, a scalar field and a vector field\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh_scalar_vector.vtk\")\n\nvar f = vtkI.field(\"f\")\n\nvar m = vtkI.mesh()\n\nprint m // expect: <Mesh: 2 vertices>\n\nprint f // expect: <Field>\n// expect: [ -1 ]\n// expect: [ 1 ]\n\nvar g = vtkI.field(\"g\")\n\nprint g // expect: <Field>\n// expect: [ -1 ]\n// expect: [ -2 ]\n// expect: [ -3 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/vtk/import_vector.morpho",
    "content": "// Import a file containing a mesh and a vector field\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh_vector.vtk\")\n\nvar m = vtkI.mesh()\n\nprint m // expect: <Mesh: 2 vertices>\n\nvar g = vtkI.field(\"g\")\n\nprint g // expect: <Field>\n// expect: [ -1 ]\n// expect: [ -2 ]\n// expect: [ -3 ]\n// expect: [ 1 ]\n// expect: [ 2 ]\n// expect: [ 3 ]\n"
  },
  {
    "path": "test/vtk/importer_containsfield.morpho",
    "content": "// Checking whether the contiainsfield function works\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh_scalar.vtk\")\n\nprint vtkI.containsfield(\"f\") // expect: true\nprint vtkI.containsfield(\"g\") // expect: false\n"
  },
  {
    "path": "test/vtk/importer_fieldlist.morpho",
    "content": "// Listing all the fields in the file\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh_scalar_vector.vtk\")\n\nprint vtkI.fieldlist() // expect: [ f, g ]\n"
  },
  {
    "path": "test/vtk/importer_incorrect_fieldname.morpho",
    "content": "// Trying to get a field that is not in the file\nimport vtk\nimport meshtools\n\nvar vtkI = VTKImporter(\"mesh_scalar.vtk\")\n\nvar f = vtkI.field(\"h\") // expect: Error 'FieldNotFound' : Couldn't find the field `h` in the file. \n"
  },
  {
    "path": "test/vtk/mesh.vtk",
    "content": "# vtk DataFile Version 3.0 \nExported using Morpho https://github.com/Morpho-lang/morpho \nASCII \nDATASET UNSTRUCTURED_GRID \n\nPOINTS 2 float \n-1 0 0 \n1 0 0 \n\n\nCELLS 1 3 \n2 0 1 \n\n\nCELL_TYPES 1\n3 \n\n\n"
  },
  {
    "path": "test/vtk/mesh_scalar.vtk",
    "content": "# vtk DataFile Version 3.0 \nExported using Morpho https://github.com/Morpho-lang/morpho \nASCII \nDATASET UNSTRUCTURED_GRID \n\nPOINTS 2 float \n-1 0 0 \n1 0 0 \n\n\nCELLS 1 3 \n2 0 1 \n\n\nCELL_TYPES 1\n3 \n\n\nPOINT_DATA 2 \nSCALARS f float 1 \nLOOKUP_TABLE default \n-1 \n1 \n\n\n"
  },
  {
    "path": "test/vtk/mesh_scalar_vector.vtk",
    "content": "# vtk DataFile Version 3.0 \nExported using Morpho https://github.com/Morpho-lang/morpho \nASCII \nDATASET UNSTRUCTURED_GRID \n\nPOINTS 2 float \n-1 0 0 \n1 0 0 \n\n\nCELLS 1 3 \n2 0 1 \n\n\nCELL_TYPES 1\n3 \n\n\nPOINT_DATA 2 \nSCALARS f float 1 \nLOOKUP_TABLE default \n-1 \n1 \n\n\nVECTORS g float\n-1 -2 -3 \n1 2 3 \n\n\n"
  },
  {
    "path": "test/vtk/mesh_vector.vtk",
    "content": "# vtk DataFile Version 3.0 \nExported using Morpho https://github.com/Morpho-lang/morpho \nASCII \nDATASET UNSTRUCTURED_GRID \n\nPOINTS 2 float \n-1 0 0 \n1 0 0 \n\n\nCELLS 1 3 \n2 0 1 \n\n\nCELL_TYPES 1\n3 \n\n\nPOINT_DATA 2 \nVECTORS g float\n-1 -2 -3 \n1 2 3 \n\n\n"
  },
  {
    "path": "test/vtk/rbc_001.vtk",
    "content": "# vtk DataFile Version 1.0\nrbc_001.vtk  3D Unstructured Grid of Triangles\nASCII\n\nDATASET UNSTRUCTURED_GRID\nPOINTS 500 float\n-3.424999 -0.855454 2.257396 \n-1.484919 0.665606 -3.151304 \n1.636841 -0.848154 -0.458954 \n3.732041 0.187906 -1.319734 \n-1.756719 0.682006 0.807596 \n0.911641 -0.828054 3.040696 \n-0.218059 -0.489374 -3.806524 \n-1.078099 0.891706 -2.420454 \n-3.338019 0.263706 1.386896 \n2.931841 1.447006 1.793796 \n-1.796229 0.715706 1.214996 \n2.421641 1.454706 0.904796 \n-0.204659 0.658506 3.627796 \n-2.160579 -0.044274 3.331996 \n0.495541 -0.380754 -3.778464 \n0.053641 1.122906 -2.441584 \n-1.962639 -1.126464 3.026496 \n0.009941 1.061706 2.921296 \n0.252541 0.849906 0.570996 \n-1.675219 -1.336964 2.454096 \n0.409141 0.968806 -1.146214 \n2.016341 1.414006 -2.211994 \n-1.008069 -1.305914 1.804096 \n-2.530049 0.495106 2.133796 \n2.489541 0.835106 -3.168524 \n-1.025719 -1.208374 -2.156554 \n0.768841 -0.904814 -2.371244 \n3.327941 -0.212814 1.173396 \n-0.179759 0.895606 -1.223224 \n2.897641 -0.330874 -1.827444 \n0.013941 -0.720894 -3.509614 \n-1.960699 -1.392874 1.492296 \n-2.685229 0.550306 -0.706694 \n-2.297709 0.619406 0.586196 \n-3.521319 -0.083864 1.833196 \n-2.201219 -0.828584 -3.217494 \n-0.360359 -0.889074 3.534896 \n3.468741 -0.190594 0.697996 \n-0.013059 1.067406 -2.013034 \n-2.036049 0.250906 3.181796 \n-3.022249 0.471206 -0.260454 \n-3.295369 -0.809574 -2.371464 \n-1.521199 0.796606 2.329096 \n0.574041 -0.871424 -2.850944 \n1.364141 -0.949124 1.757796 \n-3.098809 -1.382564 1.671896 \n0.341441 -0.982094 -0.316984 \n3.380241 0.552906 2.256096 \n-1.214439 0.759306 -1.130544 \n1.671941 1.292106 -2.835764 \n2.491941 -0.394084 -2.298934 \n2.029841 -0.587094 -2.285774 \n-2.777879 -0.951204 -2.762034 \n0.566441 -0.961874 -1.878424 \n3.446041 0.698506 -2.345714 \n-3.547709 -0.450094 2.114896 \n-2.336589 0.629806 1.022096 \n0.848541 1.224606 2.076096 \n1.314241 1.287706 2.779996 \n-2.709559 -1.280744 -2.224514 \n3.367141 0.017976 -1.816064 \n3.232341 0.051706 2.114896 \n0.541141 -0.869594 3.255796 \n2.128541 -0.497124 2.652896 \n-0.200059 -1.053504 3.208996 \n-3.756499 0.066606 0.776996 \n-3.967359 -1.328364 -0.088464 \n-1.813299 0.726006 -1.791184 \n3.796541 1.269606 1.178996 \n1.059441 -0.933104 -0.369634 \n0.764641 -0.181114 3.786496 \n3.614641 0.458406 1.807496 \n0.174741 -1.149784 2.131896 \n1.279841 -0.896984 -0.835184 \n-0.483759 0.750706 -0.474934 \n1.634441 -0.617364 2.942196 \n1.827941 -0.867034 1.017696 \n3.867841 0.122106 0.346746 \n0.176841 1.027506 1.681196 \n2.225041 1.175706 2.845196 \n-2.500429 -1.389734 0.049896 \n-2.051029 0.619206 2.480296 \n-3.125099 -0.430314 2.663296 \n-0.736519 -1.247034 1.423396 \n1.188141 -0.197534 -3.677924 \n1.832341 -0.803304 1.991596 \n0.211041 1.027506 -1.579384 \n-3.254559 0.137506 -1.890674 \n-0.771749 0.856406 1.552396 \n4.013141 1.273606 0.476596 \n-2.140089 0.548706 -2.465164 \n-0.101059 0.185806 -4.007984 \n-2.145699 0.432206 2.838696 \n-0.124659 -1.003264 -2.836324 \n-3.656639 -1.327274 -1.205834 \n1.990941 0.889406 3.301896 \n-3.563009 -1.078494 -1.807824 \n3.114041 -0.382604 -0.930404 \n-2.629559 -1.438624 -1.052814 \n0.668341 -0.484874 3.660796 \n2.528741 1.352506 2.330696 \n3.056941 -0.458614 -0.045094 \n-2.171369 -1.329874 0.457096 \n-0.542059 -1.084944 -0.932734 \n-3.823749 -0.821974 1.650896 \n0.440041 -0.998044 -1.463874 \n-2.106999 0.624406 -0.558624 \n1.826441 0.404306 3.518896 \n3.806941 1.402606 -0.673144 \n-0.175859 -1.177544 1.418596 \n-0.430259 -0.984474 -3.124334 \n-1.429589 0.105206 -3.781744 \n2.318241 -0.226994 2.950596 \n0.988841 -0.551814 3.477596 \n-3.672659 0.131006 -0.855224 \n3.800141 0.181506 0.958096 \n-1.200429 0.745406 1.081696 \n-0.132059 1.001306 -3.235794 \n2.477541 1.434306 0.434786 \n1.191441 -0.736104 -2.760274 \n-4.140549 -0.733584 -0.846874 \n-3.094119 -1.123994 -2.225014 \n-3.149999 -0.062234 2.407796 \n-1.158149 -1.072274 -3.039524 \n-0.400659 -1.241414 1.903496 \n1.400641 -0.174024 3.597496 \n-1.630609 0.521006 3.115196 \n-1.910689 0.640406 0.362426 \n2.543641 -0.179114 -2.735614 \n1.308541 -0.967644 1.277396 \n-1.093769 -1.170714 -1.120094 \n-2.038329 0.620406 -0.058894 \n-3.683249 -0.062744 -1.421644 \n3.818441 0.036916 -0.292334 \n-0.993219 -0.522244 3.780696 \n-2.148329 -1.325934 -2.178484 \n2.447041 1.396806 -0.113854 \n-3.777879 -1.387444 0.533296 \n-0.112359 -1.087874 0.573196 \n1.377841 1.272306 1.635396 \n1.254941 0.151606 3.713996 \n0.372641 -1.007944 -1.036354 \n0.403441 1.165806 2.515396 \n1.230641 -0.844674 2.678496 \n3.348241 0.265006 -2.227234 \n-2.340539 0.614206 -1.496794 \n2.706141 1.026906 2.803596 \n-1.531129 0.802406 1.941896 \n0.804641 1.121306 -1.501914 \n0.059541 0.809706 -0.311584 \n1.451341 -0.739234 -2.431744 \n0.343741 0.558806 -3.900584 \n-2.072989 0.334706 -3.202074 \n-1.751489 -1.001144 -3.138244 \n2.893641 0.571106 -2.934174 \n1.919241 1.291906 0.708896 \n-3.474459 -1.394114 1.127496 \n0.477941 0.873206 -3.684324 \n1.020041 0.816806 -3.717124 \n-1.097129 -1.220604 1.032496 \n-3.003429 -1.491814 0.041216 \n-0.507559 -0.220114 -3.970844 \n-0.871339 0.908506 -1.824914 \n-1.002469 0.877506 -2.880294 \n-4.003789 -0.252654 -0.874304 \n1.928341 -0.505854 -2.700694 \n-4.185099 -0.475784 -0.280294 \n2.133041 1.318306 -0.575044 \n-1.046039 0.694406 3.191796 \n-3.890309 -1.158434 1.104596 \n2.113641 1.121406 -3.070624 \n-2.010099 -1.292954 -0.216314 \n1.987941 -0.788144 -0.140574 \n-1.267489 0.847406 -2.042934 \n1.081541 -0.900484 -1.586604 \n1.738041 -0.756064 2.435396 \n-0.069359 0.820406 0.927096 \n-0.635559 0.800206 1.127196 \n2.931141 0.143506 2.623996 \n-4.225549 -0.669494 0.371026 \n-0.338759 0.966206 -1.677904 \n3.366541 -0.280934 -0.452624 \n-1.410509 -1.305514 1.425996 \n0.167541 1.102406 2.105496 \n-2.300829 0.579706 -1.995564 \n1.544641 -0.527864 -2.984594 \n-1.480049 -1.367304 2.012496 \n1.878641 -0.009994 -3.437454 \n-1.177279 -1.306154 2.668896 \n-0.553259 -1.221724 2.822196 \n1.621241 1.165206 -0.259554 \n0.955141 -0.677624 -3.163504 \n0.479541 1.086106 -3.276734 \n1.177041 -0.857644 -2.034864 \n-2.630079 -0.422604 3.087996 \n3.196841 -0.183384 1.661096 \n-0.283959 -1.216724 2.424996 \n-0.107759 -1.058054 -1.231574 \n2.249141 1.312206 -2.593074 \n0.830641 -1.040704 1.520796 \n0.576841 -1.023904 0.578496 \n1.423641 1.337406 -2.343334 \n-0.730489 0.730806 0.734796 \n0.769941 0.229206 3.836296 \n4.100741 1.182206 -0.238214 \n2.089241 0.092906 3.337096 \n-0.763159 0.136706 -3.966944 \n2.048641 -0.227544 -3.081924 \n-3.669929 0.168106 0.252466 \n0.140141 -1.084624 2.934796 \n0.851241 0.607406 3.712096 \n3.700541 0.473606 -1.803374 \n-2.468259 -0.464164 -3.254234 \n-0.203359 0.903606 1.386696 \n-0.643559 -0.829614 -3.468714 \n1.614941 1.173406 0.307636 \n1.392641 0.580206 3.626796 \n-2.505129 0.283706 -2.756974 \n0.253041 -1.109694 2.506596 \n3.828141 0.886106 -1.663004 \n1.044541 1.128806 -3.297774 \n0.181941 0.250406 3.905496 \n1.562941 1.190006 -0.810124 \n0.781841 -1.043654 1.933696 \n-3.185429 0.424706 0.220286 \n-3.531809 -1.195284 1.705096 \n-2.097159 -1.340254 -0.863994 \n-1.135099 -1.219464 -1.635044 \n-0.228059 0.551806 -3.881094 \n-1.908889 -0.349964 -3.592664 \n0.218941 -0.982404 -2.523044 \n0.260041 -0.803284 3.486996 \n-3.861169 -0.444074 1.489996 \n2.300241 -0.574664 2.219096 \n0.152141 -1.099804 0.946196 \n3.145741 -0.413984 0.443116 \n3.507441 -0.195284 0.108416 \n1.944941 1.339006 -1.191414 \n0.712741 1.140906 1.651796 \n-2.285379 -1.159364 -2.725134 \n1.321641 0.496906 -3.766264 \n-0.113859 0.838606 -3.632624 \n2.832541 -0.493464 1.442796 \n-1.841069 0.723306 1.646096 \n-0.459359 1.016406 2.225996 \n0.201841 -1.017674 0.189156 \n-2.841829 0.421906 -1.719214 \n0.231941 -0.095884 -3.976554 \n2.697141 1.501506 1.322996 \n-1.631849 -0.014764 3.592596 \n4.061241 0.948106 0.957796 \n1.418341 -0.368324 -3.372364 \n2.487241 1.436906 -1.049184 \n2.995641 1.236706 2.259996 \n2.362241 -0.658334 1.734596 \n-1.561139 -1.230494 2.969396 \n1.554241 1.240906 1.116496 \n2.133941 -0.699834 -1.353124 \n0.626741 0.939306 0.855396 \n-2.521189 0.573706 -0.265734 \n-3.175499 -1.467974 -1.143624 \n-2.036329 -0.890544 3.291296 \n0.962641 1.060106 -0.985534 \n-1.670869 0.685006 -0.876284 \n-1.524279 0.747106 -2.679794 \n1.228941 -0.958444 0.811796 \n2.037241 1.395306 1.342896 \n1.108541 1.029206 -0.500484 \n-2.722179 -1.445594 0.558096 \n1.703441 1.345006 -1.781734 \n-2.143929 -1.295104 2.540596 \n-1.302389 -1.200624 0.643196 \n1.449241 -0.890044 0.039516 \n-0.215759 0.355906 3.842396 \n-0.920159 -1.124644 3.251996 \n0.225241 -1.137294 1.749496 \n-0.786199 -1.095824 -0.176074 \n-1.303999 0.689606 0.573696 \n2.387341 -0.695494 1.207696 \n3.616041 -0.063804 -0.868164 \n1.275241 -0.915394 2.204296 \n1.104941 1.255006 -1.907854 \n-2.126609 0.680406 1.985496 \n-0.612959 -1.130234 -1.434614 \n0.288041 -0.826834 -3.206864 \n1.504641 1.081106 -3.301794 \n0.956941 1.083106 3.279996 \n3.425541 1.292006 1.749896 \n-1.562639 0.657506 -0.367044 \n-0.385059 0.723606 0.010976 \n0.643941 0.357306 -3.944714 \n1.143541 1.026006 0.066126 \n-3.923099 -0.495494 -1.432014 \n2.303941 1.442606 -1.648764 \n2.674241 -0.622824 0.391136 \n-2.575289 0.574706 0.181276 \n2.102141 -0.780244 0.270976 \n2.378241 1.455306 1.844496 \n-1.409929 -0.961854 3.454296 \n4.013241 0.332406 -0.723214 \n-1.573919 -1.215954 -0.711444 \n3.366141 1.546806 0.229086 \n3.014341 1.508506 -0.892014 \n-2.636479 -1.351154 2.158996 \n0.922241 0.047476 -3.878244 \n0.762741 -1.012504 2.373096 \n-3.926779 -0.052834 -0.307064 \n2.968041 0.646406 2.733196 \n3.782941 1.456006 -0.006234 \n0.293941 0.980106 3.322096 \n0.743841 0.927906 0.369276 \n0.353641 -1.106064 1.342896 \n0.550941 0.926506 -0.651724 \n1.762441 0.858206 -3.461724 \n0.785441 -0.965424 0.039136 \n-2.446739 -0.783074 3.110596 \n-1.761889 -1.299734 0.946196 \n-2.697899 -0.063044 -3.002244 \n0.640841 -0.980814 2.763096 \n-1.050859 -1.134324 0.211156 \n3.911441 0.552606 1.280196 \n3.353541 1.548406 -0.400284 \n-0.360759 0.853606 3.253696 \n-0.389559 -1.095464 -2.375984 \n-2.230669 0.634006 -1.033524 \n-0.163959 -1.039024 -0.132964 \n1.953141 1.367106 2.454696 \n3.068341 1.251806 -2.229364 \n0.992941 1.103506 1.196096 \n-0.354459 -1.058014 -0.495984 \n-2.665739 0.304606 2.489996 \n-0.427759 -1.081724 0.236916 \n3.759441 0.923206 1.707396 \n2.993841 1.552106 0.718696 \n2.296441 -0.743554 0.741996 \n-1.763279 -1.196994 -2.678954 \n3.417241 1.480306 -1.070794 \n1.431641 1.326906 2.206896 \n-2.096879 -1.401974 2.006296 \n-3.095769 -1.187684 2.249096 \n-4.128129 -1.086374 0.486896 \n0.675741 -0.955924 -0.695564 \n-1.630539 -1.295414 -1.868474 \n-0.768859 0.565106 3.506796 \n4.012841 0.625006 -1.187824 \n-1.592159 -0.658304 -3.554044 \n-2.151139 -1.366284 -1.505324 \n1.944341 0.534006 -3.505884 \n2.740341 1.467306 -0.486764 \n-1.790669 0.713506 -1.313474 \n-2.856189 0.495806 1.193596 \n-0.925189 0.930706 2.507296 \n-1.699529 0.733406 -2.239544 \n4.207241 0.885506 0.224376 \n-0.837619 0.695306 0.283396 \n0.104841 -1.031214 -2.123114 \n-2.548549 -1.424364 1.589096 \n-0.612259 0.793806 -0.936894 \n1.131341 -0.948084 0.398946 \n-2.658279 -1.417134 -1.649294 \n0.653841 1.171006 2.953596 \n-4.042969 -0.364854 0.835896 \n-3.526489 -1.494984 -0.025274 \n2.868441 1.487406 -1.434434 \n2.522341 0.143006 -3.086934 \n3.510241 1.089206 -2.010794 \n2.655341 1.425806 -2.070284 \n0.943541 1.250306 2.522996 \n2.051841 1.298306 0.066136 \n-4.223849 -0.936654 -0.173444 \n0.290641 -0.136404 3.874196 \n-1.223659 -0.271774 -3.852484 \n2.581841 0.234906 2.999296 \n2.808341 -0.374994 1.972596 \n-2.557319 -1.120344 2.757496 \n-3.287539 -1.493654 0.579396 \n-0.594659 -0.048564 3.911296 \n2.679541 -0.253214 2.470396 \n3.700541 1.267106 -1.359364 \n-0.959359 0.694006 -0.198754 \n4.180941 0.748706 -0.471614 \n2.535541 -0.659604 -0.082264 \n-3.089509 0.229006 1.982496 \n1.731841 -0.727054 -2.039894 \n0.752941 1.246206 -2.426534 \n-2.794649 0.507006 -1.181084 \n-3.928009 -0.950514 -1.345824 \n-1.636959 -1.274204 -1.269404 \n-1.940279 0.529306 -2.870564 \n1.882741 -0.848684 1.494496 \n-3.549919 -0.243094 -2.000504 \n-0.985279 0.894306 2.008496 \n-0.019559 0.850306 -0.771854 \n-4.049549 -0.204584 0.297226 \n4.087041 0.536406 0.624496 \n0.371741 0.972006 1.250196 \n-3.046759 0.034546 -2.443624 \n-0.719569 -1.137034 0.634596 \n-3.158539 -1.349784 -1.706094 \n-0.279059 0.747106 0.458496 \n-1.316909 0.801306 -1.568304 \n2.739641 -0.508704 -1.402154 \n4.125641 0.432706 -0.049424 \n-2.650109 0.030536 2.922696 \n0.728641 -0.572354 -3.501974 \n-3.251199 -0.368214 -2.488764 \n-1.667539 -1.229094 0.297986 \n-3.511899 0.271306 -0.265254 \n-1.217199 -0.839184 -3.421624 \n1.688541 -0.870954 0.546196 \n-2.974609 -0.856614 2.734296 \n-0.895229 -0.556384 -3.761854 \n2.268441 -0.709414 -0.535614 \n-1.094129 0.717706 -0.654994 \n-0.452859 -1.158414 1.013596 \n-1.450159 0.338706 3.469996 \n-4.085079 -0.786084 1.017296 \n-2.013369 -0.428744 3.485496 \n0.186241 0.812206 0.140706 \n3.297341 0.979906 2.281496 \n-1.587119 -0.584934 3.599196 \n3.576841 0.144706 1.499496 \n2.837841 -0.528204 -0.495564 \n1.889941 -0.783134 -0.895424 \n-1.428079 0.758706 2.798596 \n0.046441 -0.556934 3.784296 \n-3.684669 -0.679584 -1.936454 \n-0.686599 0.914906 2.917896 \n-0.347959 1.019106 -2.897304 \n-0.755329 0.847706 -1.379134 \n3.290941 -0.199454 -1.362574 \n-1.419229 0.659306 0.105926 \n-1.311779 0.798806 1.546596 \n1.666841 1.197906 3.029896 \n-2.321029 0.633806 1.482596 \n3.287141 1.490906 1.234996 \n2.984741 -0.097804 -2.290484 \n-2.836459 0.532306 0.656296 \n-0.861149 -1.306774 2.294096 \n-0.311359 -0.327694 3.905596 \n-3.734009 -0.074834 1.271696 \n1.872541 1.382106 1.877196 \n-1.407059 -1.176224 -0.210924 \n-2.367179 -1.400034 1.022796 \n1.364841 1.219006 -1.359284 \n1.681541 -0.782554 -1.677954 \n-0.059659 -1.061814 -1.689014 \n2.290341 0.381006 -3.348324 \n0.475541 0.734206 3.681596 \n-4.023649 -1.188844 -0.711754 \n-0.577459 1.001106 -2.162764 \n1.243741 1.071406 0.684396 \n2.914741 -0.510524 0.930496 \n0.902341 -0.944574 -1.144034 \n-0.839749 0.521606 -3.775084 \n4.048341 1.041606 -0.934924 \n-0.546859 1.000206 -2.571354 \n1.303541 0.921306 3.446996 \n2.932241 1.521106 0.098186 \n2.429641 0.627306 3.172496 \n2.306841 -0.595294 -1.818344 \n-0.992569 -1.123874 -0.610844 \n1.003941 1.263606 -2.859874 \n3.594241 1.493206 0.682696 \n-3.338079 0.342506 0.779696 \n3.029841 0.258606 -2.684064 \n2.654641 1.184106 -2.680514 \n-0.231759 1.062706 2.546896 \n-0.821239 -1.130474 -2.681534 \n-1.402569 0.509406 -3.546474 \n-1.452419 -1.244514 -2.433904 \n-0.661849 0.763206 -3.535814 \n-3.107039 -1.510414 -0.552684 \n1.605141 0.186106 -3.659124 \n-3.223199 0.382406 -0.773184 \n3.076441 0.948906 -2.630754 \n-2.743569 0.480606 1.693896 \n-2.588259 -1.436754 -0.474874 \n-3.638209 -1.458764 -0.615664 \n-2.727689 0.347506 -2.256804 \n0.030141 -1.025324 -0.732464 \n-1.283989 -0.180894 3.756196 \n-3.285119 0.273906 -1.328174 \n-0.357559 0.968406 1.837296 \n-2.064339 0.056406 -3.468924 \n-0.845689 0.842506 -3.252574 \n1.527441 -0.844614 -1.273824 \n-0.703919 -0.773084 3.688796 \n2.527141 -0.614204 -0.945414 \n-0.561259 -1.137704 -1.919154 \n1.234441 -0.629994 3.225896 \n3.253041 1.394206 -1.693584 \n0.767641 -1.038624 1.048796 \n0.639041 0.907006 -0.173444 \n-2.963859 -1.458584 1.104196 \n1.736241 -0.330294 3.299396 \n-0.916819 0.269506 3.752496 \n-2.924189 -0.517674 -2.831784 \n0.530241 1.155206 -1.994664 \n0.333941 1.167606 -2.848074 \nCELLS 996 3984\n3  270  374  303\n3  104  55  232\n3  339  225  45\n3  410  374  315\n3  104  232  416\n3  232  55  34\n3  330  122  403\n3  410  82  0\n3  55  0  82\n3  481  417  420\n3  339  45  303\n3  339  303  374\n3  416  232  361\n3  122  34  55\n3  34  122  382\n3  169  225  104\n3  104  416  169\n3  330  403  92\n3  315  194  410\n3  16  261  374\n3  417  315  261\n3  270  16  374\n3  19  270  338\n3  420  261  298\n3  261  420  417\n3  440  65  361\n3  440  361  232\n3  232  34  440\n3  92  81  330\n3  45  494  356\n3  356  303  45\n3  338  303  356\n3  268  494  375\n3  361  393  179\n3  225  169  156\n3  375  494  156\n3  156  45  225\n3  156  494  45\n3  340  169  416\n3  82  403  122\n3  82  410  194\n3  194  315  417\n3  481  249  417\n3  194  13  403\n3  403  82  194\n3  34  8  440\n3  393  361  65\n3  23  330  81\n3  443  356  494\n3  494  268  443\n3  306  166  393\n3  375  156  137\n3  137  156  169\n3  169  340  137\n3  303  338  270\n3  65  440  8\n3  23  382  330\n3  122  330  382\n3  23  81  282\n3  81  92  424\n3  415  126  39\n3  39  92  403\n3  375  137  362\n3  338  356  31\n3  356  443  31\n3  268  80  102\n3  102  443  268\n3  66  340  369\n3  340  416  179\n3  361  179  416\n3  166  179  393\n3  19  186  188\n3  476  382  23\n3  23  282  476\n3  434  350  476\n3  476  282  434\n3  126  168  424\n3  42  282  81\n3  12  343  496\n3  92  39  126\n3  343  168  415\n3  343  12  322\n3  179  369  340\n3  66  137  340\n3  66  362  137\n3  478  66  449\n3  338  186  19\n3  316  182  31\n3  438  188  186\n3  338  31  186\n3  182  22  186\n3  438  186  22\n3  316  31  443\n3  443  102  316\n3  186  31  182\n3  159  182  316\n3  406  102  171\n3  375  362  160\n3  160  268  375\n3  160  80  268\n3  40  32  474\n3  32  40  259\n3  306  407  114\n3  96  94  386\n3  438  124  196\n3  427  467  351\n3  295  259  40\n3  106  32  259\n3  259  295  131\n3  324  32  106\n3  288  431  379\n3  56  350  434\n3  434  10  56\n3  56  4  33\n3  127  33  4\n3  437  464  350\n3  437  33  295\n3  350  56  437\n3  437  56  33\n3  208  306  393\n3  208  407  306\n3  393  65  208\n3  65  464  208\n3  382  476  8\n3  382  8  34\n3  8  464  65\n3  350  464  8\n3  8  476  350\n3  126  415  168\n3  427  351  424\n3  126  424  92\n3  427  424  168\n3  362  66  478\n3  449  94  478\n3  369  449  66\n3  397  319  331\n3  102  406  316\n3  159  316  271\n3  271  316  406\n3  442  319  406\n3  319  397  271\n3  159  271  397\n3  30  404  284\n3  53  26  193\n3  43  119  26\n3  224  40  407\n3  407  208  224\n3  224  208  464\n3  295  40  224\n3  224  437  295\n3  464  437  224\n3  166  306  164\n3  306  114  164\n3  121  41  52\n3  41  121  96\n3  449  386  94\n3  386  449  120\n3  420  134  481\n3  420  298  134\n3  322  17  427\n3  142  183  467\n3  391  351  244\n3  42  424  351\n3  81  424  42\n3  114  132  164\n3  292  164  132\n3  263  324  106\n3  106  288  263\n3  32  324  385\n3  145  385  324\n3  184  246  145\n3  385  145  246\n3  259  131  106\n3  288  106  131\n3  131  295  127\n3  33  127  295\n3  127  431  131\n3  431  288  131\n3  184  90  479\n3  352  264  90\n3  249  496  415\n3  376  496  481\n3  39  13  415\n3  249  481  496\n3  39  403  13\n3  249  415  13\n3  194  417  13\n3  13  417  249\n3  376  481  134\n3  230  43  26\n3  185  191  251\n3  191  284  404\n3  119  191  185\n3  119  165  150\n3  207  185  251\n3  191  119  43\n3  472  160  362\n3  362  478  472\n3  94  96  398\n3  398  96  121\n3  426  292  390\n3  390  132  87\n3  386  120  292\n3  164  292  120\n3  369  120  449\n3  166  164  120\n3  120  369  166\n3  179  166  369\n3  16  255  298\n3  315  374  261\n3  16  270  255\n3  19  255  270\n3  193  150  383\n3  276  331  319\n3  159  397  414\n3  414  397  138\n3  331  138  397\n3  275  199  223\n3  124  275  72\n3  172  2  412\n3  196  72  218\n3  72  196  124\n3  196  189  438\n3  188  438  189\n3  218  209  196\n3  64  189  209\n3  17  309  360\n3  286  360  309\n3  147  42  391\n3  88  432  391\n3  18  399  176\n3  18  176  258\n3  116  432  88\n3  429  400  48\n3  379  289  74\n3  57  238  183\n3  78  483  183\n3  142  467  17\n3  244  467  183\n3  78  183  238\n3  183  142  57\n3  441  266  139\n3  118  155  11\n3  11  155  266\n3  238  57  139\n3  256  139  266\n3  215  451  155\n3  213  395  176\n3  238  395  78\n3  213  78  395\n3  483  78  213\n3  212  229  35\n3  317  484  212\n3  405  317  497\n3  132  482  87\n3  474  482  114\n3  284  110  30\n3  93  284  43\n3  471  454  469\n3  229  111  371\n3  1  485  469\n3  264  163  1\n3  184  479  246\n3  246  87  482\n3  87  246  479\n3  479  90  217\n3  485  1  163\n3  111  469  454\n3  485  117  471\n3  60  436  144\n3  29  436  60\n3  324  263  349\n3  48  400  349\n3  349  263  48\n3  413  288  379\n3  413  263  288\n3  48  263  413\n3  379  74  413\n3  163  7  456\n3  487  134  298\n3  425  439  487\n3  134  487  439\n3  185  165  119\n3  51  150  165\n3  383  150  51\n3  319  442  276\n3  80  171  102\n3  94  398  260\n3  260  478  94\n3  260  472  478\n3  132  390  292\n3  426  386  292\n3  426  41  96\n3  96  386  426\n3  405  390  396\n3  497  41  405\n3  212  497  317\n3  390  405  426\n3  41  426  405\n3  405  396  317\n3  87  396  390\n3  305  223  280\n3  453  174  486\n3  257  423  486\n3  311  199  275\n3  138  331  245\n3  36  64  231\n3  318  5  209\n3  209  62  64\n3  36  274  64\n3  318  209  218\n3  318  218  305\n3  218  72  305\n3  17  360  142\n3  367  142  360\n3  367  57  142\n3  63  233  377\n3  42  147  282\n3  434  282  243\n3  243  282  147\n3  243  147  432\n3  243  10  434\n3  243  432  10\n3  391  244  483\n3  183  483  244\n3  244  351  467\n3  351  391  42\n3  391  483  88\n3  147  391  432\n3  418  289  399\n3  399  18  418\n3  418  18  310\n3  277  431  127\n3  177  116  88\n3  177  213  176\n3  177  88  213\n3  483  213  88\n3  162  429  180\n3  256  451  328\n3  328  395  238\n3  238  139  328\n3  328  139  256\n3  395  258  176\n3  395  328  258\n3  258  328  451\n3  310  18  258\n3  258  451  310\n3  132  114  482\n3  474  114  407\n3  474  385  482\n3  246  482  385\n3  385  474  32\n3  407  40  474\n3  497  212  52\n3  497  52  41\n3  24  475  154\n3  154  54  465\n3  285  313  158\n3  157  158  151\n3  206  371  111\n3  111  484  469\n3  152  469  484\n3  228  454  241\n3  471  469  485\n3  152  484  317\n3  217  388  152\n3  396  217  317\n3  67  145  349\n3  324  349  145\n3  67  352  184\n3  145  67  184\n3  67  349  400\n3  48  413  357\n3  357  413  74\n3  357  429  48\n3  28  429  357\n3  163  264  7\n3  352  7  264\n3  117  485  428\n3  388  90  264\n3  400  429  162\n3  28  180  429\n3  162  450  173\n3  162  180  450\n3  415  496  343\n3  231  425  36\n3  203  370  70\n3  376  134  439\n3  439  425  370\n3  490  113  62\n3  16  298  261\n3  36  487  274\n3  189  274  188\n3  188  255  19\n3  50  29  460\n3  383  51  460\n3  461  276  442\n3  300  461  442\n3  442  171  300\n3  271  406  319\n3  406  171  442\n3  276  461  329\n3  359  260  398\n3  342  346  135\n3  472  260  98\n3  98  359  346\n3  260  359  98\n3  1  152  388\n3  388  217  90\n3  469  152  1\n3  152  317  217\n3  217  396  479\n3  87  479  396\n3  223  305  72\n3  242  373  254\n3  143  5  318\n3  423  2  73\n3  73  486  423\n3  453  486  73\n3  73  341  453\n3  73  2  69\n3  69  341  73\n3  257  486  445\n3  193  383  445\n3  445  174  193\n3  445  486  174\n3  159  414  83\n3  83  182  159\n3  83  22  182\n3  325  245  331\n3  245  200  138\n3  234  138  200\n3  487  36  425\n3  189  196  209\n3  298  274  487\n3  274  298  255\n3  189  64  274\n3  255  188  274\n3  57  337  139\n3  367  337  57\n3  441  139  337\n3  140  216  203\n3  216  140  107\n3  75  175  63\n3  58  286  433\n3  337  367  58\n3  448  210  457\n3  216  457  210\n3  79  95  146\n3  372  459  205\n3  457  216  95\n3  149  74  289\n3  289  418  149\n3  202  277  116\n3  176  399  202\n3  202  177  176\n3  116  177  202\n3  354  289  379\n3  399  289  354\n3  379  431  354\n3  431  277  354\n3  354  202  399\n3  277  202  354\n3  10  4  56\n3  4  277  127\n3  4  10  116\n3  432  116  10\n3  116  277  4\n3  368  155  118\n3  144  436  465\n3  447  154  364\n3  313  347  240\n3  151  158  290\n3  388  264  1\n3  173  450  7\n3  456  428  163\n3  428  499  117\n3  499  428  15\n3  285  220  49\n3  456  15  428\n3  499  462  192\n3  220  192  462\n3  471  241  454\n3  157  241  192\n3  97  401  430\n3  29  60  430\n3  133  236  181\n3  133  181  279\n3  173  67  400\n3  90  184  352\n3  400  162  173\n3  456  7  450\n3  173  352  67\n3  7  352  173\n3  370  221  439\n3  168  343  322\n3  210  448  221\n3  273  221  448\n3  427  168  322\n3  99  425  231\n3  231  113  99\n3  62  231  64\n3  70  99  125\n3  495  125  113\n3  370  203  221\n3  99  70  425\n3  210  221  203\n3  113  125  99\n3  5  490  62\n3  231  62  113\n3  125  140  70\n3  205  107  125\n3  490  495  113\n3  128  436  50\n3  29  50  436\n3  364  128  207\n3  128  50  165\n3  103  329  461\n3  331  276  325\n3  329  325  276\n3  325  329  46\n3  105  174  453\n3  130  461  300\n3  130  103  461\n3  411  214  408\n3  411  6  214\n3  30  214  6\n3  214  30  110\n3  239  59  52\n3  239  135  59\n3  135  359  59\n3  121  52  59\n3  359  398  59\n3  121  59  398\n3  52  35  239\n3  153  239  35\n3  35  52  212\n3  408  153  345\n3  345  35  229\n3  35  345  153\n3  472  98  477\n3  160  472  477\n3  80  160  477\n3  477  171  80\n3  300  171  226\n3  226  98  346\n3  171  477  226\n3  226  477  98\n3  492  129  199\n3  199  311  492\n3  492  234  200\n3  311  234  492\n3  389  129  76\n3  280  44  85\n3  85  254  233\n3  199  129  44\n3  199  44  223\n3  44  280  223\n3  44  129  389\n3  254  85  389\n3  389  85  44\n3  294  296  381\n3  69  2  272\n3  69  272  314\n3  272  2  172\n3  172  296  272\n3  272  296  409\n3  409  358  272\n3  257  488  423\n3  423  412  2\n3  412  423  488\n3  412  488  422\n3  412  381  172\n3  422  381  412\n3  50  460  51\n3  488  257  401\n3  401  460  29\n3  460  401  257\n3  257  445  460\n3  383  460  445\n3  109  414  234\n3  109  83  414\n3  138  234  414\n3  109  234  311\n3  46  245  325\n3  480  46  329\n3  46  341  69\n3  480  341  46\n3  360  58  367\n3  210  203  216\n3  360  286  58\n3  459  95  107\n3  457  433  286\n3  95  433  457\n3  203  70  140\n3  216  107  95\n3  373  377  233\n3  493  418  310\n3  493  149  418\n3  20  86  28\n3  256  266  155\n3  326  297  441\n3  297  248  266\n3  11  266  248\n3  433  95  79\n3  459  146  95\n3  368  118  136\n3  190  368  167\n3  256  155  451\n3  155  368  215\n3  310  451  291\n3  215  368  190\n3  222  237  444\n3  444  237  269\n3  366  21  293\n3  293  269  237\n3  364  465  128\n3  436  128  465\n3  51  165  50\n3  347  24  447\n3  447  473  347\n3  290  240  304\n3  304  247  290\n3  151  228  157\n3  241  157  228\n3  192  117  499\n3  241  471  117\n3  158  157  220\n3  285  158  220\n3  117  192  241\n3  163  428  485\n3  484  111  229\n3  411  345  371\n3  408  345  411\n3  484  229  212\n3  435  248  9\n3  422  488  97\n3  279  181  97\n3  97  488  401\n3  422  97  181\n3  29  430  401\n3  97  430  279\n3  3  430  60\n3  279  430  3\n3  376  273  496\n3  17  322  309\n3  467  427  17\n3  221  273  376\n3  309  12  448\n3  322  12  309\n3  70  370  425\n3  12  496  273\n3  376  439  221\n3  273  448  12\n3  286  448  457\n3  309  448  286\n3  193  174  53\n3  150  193  26\n3  105  53  174\n3  197  480  103\n3  329  103  480\n3  387  130  300\n3  387  226  346\n3  300  226  387\n3  93  468  110\n3  110  123  214\n3  153  408  123\n3  408  214  123\n3  229  371  345\n3  6  411  161\n3  93  110  284\n3  278  254  389\n3  334  278  76\n3  265  76  129\n3  200  358  265\n3  265  358  409\n3  409  76  265\n3  265  492  200\n3  129  492  265\n3  143  280  175\n3  62  209  5\n3  318  305  143\n3  280  143  305\n3  236  133  77\n3  77  37  236\n3  124  109  275\n3  22  124  438\n3  22  83  124\n3  223  72  275\n3  311  275  109\n3  83  109  124\n3  245  46  314\n3  314  358  200\n3  200  245  314\n3  358  314  272\n3  314  46  69\n3  146  459  307\n3  107  205  459\n3  112  205  495\n3  140  125  107\n3  495  490  75\n3  495  75  112\n3  195  421  61\n3  178  377  61\n3  180  28  86\n3  148  86  20\n3  498  38  86\n3  38  450  180\n3  456  450  15\n3  38  15  450\n3  54  154  475\n3  144  465  54\n3  466  24  170\n3  154  447  24\n3  365  475  327\n3  313  170  24\n3  198  466  170\n3  347  313  24\n3  240  158  313\n3  473  304  240\n3  180  86  38\n3  281  498  148\n3  384  15  498\n3  38  498  15\n3  49  170  285\n3  79  326  433\n3  337  58  326\n3  441  337  326\n3  266  441  297\n3  58  433  326\n3  100  326  79\n3  297  326  100\n3  9  100  253\n3  100  9  297\n3  248  297  9\n3  252  293  237\n3  136  167  368\n3  167  222  190\n3  237  222  167\n3  167  252  237\n3  291  215  190\n3  222  267  190\n3  291  493  310\n3  215  291  451\n3  267  312  493\n3  281  269  201\n3  498  281  384\n3  86  148  498\n3  269  281  444\n3  148  444  281\n3  302  336  363\n3  363  491  366\n3  363  252  302\n3  293  252  363\n3  465  364  154\n3  473  447  187\n3  240  347  473\n3  206  454  228\n3  161  91  247\n3  91  151  290\n3  206  228  91\n3  14  404  30\n3  161  206  91\n3  30  6  14\n3  371  206  161\n3  14  6  247\n3  247  304  14\n3  151  91  228\n3  111  454  206\n3  371  161  411\n3  419  332  287\n3  458  136  118\n3  333  11  248\n3  248  435  333\n3  118  11  333\n3  333  458  118\n3  68  332  250\n3  250  89  68\n3  250  353  89\n3  204  89  353\n3  68  89  463\n3  54  219  211\n3  211  144  54\n3  211  60  144\n3  211  3  60\n3  299  133  279\n3  279  3  299\n3  480  197  141\n3  141  341  480\n3  453  341  141\n3  141  105  453\n3  130  387  227\n3  335  123  470\n3  123  110  468\n3  470  468  25\n3  233  254  373\n3  254  278  242\n3  172  381  296\n3  334  76  409\n3  389  76  278\n3  409  296  334\n3  296  294  334\n3  280  85  175\n3  233  175  85\n3  233  63  175\n3  175  75  143\n3  5  75  490\n3  5  143  75\n3  37  115  27\n3  37  77  115\n3  77  394  115\n3  71  421  320\n3  205  112  372\n3  63  112  75\n3  125  495  205\n3  178  372  112\n3  178  112  377\n3  63  377  112\n3  327  491  365\n3  466  327  475\n3  54  475  365\n3  365  219  54\n3  198  170  49\n3  475  24  466\n3  49  462  201\n3  21  201  269\n3  201  384  281\n3  201  21  49\n3  198  49  21\n3  462  49  220\n3  313  285  170\n3  384  201  462\n3  192  220  157\n3  15  384  499\n3  462  499  384\n3  262  222  444\n3  20  312  262\n3  444  148  262\n3  148  20  262\n3  291  190  267\n3  222  262  267\n3  267  262  312\n3  493  291  267\n3  149  493  312\n3  149  312  392\n3  392  357  74\n3  74  149  392\n3  20  28  392\n3  357  392  28\n3  392  312  20\n3  269  293  21\n3  293  363  366\n3  327  366  491\n3  198  366  466\n3  327  466  366\n3  21  366  198\n3  91  290  247\n3  304  473  84\n3  372  307  459\n3  47  307  178\n3  68  287  332\n3  287  435  9\n3  287  68  435\n3  463  435  68\n3  136  458  348\n3  252  167  348\n3  136  348  167\n3  302  252  348\n3  108  204  455\n3  89  204  308\n3  458  333  301\n3  301  333  463\n3  435  463  333\n3  3  211  344\n3  344  299  3\n3  344  219  455\n3  344  211  219\n3  130  227  283\n3  197  103  283\n3  130  283  103\n3  346  342  387\n3  359  135  346\n3  227  387  342\n3  468  93  323\n3  150  26  119\n3  230  26  355\n3  27  195  242\n3  377  373  61\n3  71  61  421\n3  195  61  373\n3  101  294  381\n3  181  101  422\n3  101  181  236\n3  381  422  101\n3  452  334  294\n3  452  242  278\n3  278  334  452\n3  299  344  380\n3  380  344  455\n3  380  204  353\n3  455  204  380\n3  133  299  402\n3  402  77  133\n3  402  394  77\n3  299  380  402\n3  353  394  402\n3  402  380  353\n3  320  332  71\n3  115  320  421\n3  320  115  394\n3  320  250  332\n3  353  250  394\n3  320  394  250\n3  378  365  491\n3  378  108  455\n3  455  219  378\n3  219  365  378\n3  187  251  84\n3  364  187  447\n3  404  251  191\n3  165  207  128\n3  191  43  284\n3  14  84  404\n3  251  187  207\n3  165  185  207\n3  372  178  307\n3  419  307  47\n3  47  61  71\n3  178  61  47\n3  71  332  47\n3  332  419  47\n3  307  419  146\n3  419  287  253\n3  9  253  287\n3  253  100  146\n3  79  146  100\n3  253  146  419\n3  108  308  204\n3  301  463  308\n3  89  308  463\n3  355  53  446\n3  53  355  26\n3  227  342  25\n3  242  452  27\n3  373  242  195\n3  421  27  115\n3  421  195  27\n3  294  101  235\n3  235  452  294\n3  236  37  235\n3  235  101  236\n3  235  37  452\n3  27  452  37\n3  187  84  473\n3  251  404  84\n3  240  290  158\n3  364  207  187\n3  84  14  304\n3  161  247  6\n3  301  308  321\n3  302  348  321\n3  321  348  458\n3  458  301  321\n3  308  108  321\n3  336  378  491\n3  108  378  336\n3  336  321  108\n3  491  363  336\n3  302  321  336\n3  141  197  105\n3  105  446  53\n3  446  105  197\n3  283  446  197\n3  489  283  227\n3  227  25  489\n3  489  446  283\n3  355  446  489\n3  43  230  93\n3  25  323  489\n3  323  25  468\n3  230  355  323\n3  230  323  93\n3  355  489  323\n3  335  470  135\n3  342  135  470\n3  25  342  470\n3  153  123  335\n3  335  135  239\n3  239  153  335\n3  468  470  123\n3  122  55  82\n3  225  0  104\n3  55  104  0\n3  0  225  339\n3  0  339  410\n3  374  410  339\nCELL_TYPES 996\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n5\n"
  },
  {
    "path": "test/vtk/square.mesh",
    "content": "vertices\n\n1 0 0 0\n2 1 0 0\n3 0 1 0\n4 1 1 0\n\nfaces\n\n1 1 2 3\n2 2 3 4\n"
  },
  {
    "path": "test/vtk/tetrahedron.mesh",
    "content": "vertices\n\n1 0 0 0.612372\n2 -0.288675 -0.5 -0.204124\n3 -0.288675 0.5 -0.204124\n4 0.57735 0 -0.204124\n\nvolumes\n\n1 1 2 3 4\n"
  },
  {
    "path": "test/vtk/vtk_exporter_addfield_fname_not_str_err.morpho",
    "content": "// Testing whether adding a field to the VTKExporter, but giving a\n// fieldname that's not a string, throws the right response \n\nimport vtk\nimport meshtools \n\nvar a = 3.0\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:2, -1..1:2)\nvar f = Field(m, fn(x,y,z) x)\n\nvar vtkE = VTKExporter(m)\nvtkE.addfield(f, fieldname=a) // expect: Error 'FnameNotStr' : Expected a string, but received `3`.\n"
  },
  {
    "path": "test/vtk/vtk_exporter_fname_not_str_err.morpho",
    "content": "// Testing whether initializing VTKExporter with a field, but giving a\n// fieldname that's not a string, produces the right error\n\nimport vtk\nimport meshtools \n\nvar a = 3.0\n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:2, -1..1:2)\nvar f = Field(m, fn(x,y,z) x)\n\nvar vtkE = VTKExporter(f, fieldname=a) // expect: Error 'FnameNotStr' : Expected a string, but received `3`.\n"
  },
  {
    "path": "test/vtk/vtk_exporter_init_err.morpho",
    "content": "// Testing whether initializing VTKExporter with an object that is\n// neither a Mesh nor a Field produces the right error\n\nimport vtk\nimport meshtools \n\nvar a = 3.0\n\nvar vtkE = VTKExporter(a) // expect: Error 'InitErr' : Expected a Mesh or a Field object, but received `3`.\n"
  },
  {
    "path": "test/vtk/vtk_exporter_invalid_fname_err.morpho",
    "content": "// Testing whether initializing VTKExporter with a field, but giving a\n// fieldname that has an embedded whitespace in it, produces the right\n// error\n \nimport vtk\nimport meshtools \n\nvar m = AreaMesh(fn (u,v) [u,v,0], -1..1:2, -1..1:2)\nvar f = Field(m, fn(x,y,z) x)\n\nvar vtkE = VTKExporter(f, fieldname=\"x y\") // expect: Error 'InvalidFname' : fieldname (`x y`) cannot have embedded whitespaces.\n"
  },
  {
    "path": "test/wc",
    "content": "arithmetic/power.morpho\narray/array.morpho\narray/array_assign_beyond_bounds.morpho\narray/array_garbage_collect.morpho\narray/array_nonnum_indices.morpho\narray/array_read_beyond_bounds.morpho\narray/array_three_dim.morpho\narray/array_two_dim.morpho\narray/array_wrong_dim.morpho\nassignment/associativity.morpho\nassignment/global.morpho\nassignment/grouping.morpho\nassignment/infix_operator.morpho\nassignment/local.morpho\nassignment/prefix_operator.morpho\nassignment/shorthand.morpho\nassignment/syntax.morpho\nassignment/to_self.morpho\nassignment/undefined.morpho\nblock/empty.morpho\nblock/scope.morpho\nblock/scope_error.morpho\nbool/equality.morpho\nbool/not.morpho\nbreak/break_in_if_outside_loop.morpho\nbreak/break_outside_loop.morpho\nbreak/continue_in_if_outside_loop.morpho\nbreak/continue_outside_loop.morpho\nbreak/in_for.morpho\nbreak/in_forin.morpho\nbreak/in_while.morpho\ncall/bool.morpho\ncall/call.morpho\ncall/nil.morpho\ncall/num.morpho\ncall/object.morpho\ncall/string.morpho\nclass/empty.morpho\nclass/inherit_self.morpho\nclass/inherited_method.morpho\nclass/local_inherit_other.morpho\nclass/local_inherit_self.morpho\nclass/local_reference_self.morpho\nclass/reference_self.morpho\nclosure/assign_to_closure.morpho\nclosure/assign_to_shadowed_later.morpho\nclosure/close_over_function_parameter.morpho\nclosure/close_over_later_variable.morpho\nclosure/closed_closure_in_function.morpho\nclosure/nested_closure.morpho\nclosure/open_closure_in_function.morpho\nclosure/reference_closure_multiple_times.morpho\nclosure/reuse_closure_slot.morpho\nclosure/shadow_closure_with_local.morpho\nclosure/unused_closure.morpho\nclosure/unused_later_closure.morpho\ncomments/line_at_eof.morpho\ncomments/multline.morpho\ncomments/nested.morpho\ncomments/only_line_comment.morpho\ncomments/only_line_comment_and_line.morpho\ncomments/unicode.morpho\ncomments/unterminated.morpho\nconstructor/arguments.morpho\nconstructor/call_init_early_return.morpho\nconstructor/call_init_explicitly.morpho\nconstructor/default.morpho\nconstructor/default_arguments.morpho\nconstructor/early_return.morpho\nconstructor/extra_arguments.morpho\nconstructor/init_not_method.morpho\nconstructor/missing_arguments.morpho\nconstructor/return_in_nested_function.morpho\nconstructor/return_value.morpho\ndictionary/key_not_found.morpho\ndictionary/literal_from_vars.morpho\ndictionary/literal_in_function.morpho\ndictionary/literal_in_loop.morpho\ndictionary/methods.morpho\ndictionary/syntax.morpho\nerror/stacktrace.morpho\nfile/file.morpho\nfile/file_lines.morpho\nfile/file_not_found.morpho\nfile/file_write.morpho\nfile/filename_missing.morpho\nfile/filename_not_string.morpho\nfor/class_in_body.morpho\nfor/closure_in_body.morpho\nfor/fn_in_body.morpho\nfor/return_closure.morpho\nfor/return_inside.morpho\nfor/scope.morpho\nfor/statement_condition.morpho\nfor/statement_increment.morpho\nfor/statement_initializer.morpho\nfor/syntax.morpho\nfor/var_in_body.morpho\nfor_in/forin.morpho\nfor_in/forin_custom.morpho\nfor_in/forin_in_function.morpho\nfor_in/forin_string.morpho\nfunction/body_must_be_block.morpho\nfunction/empty_body.morpho\nfunction/extra_arguments.morpho\nfunction/index_in_arguments.morpho\nfunction/local_recursion.morpho\nfunction/missing_arguments.morpho\nfunction/missing_comma_in_parameters.morpho\nfunction/parameters.morpho\nfunction/print.morpho\nfunction/recursion.morpho\nfunction/too_many_arguments.morpho\nfunction/too_many_parameters.morpho\nif/class_in_else.morpho\nif/class_in_then.morpho\nif/dangling_else.morpho\nif/else.morpho\nif/fn_in_else.morpho\nif/fn_in_then.morpho\nif/if.morpho\nif/truth.morpho\nif/var_in_else.morpho\nif/var_in_then.morpho\nimport/file_not_found.morpho\nimport/for_clause.morpho\nimport/for_clause_restrict.morpho\nimport/import_file.morpho\nimport/import_module.morpho\nimport/module_not_found.morpho\ninheritance/constructor.morpho\ninheritance/inherit_from_function.morpho\ninheritance/inherit_from_nil.morpho\ninheritance/inherit_from_number.morpho\ninheritance/inherit_methods.morpho\ninheritance/parenthesized_superclass.morpho\ninheritance/set_fields_from_base_class.morpho\nlist/index_out_of_bounds.morpho\nlist/syntax.morpho\nlogical/and.morpho\nlogical/and_truth.morpho\nlogical/or.morpho\nlogical/or_truth.morpho\nmath/math.morpho\nmatrix/arithmetic.morpho\nmatrix/incompatible_add.morpho\nmatrix/incompatible_mul.morpho\nmatrix/incompatible_sub.morpho\nmatrix/initializer.morpho\nmatrix/linearsolve.morpho\nmatrix/linearsolve3x3.morpho\nmatrix/nonnum_indices.morpho\nmatrix/trace.morpho\nmatrix/transpose.morpho\nmesh/meshload.morpho\nmethod/arity.morpho\nmethod/empty_block.morpho\nmethod/extra_arguments.morpho\nmethod/missing_arguments.morpho\nmethod/not_found.morpho\nmethod/print_bound_method.morpho\nmethod/return_in_method.morpho\nmethod/too_many_arguments.morpho\nmethod/too_many_parameters.morpho\nnewline/block.morpho\nnewline/classes.morpho\nnewline/for.morpho\nnewline/functions.morpho\nnewline/variables.morpho\nnil/literal.morpho\nnumber/decimal_point_at_eof.morpho\nnumber/leading_dot.morpho\nnumber/literals.morpho\nnumber/nan_equality.morpho\nnumber/trailing_dot.morpho\noperator/more_comparison.morpho\nprint/missing_argument.morpho\nprograms/delta_blue.morpho\nprograms/fannkuch.morpho\nprograms/fibonacci.morpho\nprograms/histogram.morpho\nprograms/integrate.morpho\nrange/constructor.morpho\nrange/count_down.morpho\nrange/syntax.morpho\nreturn/after_else.morpho\nreturn/after_if.morpho\nreturn/after_while.morpho\nreturn/at_top_level.morpho\nreturn/in_for_in.morpho\nreturn/in_function.morpho\nreturn/in_method.morpho\nreturn/return_nil_if_no_value.morpho\nself/closure.morpho\nself/nested_class.morpho\nself/nested_closure.morpho\nself/self_at_top_level.morpho\nself/self_in_method.morpho\nself/self_in_top_level_function.morpho\nsparse/arithmetic.morpho\nsparse/incompatible_add.morpho\nsparse/incompatible_mul.morpho\nsparse/initializer.morpho\nsparse/invld_initializer.morpho\nsparse/linearsolve.morpho\nsparse/nonnum.morpho\nstring/error_after_multiline.morpho\nstring/interpolation.morpho\nstring/literals.morpho\nstring/multiline.morpho\nstring/string_veneer.morpho\nstring/unterminated.morpho\nsyntax/comments.morpho\nsyntax/empty_file.morpho\nsyntax/illegal_chars_in_symbol.morpho\nsyntax/symbols.morpho\nvariable/duplicate_local.morpho\nvariable/duplicate_parameter.morpho\nvariable/early_bound.morpho\nvariable/in_middle_of_block.morpho\nvariable/in_nested_block.morpho\nvariable/local_from_method.morpho\nvariable/multiple.morpho\nvariable/redeclare_global.morpho\nvariable/redefine_global.morpho\nvariable/scope_reuse_in_different_blocks.morpho\nvariable/shadow_and_local.morpho\nvariable/shadow_global.morpho\nvariable/shadow_local.morpho\nvariable/undefined_global.morpho\nvariable/undefined_local.morpho\nvariable/uninitialized.morpho\nvariable/use_false_as_var.morpho\nvariable/use_global_in_initializer.morpho\nvariable/use_nil_as_var.morpho\nvariable/use_self_as_var.morpho\nwhile/class_in_body.morpho\nwhile/closure_in_body.morpho\nwhile/fun_in_body.morpho\nwhile/return_closure.morpho\nwhile/return_inside.morpho\nwhile/syntax.morpho\nwhile/var_in_body.morpho\n"
  },
  {
    "path": "test/while/class_in_body.morpho",
    "content": "// Can't have a class as the body of a while loop\n\nwhile (true) class Foo {}\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/while/closure_in_body.morpho",
    "content": "var f1,f2,f3\n\nvar i = 1\nwhile (i < 4) {\n  var j = i\n  fn f() {\n    print j\n  }\n\n  if (j == 1) f1 = f\n  else if (j == 2) f2 = f\n  else f3 = f\n\n  i = i + 1\n}\n\nf1() // expect: 1\nf2() // expect: 2\nf3() // expect: 3\n"
  },
  {
    "path": "test/while/fn_in_body.morpho",
    "content": "// Can't have a function as the body of a while loop\n\nwhile (true) fn foo() {}\n// expect error 'ExpExpr'\n"
  },
  {
    "path": "test/while/if_in_body.morpho",
    "content": "// The if divides the loop into multiple control flow blocks\n\nvar i = 1\nwhile (i < 4) {\n  var j=i \n\n  if (j == 1) print true\n\n  i = i + 1\n}\n\n// expect: true"
  },
  {
    "path": "test/while/return_closure.morpho",
    "content": "// Checks that returning a closure inside a while loop works\n\nfn f() {\n  while (true) {\n    var i = \"i\"\n    fn g() { print i }\n    return g\n  }\n}\n\nvar h = f()\nh() // expect: i\n"
  },
  {
    "path": "test/while/return_inside.morpho",
    "content": "// Checks that a return inside a while loop works. \n\nfn f() {\n  while (true) {\n    var i = \"i\"\n    return i\n  }\n}\n\nprint f()\n// expect: i\n"
  },
  {
    "path": "test/while/syntax.morpho",
    "content": "// While loop syntax\n\n// Single-expression body.\nvar c = 0\nwhile (c < 3) print c+=1\n// expect: 1\n// expect: 2\n// expect: 3\n\n// Block body.\nvar a = 0\nwhile (a < 3) {\n  print a\n  a+=1\n}\n// expect: 0\n// expect: 1\n// expect: 2\n\n// Statement bodies.\nwhile (false) if (true) 1; else 2\nwhile (false) while (true) 1\nwhile (false) for (;;) 1\n"
  },
  {
    "path": "test/while/var_in_body.morpho",
    "content": "// Can't have a var as the body of a while loop\nwhile (true) var foo;\n// expect error 'ExpExpr'\n"
  }
]